Further fixes for SolidJS
This commit is contained in:
parent
db7fb9213e
commit
89b79fa245
42 changed files with 1009 additions and 913 deletions
|
@ -6,7 +6,8 @@
|
|||
"build": "solid-start build",
|
||||
"lint": "eslint --fix \"**/*.{ts,tsx,js,jsx}\"",
|
||||
"push": "drizzle-kit push:pg",
|
||||
"test": "pnpm playwright test --ui"
|
||||
"test": "pnpm playwright test --ui",
|
||||
"typecheck": "tsc --noEmit --checkJs false --skipLibCheck"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
@ -30,6 +31,7 @@
|
|||
"drizzle-zod": "^0.5.0",
|
||||
"http-status": "^1.6.2",
|
||||
"nodemailer": "6.9.4",
|
||||
"object-hash": "^3.0.0",
|
||||
"postgres": "^3.3.5",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io-client": "^4.7.2",
|
||||
|
@ -44,6 +46,7 @@
|
|||
"@total-typescript/ts-reset": "^0.4.2",
|
||||
"@types/node": "^20.5.0",
|
||||
"@types/nodemailer": "^6.4.9",
|
||||
"@types/object-hash": "^3.0.3",
|
||||
"@types/web-bluetooth": "^0.0.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
|
|
11
leaky-ships/pnpm-lock.yaml
generated
11
leaky-ships/pnpm-lock.yaml
generated
|
@ -65,6 +65,9 @@ dependencies:
|
|||
nodemailer:
|
||||
specifier: 6.9.4
|
||||
version: 6.9.4
|
||||
object-hash:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
postgres:
|
||||
specifier: ^3.3.5
|
||||
version: 3.3.5
|
||||
|
@ -103,6 +106,9 @@ devDependencies:
|
|||
'@types/nodemailer':
|
||||
specifier: ^6.4.9
|
||||
version: 6.4.9
|
||||
'@types/object-hash':
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
'@types/web-bluetooth':
|
||||
specifier: ^0.0.17
|
||||
version: 0.0.17
|
||||
|
@ -2075,6 +2081,10 @@ packages:
|
|||
'@types/node': 18.17.5
|
||||
dev: true
|
||||
|
||||
/@types/object-hash@3.0.3:
|
||||
resolution: {integrity: sha512-Mb0SDIhjhBAz4/rDNU0cYcQR4lSJIwy+kFlm0whXLkx+o0pXwEszwyrWD6gXWumxVbAS6XZ9gXK82LR+Uk+cKQ==}
|
||||
dev: true
|
||||
|
||||
/@types/resolve@1.20.2:
|
||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||
|
||||
|
@ -4257,7 +4267,6 @@ packages:
|
|||
/object-hash@3.0.0:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/object-inspect@1.12.3:
|
||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import { For } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import useShips from "~/hooks/useShips"
|
||||
import {
|
||||
gameState,
|
||||
mode,
|
||||
mouseCursor,
|
||||
removeShip,
|
||||
setMode,
|
||||
setMouseCursor,
|
||||
setShips,
|
||||
setTarget,
|
||||
targetPreview,
|
||||
} from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import {
|
||||
borderCN,
|
||||
cornerCN,
|
||||
|
@ -23,40 +32,36 @@ type TilesType = {
|
|||
}
|
||||
|
||||
function BorderTiles() {
|
||||
const { activeUser } = useIndex()
|
||||
const {
|
||||
payload,
|
||||
mode,
|
||||
targetPreview,
|
||||
mouseCursor,
|
||||
setTarget,
|
||||
setMouseCursor,
|
||||
} = useGameProps()
|
||||
const { ships, setShips, removeShip } = useShips()
|
||||
const { selfIndex, activeUser, ships } = useSession()
|
||||
|
||||
const settingTarget = (isGameTile: boolean, x: number, y: number) => {
|
||||
if (payload?.game?.state === "running") {
|
||||
const list = targetList(targetPreview, mode)
|
||||
if (gameState() === "running") {
|
||||
const list = targetList(targetPreview(), mode())
|
||||
if (
|
||||
!isGameTile ||
|
||||
!list.filter(({ x, y }) => !isAlreadyHit(x, y, activeUser?.hits ?? []))
|
||||
.length
|
||||
!list.filter(
|
||||
({ x, y }) => !isAlreadyHit(x, y, activeUser()?.hits() ?? []),
|
||||
).length
|
||||
)
|
||||
return
|
||||
if (!overlapsWithAnyBorder(targetPreview, mode))
|
||||
if (!overlapsWithAnyBorder(targetPreview(), mode()))
|
||||
setTarget({
|
||||
show: true,
|
||||
x,
|
||||
y,
|
||||
orientation: targetPreview.orientation,
|
||||
orientation: targetPreview().orientation,
|
||||
})
|
||||
} else if (
|
||||
payload?.game?.state === "starting" &&
|
||||
targetPreview.show &&
|
||||
!intersectingShip(ships(), shipProps(ships(), mode, targetPreview)).score
|
||||
gameState() === "starting" &&
|
||||
targetPreview().show &&
|
||||
!intersectingShip(ships(), shipProps(ships(), mode(), targetPreview()))
|
||||
.score
|
||||
) {
|
||||
setMouseCursor((e) => ({ ...e, shouldShow: false }))
|
||||
setShips([...ships(), shipProps(ships(), mode, targetPreview)])
|
||||
setShips(
|
||||
[...ships(), shipProps(ships(), mode(), targetPreview())],
|
||||
selfIndex(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,11 +94,11 @@ function BorderTiles() {
|
|||
class={props.className}
|
||||
style={{ "--x": props.x, "--y": props.y }}
|
||||
onClick={() => {
|
||||
if (payload?.game?.state === "running") {
|
||||
if (gameState() === "running") {
|
||||
settingTarget(props.isGameTile, props.x, props.y)
|
||||
} else if (payload?.game?.state === "starting") {
|
||||
} else if (gameState() === "starting") {
|
||||
const { index } = intersectingShip(ships(), {
|
||||
...mouseCursor,
|
||||
...mouseCursor(),
|
||||
size: 1,
|
||||
variant: 0,
|
||||
orientation: "h",
|
||||
|
@ -102,8 +107,8 @@ function BorderTiles() {
|
|||
settingTarget(props.isGameTile, props.x, props.y)
|
||||
else {
|
||||
const ship = ships()[index]
|
||||
useGameProps.setState({ mode: ship.size - 2 })
|
||||
removeShip(ship)
|
||||
setMode(ship.size - 2)
|
||||
removeShip(ship, selfIndex())
|
||||
setMouseCursor((e) => ({ ...e, shouldShow: true }))
|
||||
}
|
||||
}
|
||||
|
@ -114,13 +119,13 @@ function BorderTiles() {
|
|||
y: props.y,
|
||||
shouldShow:
|
||||
props.isGameTile &&
|
||||
(payload?.game?.state === "starting"
|
||||
(gameState() === "starting"
|
||||
? intersectingShip(
|
||||
ships(),
|
||||
shipProps(ships(), mode, {
|
||||
shipProps(ships(), mode(), {
|
||||
x: props.x,
|
||||
y: props.y,
|
||||
orientation: targetPreview.orientation,
|
||||
orientation: targetPreview().orientation,
|
||||
}),
|
||||
true,
|
||||
).score < 2
|
||||
|
|
|
@ -23,52 +23,36 @@ import {
|
|||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import { socket } from "~/lib/socket"
|
||||
import { modes } from "~/lib/utils/helpers"
|
||||
import { GamePropsSchema } from "~/lib/zodSchemas"
|
||||
// import { Icons, toast } from "react-toastify"
|
||||
import { For, Show, createEffect } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import useShips from "~/hooks/useShips"
|
||||
import { EventBarModes, GameSettings } from "../../interfaces/frontend"
|
||||
import {
|
||||
allowChat,
|
||||
allowMarkDraw,
|
||||
allowSpecials,
|
||||
allowSpectators,
|
||||
gameState,
|
||||
menu,
|
||||
mode,
|
||||
reset,
|
||||
setGameSetting,
|
||||
setIsReadyFor,
|
||||
setMenu,
|
||||
setMode,
|
||||
setTarget,
|
||||
setTargetPreview,
|
||||
target,
|
||||
users,
|
||||
} from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import { EventBarModes } from "../../interfaces/frontend"
|
||||
import Item from "./Item"
|
||||
|
||||
export function setGameSetting(
|
||||
payload: GameSettings,
|
||||
setSetting: (settings: GameSettings) => string | null,
|
||||
full: (payload: GamePropsSchema) => void,
|
||||
) {
|
||||
return () => {
|
||||
const hash = setSetting(payload)
|
||||
socket.emit("gameSetting", payload, (newHash) => {
|
||||
if (newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", full)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function EventBar(props: { clear: () => void }) {
|
||||
const { shouldHide, color } = useDrawProps()
|
||||
const { selfIndex, isActiveIndex, selfUser } = useIndex()
|
||||
const { ships } = useShips()
|
||||
const navigate = useNavigate()
|
||||
const {
|
||||
payload,
|
||||
userStates,
|
||||
menu,
|
||||
mode,
|
||||
setSetting,
|
||||
full,
|
||||
target,
|
||||
setTarget,
|
||||
setTargetPreview,
|
||||
setIsReady,
|
||||
reset,
|
||||
} = useGameProps()
|
||||
const gameSetting = (payload: GameSettings) =>
|
||||
setGameSetting(payload, setSetting, full)
|
||||
const { selfIndex, isActiveIndex, selfUser, ships } = useSession()
|
||||
const navigator = useNavigate()
|
||||
|
||||
const items = (): EventBarModes => ({
|
||||
main: [
|
||||
|
@ -76,36 +60,36 @@ function EventBar(props: { clear: () => void }) {
|
|||
icon: "burger-menu",
|
||||
text: "Menu",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "menu" })
|
||||
setMenu("menu")
|
||||
},
|
||||
},
|
||||
payload?.game?.state === "running"
|
||||
gameState() === "running"
|
||||
? {
|
||||
icon: faSwords,
|
||||
text: "Attack",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "moves" })
|
||||
setMenu("moves")
|
||||
},
|
||||
}
|
||||
: {
|
||||
icon: faShip,
|
||||
text: "Ships",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "moves" })
|
||||
setMenu("moves")
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "pen",
|
||||
text: "Draw",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "draw" })
|
||||
setMenu("draw")
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "gear",
|
||||
text: "Settings",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "settings" })
|
||||
setMenu("settings")
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -115,47 +99,49 @@ function EventBar(props: { clear: () => void }) {
|
|||
text: "Surrender",
|
||||
iconColor: "darkred",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "surrender" })
|
||||
setMenu("surrender")
|
||||
},
|
||||
},
|
||||
],
|
||||
moves:
|
||||
payload?.game?.state === "running"
|
||||
gameState() === "running"
|
||||
? [
|
||||
{
|
||||
icon: "scope",
|
||||
text: "Fire missile",
|
||||
enabled: mode === 0,
|
||||
enabled: mode() === 0,
|
||||
callback: () => {
|
||||
useGameProps.setState({ mode: 0 })
|
||||
setTarget((e) => ({ ...e, show: false }))
|
||||
setMode(0)
|
||||
setTarget((t) => ({ ...t, show: false }))
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "torpedo",
|
||||
text: "Fire torpedo",
|
||||
enabled: mode === 1 || mode === 2,
|
||||
enabled: mode() === 1 || mode() === 2,
|
||||
amount:
|
||||
2 -
|
||||
((selfUser?.moves ?? []).filter(
|
||||
(e) => e.type === "htorpedo" || e.type === "vtorpedo",
|
||||
).length ?? 0),
|
||||
(selfUser()
|
||||
?.moves()
|
||||
.filter((e) => e.type === "htorpedo" || e.type === "vtorpedo")
|
||||
.length ?? 0),
|
||||
callback: () => {
|
||||
useGameProps.setState({ mode: 1 })
|
||||
setTarget((e) => ({ ...e, show: false }))
|
||||
setMode(1)
|
||||
setTarget((t) => ({ ...t, show: false }))
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "radar",
|
||||
text: "Radar scan",
|
||||
enabled: mode === 3,
|
||||
enabled: mode() === 3,
|
||||
amount:
|
||||
1 -
|
||||
((selfUser?.moves ?? []).filter((e) => e.type === "radar")
|
||||
.length ?? 0),
|
||||
(selfUser()
|
||||
?.moves()
|
||||
.filter((e) => e.type === "radar").length ?? 0),
|
||||
callback: () => {
|
||||
useGameProps.setState({ mode: 3 })
|
||||
setTarget((e) => ({ ...e, show: false }))
|
||||
setMode(3)
|
||||
setTarget((t) => ({ ...t, show: false }))
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -166,7 +152,7 @@ function EventBar(props: { clear: () => void }) {
|
|||
amount: 1 - ships().filter((e) => e.size === 2).length,
|
||||
callback: () => {
|
||||
if (1 - ships().filter((e) => e.size === 2).length === 0) return
|
||||
useGameProps.setState({ mode: 0 })
|
||||
setMode(0)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -175,7 +161,7 @@ function EventBar(props: { clear: () => void }) {
|
|||
amount: 3 - ships().filter((e) => e.size === 3).length,
|
||||
callback: () => {
|
||||
if (3 - ships().filter((e) => e.size === 3).length === 0) return
|
||||
useGameProps.setState({ mode: 1 })
|
||||
setMode(1)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -184,7 +170,7 @@ function EventBar(props: { clear: () => void }) {
|
|||
amount: 2 - ships().filter((e) => e.size === 4).length,
|
||||
callback: () => {
|
||||
if (2 - ships().filter((e) => e.size === 4).length === 0) return
|
||||
useGameProps.setState({ mode: 2 })
|
||||
setMode(2)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -213,31 +199,31 @@ function EventBar(props: { clear: () => void }) {
|
|||
{
|
||||
icon: faGlasses,
|
||||
text: "Spectators",
|
||||
disabled: !payload?.game?.allowSpectators,
|
||||
callback: gameSetting({
|
||||
allowSpectators: !payload?.game?.allowSpectators,
|
||||
disabled: !allowSpectators(),
|
||||
callback: setGameSetting({
|
||||
allowSpectators: !allowSpectators(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: faSparkles,
|
||||
text: "Specials",
|
||||
disabled: !payload?.game?.allowSpecials,
|
||||
callback: gameSetting({
|
||||
allowSpecials: !payload?.game?.allowSpecials,
|
||||
disabled: !allowSpecials(),
|
||||
callback: setGameSetting({
|
||||
allowSpecials: !allowSpecials(),
|
||||
}),
|
||||
},
|
||||
{
|
||||
icon: faComments,
|
||||
text: "Chat",
|
||||
disabled: !payload?.game?.allowChat,
|
||||
callback: gameSetting({ allowChat: !payload?.game?.allowChat }),
|
||||
disabled: !allowChat(),
|
||||
callback: setGameSetting({ allowChat: !allowChat() }),
|
||||
},
|
||||
{
|
||||
icon: faScribble,
|
||||
text: "Mark/Draw",
|
||||
disabled: !payload?.game?.allowMarkDraw,
|
||||
callback: gameSetting({
|
||||
allowMarkDraw: !payload?.game?.allowMarkDraw,
|
||||
disabled: !allowMarkDraw(),
|
||||
callback: setGameSetting({
|
||||
allowMarkDraw: !allowMarkDraw(),
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
@ -248,7 +234,7 @@ function EventBar(props: { clear: () => void }) {
|
|||
iconColor: "green",
|
||||
callback: async () => {
|
||||
socket.emit("gameState", "aborted")
|
||||
await navigate("/")
|
||||
await navigator("/")
|
||||
reset()
|
||||
},
|
||||
},
|
||||
|
@ -257,7 +243,7 @@ function EventBar(props: { clear: () => void }) {
|
|||
text: "No",
|
||||
iconColor: "red",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "main" })
|
||||
setMenu("main")
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -265,22 +251,22 @@ function EventBar(props: { clear: () => void }) {
|
|||
|
||||
createEffect(() => {
|
||||
if (
|
||||
menu !== "moves" ||
|
||||
payload?.game?.state !== "starting" ||
|
||||
mode < 0 ||
|
||||
items().moves[mode].amount
|
||||
menu() !== "moves" ||
|
||||
gameState() !== "starting" ||
|
||||
mode() < 0 ||
|
||||
items().moves[mode()].amount
|
||||
)
|
||||
return
|
||||
const index = items().moves.findIndex((e) => e.amount)
|
||||
useGameProps.setState({ mode: index })
|
||||
setMode(index)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
useDrawProps.setState({ enable: menu === "draw" })
|
||||
useDrawProps.setState({ enable: menu() === "draw" })
|
||||
})
|
||||
|
||||
// createEffect(() => {
|
||||
// if (payload?.game?.state !== "running") return
|
||||
// if (gameState() !== "running") return
|
||||
|
||||
// const toastId = "otherPlayer"
|
||||
// if (isActiveIndex) toast.dismiss(toastId)
|
||||
|
@ -307,59 +293,51 @@ function EventBar(props: { clear: () => void }) {
|
|||
|
||||
return (
|
||||
<div class="event-bar">
|
||||
<Show when={menu !== "main"}>
|
||||
<Show when={menu() !== "main"}>
|
||||
<Item
|
||||
{...{
|
||||
icon: faReply,
|
||||
text: "Return",
|
||||
iconColor: "#555",
|
||||
callback: () => {
|
||||
useGameProps.setState({ menu: "main" })
|
||||
setMenu("main")
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Show>
|
||||
<For each={items()[menu]}>
|
||||
<For each={items()[menu()]}>
|
||||
{(e, i) => (
|
||||
<Show when={isActiveIndex && menu !== "main" && i() !== 1}>
|
||||
<Show when={isActiveIndex() && menu() !== "main" && i() !== 1}>
|
||||
<Item {...e} />
|
||||
</Show>
|
||||
)}
|
||||
</For>
|
||||
<Show when={menu === "moves"}>
|
||||
<Show when={menu() === "moves"}>
|
||||
<Item
|
||||
{...{
|
||||
icon:
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady()
|
||||
? faLock
|
||||
: faCheck,
|
||||
text:
|
||||
selfIndex >= 0 && userStates[selfIndex].isReady()
|
||||
? "unready"
|
||||
: "Done",
|
||||
disabled:
|
||||
payload?.game?.state === "starting" ? mode >= 0 : undefined,
|
||||
enabled:
|
||||
payload?.game?.state === "running" && mode >= 0 && target.show,
|
||||
icon: selfUser()?.isReady() ? faLock : faCheck,
|
||||
text: selfUser()?.isReady() ? "unready" : "Done",
|
||||
disabled: gameState() === "starting" ? mode() >= 0 : undefined,
|
||||
enabled: gameState() === "running" && mode() >= 0 && target().show,
|
||||
callback: () => {
|
||||
if (selfIndex < 0) return
|
||||
switch (payload?.game?.state) {
|
||||
const i = selfIndex()
|
||||
if (i === -1) return
|
||||
switch (gameState()) {
|
||||
case "starting":
|
||||
const isReady = !userStates[selfIndex].isReady
|
||||
setIsReady({ isReady, i: selfIndex })
|
||||
const isReady = !users[i].isReady()
|
||||
setIsReadyFor({ isReady, i })
|
||||
socket.emit("isReady", isReady)
|
||||
break
|
||||
|
||||
case "running":
|
||||
const i = (selfUser?.moves ?? [])
|
||||
.map((e) => e.index)
|
||||
.reduce((prev, curr) => (curr > prev ? curr : prev), 0)
|
||||
const moves = selfUser()?.moves()
|
||||
const length = moves?.length
|
||||
const props = {
|
||||
type: modes[mode].type,
|
||||
x: target.x,
|
||||
y: target.y,
|
||||
orientation: target.orientation,
|
||||
index: (selfUser?.moves ?? []).length ? i + 1 : 0,
|
||||
type: modes[mode()].type,
|
||||
x: target().x,
|
||||
y: target().y,
|
||||
orientation: target().orientation,
|
||||
index: length ?? 0,
|
||||
}
|
||||
socket.emit("dispatchMove", props)
|
||||
setTarget((t) => ({ ...t, show: false }))
|
||||
|
|
|
@ -9,8 +9,20 @@ import HitElems from "~/components/Gamefield/HitElems"
|
|||
import Targets from "~/components/Gamefield/Targets"
|
||||
import { useDraw } from "~/hooks/useDraw"
|
||||
import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import {
|
||||
full,
|
||||
gameId,
|
||||
gameState,
|
||||
mode,
|
||||
mouseCursor,
|
||||
reset,
|
||||
setMode,
|
||||
setMouseCursor,
|
||||
setTargetPreview,
|
||||
target,
|
||||
users,
|
||||
} from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import useSocket from "~/hooks/useSocket"
|
||||
import { socket } from "~/lib/socket"
|
||||
import { overlapsWithAnyBorder } from "~/lib/utils/helpers"
|
||||
|
@ -20,47 +32,35 @@ import Ships from "./Ships"
|
|||
export const count = 12
|
||||
|
||||
function Gamefield() {
|
||||
const { selfUser } = useIndex()
|
||||
const navigate = useNavigate()
|
||||
const {
|
||||
userStates,
|
||||
mode,
|
||||
target,
|
||||
mouseCursor,
|
||||
setMouseCursor,
|
||||
payload,
|
||||
setTargetPreview,
|
||||
full,
|
||||
reset,
|
||||
} = useGameProps()
|
||||
const { ships } = useSession()
|
||||
const navigator = useNavigate()
|
||||
const { isConnected } = useSocket()
|
||||
|
||||
const usingDraw = useDraw()
|
||||
const { enable, color, shouldHide } = useDrawProps()
|
||||
|
||||
createEffect(() => {
|
||||
if (
|
||||
payload?.game?.state !== "starting" ||
|
||||
userStates.reduce((prev, curr) => prev || !curr.isReady, false)
|
||||
gameState() !== "starting" ||
|
||||
!users[0].isReady() ||
|
||||
!users[1].isReady()
|
||||
)
|
||||
return
|
||||
socket.emit("ships", selfUser?.ships ?? [])
|
||||
socket.emit("ships", ships() ?? [])
|
||||
socket.emit("gameState", "running")
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.id || !isConnected) return
|
||||
if (gameId() || !isConnected()) return
|
||||
socket.emit("update", full)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (mode < 0) return
|
||||
const { x, y, show } = target
|
||||
const { shouldShow, ...position } = mouseCursor
|
||||
if (mode() < 0) return
|
||||
const { x, y, show } = target()
|
||||
const { shouldShow, ...position } = mouseCursor()
|
||||
if (
|
||||
!shouldShow ||
|
||||
(payload?.game?.state === "running" &&
|
||||
overlapsWithAnyBorder(position, mode))
|
||||
(gameState() === "running" && overlapsWithAnyBorder(position, mode()))
|
||||
)
|
||||
setTargetPreview((t) => ({ ...t, show: false }))
|
||||
else {
|
||||
|
@ -71,14 +71,14 @@ function Gamefield() {
|
|||
}))
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.key !== "r") return
|
||||
if (payload?.game?.state === "starting") {
|
||||
if (gameState() === "starting") {
|
||||
setTargetPreview((t) => ({
|
||||
...t,
|
||||
orientation: t.orientation === "h" ? "v" : "h",
|
||||
}))
|
||||
}
|
||||
if (payload?.game?.state === "running" && (mode === 1 || mode === 2))
|
||||
useGameProps.setState({ mode: mode === 1 ? 2 : 1 })
|
||||
if (gameState() === "running" && (mode() === 1 || mode() === 2))
|
||||
setMode(mode() === 1 ? 2 : 1)
|
||||
}
|
||||
document.addEventListener("keydown", handleKeyPress)
|
||||
return () => {
|
||||
|
@ -88,15 +88,15 @@ function Gamefield() {
|
|||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.state !== "aborted") return
|
||||
if (gameState() !== "aborted") return
|
||||
// toast.info("Enemy gave up!")
|
||||
navigate("/")
|
||||
navigator("/")
|
||||
reset()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.id) return
|
||||
const timeout = setTimeout(() => navigate("/"), 5000)
|
||||
if (gameId()) return
|
||||
const timeout = setTimeout(() => navigator("/"), 5000)
|
||||
return () => clearTimeout(timeout)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { faBurst, faXmark } from "@fortawesome/pro-solid-svg-icons"
|
||||
import { For } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import { Hit } from "../../interfaces/frontend"
|
||||
|
||||
function HitElems(props: { hits?: Hit[]; colorOverride?: string }) {
|
||||
const { activeUser } = useIndex()
|
||||
const { activeUser } = useSession()
|
||||
const hits = () => props?.hits
|
||||
const colorOverride = () => props?.colorOverride
|
||||
|
||||
return (
|
||||
<For each={hits() ?? activeUser?.hits ?? []}>
|
||||
<For each={hits() ?? activeUser()?.hits()}>
|
||||
{(props) => (
|
||||
<div class="hit-svg" style={{ "--x": props.x, "--y": props.y }}>
|
||||
<FontAwesomeIcon
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { createEffect, createSignal } from "solid-js"
|
||||
import { Show, createEffect, createSignal } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
// import { useDrawProps } from "~/hooks/useDrawProps"
|
||||
// import { HexColorPicker } from "react-colorful"
|
||||
|
@ -33,14 +33,14 @@ function Item(props: ItemProps) {
|
|||
isColor() ? setActive(true) : props.callback && props.callback()
|
||||
}
|
||||
>
|
||||
{isColor() ? (
|
||||
<Show when={isColor()}>
|
||||
<div
|
||||
ref={cpRef!}
|
||||
class={classNames("react-colorful-wrapper", { active: active })}
|
||||
>
|
||||
{/* <HexColorPicker color={color} onChange={setColor} /> */}
|
||||
</div>
|
||||
) : null}
|
||||
</Show>
|
||||
<div
|
||||
class={classNames("container", {
|
||||
amount: typeof props.amount !== "undefined",
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { For, Show } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import { gameState } from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import Ship from "./Ship"
|
||||
|
||||
function Ships() {
|
||||
const { payload } = useGameProps()
|
||||
const { isActiveIndex, selfUser } = useIndex()
|
||||
const { isActiveIndex, selfUser } = useSession()
|
||||
|
||||
return (
|
||||
<Show when={payload?.game?.state === "running" && isActiveIndex}>
|
||||
<For each={selfUser?.ships}>{(props) => <Ship {...props} />}</For>
|
||||
<Show when={gameState() === "running" && isActiveIndex}>
|
||||
<For each={selfUser()?.ships()}>{(props) => <Ship {...props} />}</For>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { For, Match, Switch } from "solid-js"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import useIndex from "~/hooks/useIndex"
|
||||
import useShips from "~/hooks/useShips"
|
||||
import { gameState, mode, target, targetPreview } from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import {
|
||||
composeTargetTiles,
|
||||
intersectingShip,
|
||||
|
@ -12,29 +11,29 @@ import HitElems from "./HitElems"
|
|||
import Ship from "./Ship"
|
||||
|
||||
function Targets() {
|
||||
const { activeUser } = useIndex()
|
||||
const { payload, target, targetPreview, mode } = useGameProps()
|
||||
const { ships } = useShips()
|
||||
const { activeUser, ships } = useSession()
|
||||
|
||||
const ship = shipProps(ships(), mode, targetPreview)
|
||||
const ship = shipProps(ships(), mode(), targetPreview())
|
||||
const { fields, borders, score } = intersectingShip(ships(), ship)
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={payload?.game?.state === "running"}>
|
||||
<For each={composeTargetTiles(target, mode, activeUser?.hits ?? [])}>
|
||||
<Match when={gameState() === "running"}>
|
||||
<For each={composeTargetTiles(target(), mode(), activeUser().hits())}>
|
||||
{(props) => <GamefieldPointer {...props} />}
|
||||
</For>
|
||||
<For
|
||||
each={composeTargetTiles(targetPreview, mode, activeUser?.hits ?? [])}
|
||||
each={composeTargetTiles(
|
||||
targetPreview(),
|
||||
mode(),
|
||||
activeUser().hits(),
|
||||
)}
|
||||
>
|
||||
{(props) => <GamefieldPointer {...props} preview />}
|
||||
</For>
|
||||
</Match>
|
||||
<Match
|
||||
when={
|
||||
payload?.game?.state === "starting" && mode >= 0 && targetPreview.show
|
||||
}
|
||||
when={gameState() === "starting" && mode() >= 0 && targetPreview().show}
|
||||
>
|
||||
<Ship
|
||||
{...ship}
|
||||
|
|
|
@ -2,14 +2,21 @@ import {
|
|||
faRightFromBracket,
|
||||
faSpinnerThird,
|
||||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import { JSX, createEffect, createSignal } from "solid-js"
|
||||
import { JSX, Show, createEffect, createSignal } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import {
|
||||
full,
|
||||
gameId,
|
||||
gamePin,
|
||||
gameState,
|
||||
leave,
|
||||
reset,
|
||||
users,
|
||||
} from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
import useSocket from "~/hooks/useSocket"
|
||||
import { socket } from "~/lib/socket"
|
||||
|
||||
import Button from "./Button"
|
||||
import Icon from "./Icon"
|
||||
import Player from "./Player"
|
||||
|
@ -34,15 +41,11 @@ function WithDots(props: { children: JSX.Element }) {
|
|||
}
|
||||
|
||||
function LobbyFrame(props: { openSettings: () => void }) {
|
||||
const { payload, userStates, full, leave, reset } = useGameProps()
|
||||
const { isConnected } = useSocket()
|
||||
const navigate = useNavigate()
|
||||
const session = useSession()
|
||||
const navigator = useNavigate()
|
||||
const { session } = useSession()
|
||||
const [launchTime, setLaunchTime] = createSignal(3)
|
||||
|
||||
const launching = () =>
|
||||
payload?.users.length === 2 &&
|
||||
!userStates.filter((user) => !user.isReady).length
|
||||
const launching = () => users[0].isReady() && users[1].isReady()
|
||||
|
||||
createEffect(() => {
|
||||
if (!launching() || launchTime() > 0) return
|
||||
|
@ -61,17 +64,13 @@ function LobbyFrame(props: { openSettings: () => void }) {
|
|||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (payload?.game?.id || !isConnected) return
|
||||
if (gameId() || !isConnected()) return
|
||||
socket.emit("update", full)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (
|
||||
typeof payload?.game?.state !== "string" ||
|
||||
payload?.game?.state === "lobby"
|
||||
)
|
||||
return
|
||||
navigate("/gamefield")
|
||||
if (gameState() === "unknown" || gameState() === "lobby") return
|
||||
navigator("/gamefield")
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -79,53 +78,50 @@ function LobbyFrame(props: { openSettings: () => void }) {
|
|||
<div class="flex items-center justify-between border-b-2 border-slate-900">
|
||||
<Icon src="speech_bubble.png">Chat</Icon>
|
||||
<h1 class="font-farro text-5xl font-medium">
|
||||
{launching() ? (
|
||||
<WithDots>
|
||||
{launchTime() < 0
|
||||
? "Game starts"
|
||||
: "Game is starting in " + launchTime()}
|
||||
</WithDots>
|
||||
) : (
|
||||
<>
|
||||
{"Game-PIN: "}
|
||||
{isConnected() ? (
|
||||
<span class="underline">{payload?.gamePin ?? "----"}</span>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faSpinnerThird} spin />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Show
|
||||
when={!launching()}
|
||||
fallback={
|
||||
<WithDots>
|
||||
{launchTime() < 0
|
||||
? "Game starts"
|
||||
: "Game is starting in " + launchTime()}
|
||||
</WithDots>
|
||||
}
|
||||
>
|
||||
{"Game-PIN: "}
|
||||
<Show
|
||||
when={isConnected()}
|
||||
fallback={<FontAwesomeIcon icon={faSpinnerThird} spin />}
|
||||
>
|
||||
<span class="underline">{gamePin() ?? "----"}</span>
|
||||
</Show>
|
||||
</Show>
|
||||
</h1>
|
||||
<Icon src="gear.png" onClick={props.openSettings}>
|
||||
Settings
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="flex items-center justify-around">
|
||||
{isConnected() ? (
|
||||
<Show
|
||||
when={isConnected()}
|
||||
fallback={
|
||||
<p class="font-farro m-48 text-center text-6xl font-medium">
|
||||
Warte auf Verbindung
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<>
|
||||
<Player
|
||||
src="player_blue.png"
|
||||
i={0}
|
||||
userId={session.latest?.user?.id}
|
||||
/>
|
||||
<Player src="player_blue.png" i={0} userId={session()?.user?.id} />
|
||||
<p class="font-farro m-4 text-6xl font-semibold">VS</p>
|
||||
{payload?.users[1] ? (
|
||||
<Player
|
||||
src="player_red.png"
|
||||
i={1}
|
||||
userId={session.latest?.user?.id}
|
||||
/>
|
||||
{users[1].id() ? (
|
||||
<Player src="player_red.png" i={1} userId={session()?.user?.id} />
|
||||
) : (
|
||||
<p class="font-farro w-96 text-center text-4xl font-medium">
|
||||
<WithDots>Warte auf Spieler 2</WithDots>
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<p class="font-farro m-48 text-center text-6xl font-medium">
|
||||
Warte auf Verbindung
|
||||
</p>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<div class="flex items-center justify-around border-t-2 border-slate-900 p-4">
|
||||
<Button
|
||||
|
@ -134,7 +130,7 @@ function LobbyFrame(props: { openSettings: () => void }) {
|
|||
onClick={() => {
|
||||
leave(async () => {
|
||||
reset()
|
||||
await navigate("/")
|
||||
navigator("/")
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -8,9 +8,9 @@ import {
|
|||
} from "@fortawesome/pro-solid-svg-icons"
|
||||
import { faCaretDown } from "@fortawesome/sharp-solid-svg-icons"
|
||||
import classNames from "classnames"
|
||||
import { createEffect, createSignal } from "solid-js"
|
||||
import { Show, createEffect, createSignal } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { setIsReadyFor, users } from "~/hooks/useGameProps"
|
||||
import { socket } from "~/lib/socket"
|
||||
import Button from "./Button"
|
||||
|
||||
|
@ -42,13 +42,11 @@ function HourGlass() {
|
|||
)
|
||||
}
|
||||
|
||||
function Player(props: { src: string; i: number; userId?: string }) {
|
||||
const { payload, userStates, setIsReady } = useGameProps()
|
||||
const player = () => payload?.users[props.i]
|
||||
const isReady = () => userStates[props.i].isReady
|
||||
const isConnected = () => userStates[props.i].isConnected
|
||||
const primary = () =>
|
||||
props.userId && props.userId === payload?.users[props.i]?.id
|
||||
function Player(props: { src: string; i: 0 | 1; userId?: string }) {
|
||||
const player = () => users[props.i]
|
||||
const isReady = () => users[props.i].isReady()
|
||||
const isConnected = () => users[props.i].isConnected()
|
||||
const primary = () => props.userId && props.userId === users[props.i].id()
|
||||
|
||||
return (
|
||||
<div class="flex w-96 flex-col items-center gap-4 p-4">
|
||||
|
@ -58,7 +56,7 @@ function Player(props: { src: string; i: number; userId?: string }) {
|
|||
primary() ? "font-semibold" : "font-normal",
|
||||
)}
|
||||
>
|
||||
{player?.name ?? "Spieler " + (player()?.index === 2 ? "2" : "1")}
|
||||
{player().name() ?? "Spieler " + (props.i === 1 ? "2" : "1")}
|
||||
</p>
|
||||
<div class="relative">
|
||||
<img
|
||||
|
@ -66,14 +64,14 @@ function Player(props: { src: string; i: number; userId?: string }) {
|
|||
src={"/assets/" + props.src}
|
||||
alt={props.src}
|
||||
/>
|
||||
{primary() ? (
|
||||
<Show when={primary()}>
|
||||
<button class="absolute right-4 top-4 h-14 w-14 rounded-lg border-2 border-dashed border-warn bg-gray-800 bg-opacity-90">
|
||||
<FontAwesomeIcon
|
||||
class="h-full w-full text-warn"
|
||||
icon={faCaretDown}
|
||||
/>
|
||||
</button>
|
||||
) : null}
|
||||
</Show>
|
||||
</div>
|
||||
<Button
|
||||
type={isConnected() ? (isReady() ? "green" : "orange") : "gray"}
|
||||
|
@ -81,11 +79,11 @@ function Player(props: { src: string; i: number; userId?: string }) {
|
|||
isLatched={!!isReady()}
|
||||
onClick={() => {
|
||||
if (!player()) return
|
||||
setIsReady({
|
||||
socket.emit("isReady", !isReady())
|
||||
setIsReadyFor({
|
||||
i: props.i,
|
||||
isReady: !isReady(),
|
||||
})
|
||||
socket.emit("isReady", !isReady())
|
||||
}}
|
||||
disabled={!primary()}
|
||||
>
|
||||
|
|
|
@ -5,13 +5,25 @@ import {
|
|||
import classNames from "classnames"
|
||||
import { JSX } from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { setGameSetting } from "~/components/Gamefield/EventBar"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import {
|
||||
allowChat,
|
||||
allowMarkDraw,
|
||||
allowSpecials,
|
||||
allowSpectators,
|
||||
setGameSetting,
|
||||
} from "~/hooks/useGameProps"
|
||||
import { GameSettingKeys } from "../../../interfaces/frontend"
|
||||
|
||||
function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
||||
const { payload, setSetting, full } = useGameProps()
|
||||
const state = () => payload?.game?.[props.key]
|
||||
const state = () => {
|
||||
const gameProps = {
|
||||
allowChat,
|
||||
allowMarkDraw,
|
||||
allowSpecials,
|
||||
allowSpectators,
|
||||
}
|
||||
return gameProps[props.key]()
|
||||
}
|
||||
|
||||
return (
|
||||
<label class="flex items-center justify-between" for={props.key}>
|
||||
|
@ -23,7 +35,7 @@ function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
|||
"text-md mx-auto rounded-full px-4 drop-shadow-md transition-all",
|
||||
state() ? "text-blue-500" : "text-gray-800",
|
||||
{
|
||||
"bg-gray-300 ": state,
|
||||
"bg-gray-300 ": state(),
|
||||
},
|
||||
)}
|
||||
size="3x"
|
||||
|
@ -35,13 +47,9 @@ function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
|||
type="checkbox"
|
||||
id={props.key}
|
||||
onChange={() =>
|
||||
setGameSetting(
|
||||
{
|
||||
[props.key]: !state,
|
||||
},
|
||||
setSetting,
|
||||
full,
|
||||
)
|
||||
setGameSetting({
|
||||
[props.key]: !state(),
|
||||
})
|
||||
}
|
||||
hidden={true}
|
||||
/>
|
||||
|
|
|
@ -2,17 +2,15 @@ import { faRotateLeft } from "@fortawesome/pro-regular-svg-icons"
|
|||
import { faXmark } from "@fortawesome/pro-solid-svg-icons"
|
||||
import {} from "solid-js"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { full, setSetting } from "~/hooks/useGameProps"
|
||||
import { socket } from "~/lib/socket"
|
||||
import { GameSettings } from "../../../interfaces/frontend"
|
||||
import Setting from "./Setting"
|
||||
|
||||
function Settings(props: { closeSettings: () => void }) {
|
||||
const { setSetting, full } = useGameProps()
|
||||
|
||||
const gameSetting = (payload: GameSettings) => {
|
||||
const hash = setSetting(payload)
|
||||
socket.emit("gameSetting", payload, (newHash) => {
|
||||
const gameSetting = (newSettings: GameSettings) => {
|
||||
const hash = setSetting(newSettings)
|
||||
socket.emit("gameSetting", newSettings, (newHash) => {
|
||||
if (newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", full)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import type { AdapterAccount } from "@auth/core/adapters"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
import { relations } from "drizzle-orm"
|
||||
import {
|
||||
AnyPgColumn,
|
||||
boolean,
|
||||
integer,
|
||||
pgTable,
|
||||
|
@ -15,7 +13,7 @@ import { gameState, moveType, orientation } from "./Types"
|
|||
|
||||
export const users = pgTable("user", {
|
||||
id: text("id").notNull().primaryKey(),
|
||||
name: text("name"),
|
||||
name: text("name").notNull(),
|
||||
email: text("email").notNull().unique(),
|
||||
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||
image: text("image"),
|
||||
|
@ -69,7 +67,7 @@ export const verificationTokens = pgTable(
|
|||
)
|
||||
|
||||
export const games = pgTable("game", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
updatedAt: timestamp("updated_at").notNull().defaultNow(),
|
||||
state: gameState("state").notNull().default("lobby"),
|
||||
|
@ -80,17 +78,17 @@ export const games = pgTable("game", {
|
|||
})
|
||||
|
||||
export const gamepins = pgTable("gamepin", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
pin: text("pin").notNull().unique(),
|
||||
gameId: text("game_id")
|
||||
.notNull()
|
||||
.unique()
|
||||
.references((): AnyPgColumn => games.id),
|
||||
.references(() => games.id, { onDelete: "cascade" }),
|
||||
})
|
||||
|
||||
export const ships = pgTable("ship", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
size: integer("size").notNull(),
|
||||
variant: integer("variant").notNull(),
|
||||
x: integer("x").notNull(),
|
||||
|
@ -102,7 +100,7 @@ export const ships = pgTable("ship", {
|
|||
})
|
||||
|
||||
export const hits = pgTable("hit", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
x: integer("x").notNull(),
|
||||
y: integer("y").notNull(),
|
||||
hit: boolean("hit").notNull(),
|
||||
|
@ -112,7 +110,7 @@ export const hits = pgTable("hit", {
|
|||
})
|
||||
|
||||
export const moves = pgTable("move", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
index: integer("index").notNull(),
|
||||
type: moveType("type").notNull(),
|
||||
|
@ -125,7 +123,7 @@ export const moves = pgTable("move", {
|
|||
})
|
||||
|
||||
export const chats = pgTable("chat", {
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
message: text("message"),
|
||||
event: text("event"),
|
||||
|
@ -137,7 +135,7 @@ export const chats = pgTable("chat", {
|
|||
export const user_games = pgTable(
|
||||
"user_game",
|
||||
{
|
||||
id: text("id").notNull().primaryKey().default(createId()),
|
||||
id: text("id").notNull().primaryKey(),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
gameId: text("game_id")
|
||||
.notNull()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { pgEnum } from "drizzle-orm/pg-core"
|
||||
|
||||
export const gameState = pgEnum("game_state", [
|
||||
"unknown",
|
||||
"lobby",
|
||||
"starting",
|
||||
"running",
|
||||
|
|
|
@ -1,261 +1,262 @@
|
|||
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
||||
/* eslint-disable solid/reactivity */
|
||||
import { socket } from "~/lib/socket"
|
||||
import { GamePropsSchema, GameState, MoveType } from "~/lib/zodSchemas"
|
||||
// import { toast } from "react-toastify"
|
||||
import { createSignal } from "solid-js"
|
||||
import { getPayloadFromProps } from "~/lib/getPayloadFromProps"
|
||||
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
||||
import {
|
||||
initialUser,
|
||||
initlialMouseCursor,
|
||||
initlialTarget,
|
||||
initlialTargetPreview,
|
||||
intersectingShip,
|
||||
targetList,
|
||||
} from "~/lib/utils/helpers"
|
||||
import {
|
||||
GamePropsSchema,
|
||||
GameState,
|
||||
MoveType,
|
||||
PlayerSchema,
|
||||
optionalGamePropsSchema,
|
||||
} from "~/lib/zodSchemas"
|
||||
// import { toast } from "react-toastify"
|
||||
import create from "solid-zustand"
|
||||
|
||||
import { Accessor, Setter, createSignal } from "solid-js"
|
||||
import {
|
||||
EventBarModes,
|
||||
GameSettings,
|
||||
MouseCursor,
|
||||
MoveDispatchProps,
|
||||
NewUsers,
|
||||
ShipProps,
|
||||
Target,
|
||||
TargetPreview,
|
||||
} from "../interfaces/frontend"
|
||||
|
||||
const initialState: optionalGamePropsSchema & {
|
||||
userStates: {
|
||||
isReady: Accessor<boolean>
|
||||
setIsReady: Setter<boolean>
|
||||
isConnected: Accessor<boolean>
|
||||
setIsConnected: Setter<boolean>
|
||||
}[]
|
||||
menu: keyof EventBarModes
|
||||
mode: number
|
||||
target: Target
|
||||
targetPreview: TargetPreview
|
||||
mouseCursor: MouseCursor
|
||||
} = {
|
||||
menu: "moves",
|
||||
mode: 0,
|
||||
payload: null,
|
||||
hash: null,
|
||||
target: initlialTarget,
|
||||
targetPreview: initlialTargetPreview,
|
||||
mouseCursor: initlialMouseCursor,
|
||||
userStates: Array.from(Array(2), () => {
|
||||
const [isReady, setIsReady] = createSignal(false)
|
||||
const [isConnected, setIsConnected] = createSignal(false)
|
||||
return { isReady, setIsReady, isConnected, setIsConnected }
|
||||
}),
|
||||
export const [hash, setHash] = createSignal<string | null>(null)
|
||||
export const [activeIndex, setActiveIndex] = createSignal<0 | 1>(0)
|
||||
export const [gamePin, setGamePin] = createSignal<string | null>(null)
|
||||
export const [gameId, setGameId] = createSignal<string>("")
|
||||
export const [gameState, setGameState] = createSignal<GameState>("unknown")
|
||||
export const [allowChat, setAllowChat] = createSignal(false)
|
||||
export const [allowMarkDraw, setAllowMarkDraw] = createSignal(false)
|
||||
export const [allowSpecials, setAllowSpecials] = createSignal(false)
|
||||
export const [allowSpectators, setallowSpectators] = createSignal(false)
|
||||
export const [menu, setMenu] = createSignal<keyof EventBarModes>("moves")
|
||||
export const [mode, setMode] = createSignal(0)
|
||||
export const [target, setTarget] = createSignal<Target>(initlialTarget)
|
||||
export const [targetPreview, setTargetPreview] = createSignal<TargetPreview>(
|
||||
initlialTargetPreview,
|
||||
)
|
||||
export const [mouseCursor, setMouseCursor] =
|
||||
createSignal<MouseCursor>(initlialMouseCursor)
|
||||
export const users = {
|
||||
0: initialUser(),
|
||||
1: initialUser(),
|
||||
forEach(cb: (user: ReturnType<typeof initialUser>, i: 0 | 1) => void) {
|
||||
cb(this[0], 0)
|
||||
cb(this[1], 1)
|
||||
},
|
||||
map<T>(cb: (user: ReturnType<typeof initialUser>, i: 0 | 1) => T) {
|
||||
return { 0: cb(this[0], 0), 1: cb(this[1], 1) }
|
||||
},
|
||||
}
|
||||
|
||||
export type State = typeof initialState
|
||||
// export function setActiveIndex(i: number, selfIndex: number) {
|
||||
// if (!payload()) return
|
||||
// payload().activeIndex = i
|
||||
// if (i === selfIndex) {
|
||||
// setMenu("moves")
|
||||
// setMode(0)
|
||||
// } else {
|
||||
// setMenu("main")
|
||||
// setMode(-1)
|
||||
// }
|
||||
// }
|
||||
export function DispatchMove(move: MoveDispatchProps, index: number) {
|
||||
const list = targetList(move, move.type)
|
||||
users.forEach((user, i) => {
|
||||
if (!user) return
|
||||
|
||||
export type Action = {
|
||||
DispatchMove: (props: MoveDispatchProps, i: number) => void
|
||||
setTarget: (target: Target | ((i: Target) => Target)) => void
|
||||
setTargetPreview: (
|
||||
targetPreview: TargetPreview | ((i: TargetPreview) => TargetPreview),
|
||||
) => void
|
||||
setMouseCursor: (
|
||||
mouseCursor: MouseCursor | ((i: MouseCursor) => MouseCursor),
|
||||
) => void
|
||||
setPlayer: (payload: { users: PlayerSchema[] }) => string | null
|
||||
setSetting: (settings: GameSettings) => string | null
|
||||
full: (newProps: GamePropsSchema) => void
|
||||
leave: (cb: () => void) => void
|
||||
setIsReady: (payload: { i: number; isReady: boolean }) => void
|
||||
gameState: (newState: GameState) => void
|
||||
setShips: (ships: ShipProps[], index: number) => void
|
||||
removeShip: (props: ShipProps, index: number) => void
|
||||
setIsConnected: (payload: { i: number; isConnected: boolean }) => void
|
||||
reset: () => void
|
||||
setActiveIndex: (i: number, selfIndex: number) => void
|
||||
if (index === i) {
|
||||
user.setMoves((e) => [...e, move])
|
||||
} else {
|
||||
if (move.type === MoveType.Enum.radar) return
|
||||
user.setHits((e) => [
|
||||
...e,
|
||||
...list.map(({ x, y }) => ({
|
||||
hit: !!intersectingShip(user.ships(), {
|
||||
...move,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
}).fields.length,
|
||||
x,
|
||||
y,
|
||||
})),
|
||||
])
|
||||
}
|
||||
})
|
||||
}
|
||||
export function setShips(ships: ShipProps[], index: number) {
|
||||
users.forEach(({ setShips }, i) => {
|
||||
if (index !== i) return
|
||||
setShips(ships)
|
||||
})
|
||||
}
|
||||
export function removeShip({ size, variant, x, y }: ShipProps, index: number) {
|
||||
users.forEach((user, i) => {
|
||||
if (index !== i) return
|
||||
const indexToRemove = user
|
||||
.ships()
|
||||
.findIndex(
|
||||
(ship) =>
|
||||
ship.size === size &&
|
||||
ship.variant === variant &&
|
||||
ship.x === x &&
|
||||
ship.y === y,
|
||||
)
|
||||
user.setShips((ships) => ships.filter((_, i) => i !== indexToRemove))
|
||||
})
|
||||
}
|
||||
|
||||
export const useGameProps = create<State & Action>()((set) => ({
|
||||
...initialState,
|
||||
setActiveIndex: (i, selfIndex) =>
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.activeIndex = i
|
||||
if (i === selfIndex) {
|
||||
state.menu = "moves"
|
||||
state.mode = 0
|
||||
} else {
|
||||
state.menu = "main"
|
||||
state.mode = -1
|
||||
}
|
||||
return state
|
||||
}),
|
||||
DispatchMove: (move, i) =>
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
const list = targetList(move, move.type)
|
||||
state.payload.users.map((e) => {
|
||||
if (!e) return e
|
||||
if (i === e.index) e.moves.push(move)
|
||||
else if (move.type !== MoveType.Enum.radar)
|
||||
e.hits.push(
|
||||
...list.map(({ x, y }) => ({
|
||||
hit: !!intersectingShip(e.ships, {
|
||||
...move,
|
||||
size: 1,
|
||||
variant: 0,
|
||||
}).fields.length,
|
||||
x,
|
||||
y,
|
||||
})),
|
||||
)
|
||||
export function setPlayer(newUsers: NewUsers): string | null {
|
||||
let hash: string | null = null
|
||||
console.log(newUsers)
|
||||
users.forEach((user, i) => {
|
||||
const newUser = newUsers[i]
|
||||
if (!newUser) return defaultUser(user)
|
||||
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setTarget: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function") state.target = dispatch(state.target)
|
||||
else state.target = dispatch
|
||||
return state
|
||||
}),
|
||||
setTargetPreview: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.targetPreview = dispatch(state.targetPreview)
|
||||
else state.targetPreview = dispatch
|
||||
return state
|
||||
}),
|
||||
setMouseCursor: (dispatch) =>
|
||||
set((state: State) => {
|
||||
if (typeof dispatch === "function")
|
||||
state.mouseCursor = dispatch(state.mouseCursor)
|
||||
else state.mouseCursor = dispatch
|
||||
return state
|
||||
}),
|
||||
setShips: (ships, index) =>
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.users = state.payload.users.map((e) => {
|
||||
if (!e || e.index !== index) return e
|
||||
e.ships = ships
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
removeShip: ({ size, variant, x, y }, index) =>
|
||||
set((state: State) => {
|
||||
state.payload?.users.map((e) => {
|
||||
if (!e || e.index !== index) return
|
||||
const indexToRemove = e.ships.findIndex(
|
||||
(ship) =>
|
||||
ship.size === size &&
|
||||
ship.variant === variant &&
|
||||
ship.x === x &&
|
||||
ship.y === y,
|
||||
)
|
||||
e.ships.splice(indexToRemove, 1)
|
||||
return e
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setPlayer: (payload) => {
|
||||
let hash: string | null = null
|
||||
set((state: State) => {
|
||||
if (!state.payload) return state
|
||||
state.payload.users = payload.users
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
user.setId(newUser.id)
|
||||
user.setName(newUser.name)
|
||||
user.setChats(newUser.chats)
|
||||
user.setMoves(newUser.moves)
|
||||
user.setShips(newUser.ships)
|
||||
user.setHits(newUser.hits)
|
||||
})
|
||||
const body = getPayloadwithChecksum(getPayloadFromProps())
|
||||
if (!body.hash) {
|
||||
console.log("Something is wrong... ")
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return null
|
||||
}
|
||||
hash = body.hash
|
||||
setHash(hash)
|
||||
return hash
|
||||
}
|
||||
export function setSetting(newSettings: GameSettings): string | null {
|
||||
let hash: string | null = null
|
||||
setAllowChat((e) => newSettings.allowChat ?? e)
|
||||
setAllowMarkDraw((e) => newSettings.allowMarkDraw ?? e)
|
||||
setAllowSpecials((e) => newSettings.allowSpecials ?? e)
|
||||
setallowSpectators((e) => newSettings.allowSpectators ?? e)
|
||||
const body = getPayloadwithChecksum(getPayloadFromProps())
|
||||
if (!body.hash) {
|
||||
console.log("Something is wrong... ")
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return null
|
||||
}
|
||||
hash = body.hash
|
||||
setHash(hash)
|
||||
return hash
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
return state
|
||||
export function setGameSetting(newSettings: GameSettings) {
|
||||
return () => {
|
||||
const hash = setSetting(newSettings)
|
||||
socket.emit("gameSetting", newSettings, (newHash) => {
|
||||
if (newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", full)
|
||||
})
|
||||
return hash
|
||||
},
|
||||
setSetting: (settings) => {
|
||||
let hash: string | null = null
|
||||
set((state: State) => {
|
||||
if (!state.payload?.game) return state
|
||||
Object.assign(state.payload.game, settings)
|
||||
const body = getPayloadwithChecksum(state.payload)
|
||||
if (!body.hash) {
|
||||
// toast.warn("Something is wrong... ", {
|
||||
// toastId: "st_wrong",
|
||||
// theme: "colored",
|
||||
// })
|
||||
return state
|
||||
}
|
||||
hash = body.hash
|
||||
state.hash = hash
|
||||
return state
|
||||
})
|
||||
return hash
|
||||
},
|
||||
full: (newGameProps) =>
|
||||
// eslint-disable-next-line solid/reactivity
|
||||
set((state) => {
|
||||
if (state.hash === newGameProps.hash) {
|
||||
console.log("Everything up to date.")
|
||||
} else {
|
||||
console.log("Update was needed.", state.hash, newGameProps.hash)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
state.payload?.game?.id &&
|
||||
state.payload?.game?.id !== newGameProps.payload?.game?.id
|
||||
) {
|
||||
console.warn(
|
||||
"Different gameId detected on update: ",
|
||||
state.payload?.game?.id,
|
||||
newGameProps.payload?.game?.id,
|
||||
)
|
||||
}
|
||||
export function full(newProps: GamePropsSchema) {
|
||||
if (hash() === newProps.hash) {
|
||||
console.log("Everything up to date.")
|
||||
} else {
|
||||
console.log("Update was needed.", hash(), newProps.hash)
|
||||
|
||||
return newGameProps
|
||||
}
|
||||
return state
|
||||
}),
|
||||
leave: (cb) => {
|
||||
socket.emit("leave", (ack) => {
|
||||
if (!ack) {
|
||||
// toast.error("Something is wrong...")
|
||||
}
|
||||
cb()
|
||||
if (gameId() !== newProps.payload?.game?.id)
|
||||
console.warn(
|
||||
"Different gameId detected on update: ",
|
||||
gameId(),
|
||||
newProps.payload?.game?.id,
|
||||
)
|
||||
|
||||
setHash(newProps.hash)
|
||||
setActiveIndex(newProps.payload.activeIndex)
|
||||
setGamePin(newProps.payload.gamePin)
|
||||
setGameId(newProps.payload.game?.id ?? "")
|
||||
setGameState(newProps.payload.game?.state ?? "unknown")
|
||||
setAllowChat(newProps.payload.game?.allowChat ?? false)
|
||||
setAllowMarkDraw(newProps.payload.game?.allowMarkDraw ?? false)
|
||||
setAllowSpecials(newProps.payload.game?.allowSpecials ?? false)
|
||||
setallowSpectators(newProps.payload.game?.allowSpectators ?? false)
|
||||
users.forEach((user, i) => {
|
||||
const newUser = newProps.payload.users[i]
|
||||
if (!newUser) return
|
||||
|
||||
user.setId(newUser.id)
|
||||
user.setName(newUser.name)
|
||||
user.setChats(newUser.chats)
|
||||
user.setMoves(newUser.moves)
|
||||
user.setShips(newUser.ships)
|
||||
user.setHits(newUser.hits)
|
||||
})
|
||||
},
|
||||
setIsReady: ({ i, isReady }) =>
|
||||
set((state: State) => {
|
||||
state.userStates[i].setIsReady(isReady)
|
||||
state.userStates[i].setIsConnected(true)
|
||||
return state
|
||||
}),
|
||||
gameState: (newState: GameState) =>
|
||||
set((state: State) => {
|
||||
if (!state.payload?.game) return state
|
||||
state.payload.game.state = newState
|
||||
state.userStates.forEach((e) => {
|
||||
e.setIsReady(false)
|
||||
})
|
||||
return state
|
||||
}),
|
||||
setIsConnected: ({ i, isConnected }) =>
|
||||
set((state: State) => {
|
||||
state.userStates[i].setIsConnected(isConnected)
|
||||
if (!isConnected) state.userStates[i].setIsReady(false)
|
||||
return state
|
||||
}),
|
||||
reset: () => {
|
||||
set(initialState)
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
export function leave(cb: () => void) {
|
||||
socket.emit("leave", (ack) => {
|
||||
if (!ack) {
|
||||
console.log("Something is wrong... ")
|
||||
// toast.error("Something is wrong...")
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
export function setIsReadyFor({ i, isReady }: { i: 0 | 1; isReady: boolean }) {
|
||||
users[i].setIsReady(isReady)
|
||||
users[i].setIsConnected(true)
|
||||
}
|
||||
export function newGameState(newState: GameState) {
|
||||
setGameState(newState)
|
||||
users.forEach((e) => e.setIsReady(false))
|
||||
}
|
||||
export function setIsConnectedFor({
|
||||
i,
|
||||
isConnected,
|
||||
}: {
|
||||
i: 0 | 1
|
||||
isConnected: boolean
|
||||
}) {
|
||||
users[i].setIsConnected(isConnected)
|
||||
if (isConnected) return
|
||||
users[i].setIsReady(false)
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
setHash(null)
|
||||
setActiveIndex(0)
|
||||
setGamePin(null)
|
||||
setGameId("")
|
||||
setGameState("unknown")
|
||||
setallowSpectators(false)
|
||||
setAllowSpecials(false)
|
||||
setAllowChat(false)
|
||||
setAllowMarkDraw(false)
|
||||
setMenu("moves")
|
||||
setMode(0)
|
||||
setTarget(initlialTarget)
|
||||
setTargetPreview(initlialTargetPreview)
|
||||
setMouseCursor(initlialMouseCursor)
|
||||
users.forEach(defaultUser)
|
||||
}
|
||||
|
||||
function defaultUser(user: ReturnType<typeof initialUser>) {
|
||||
user.setIsReady(false)
|
||||
user.setIsConnected(false)
|
||||
user.setId("")
|
||||
user.setName("")
|
||||
user.setChats([])
|
||||
user.setMoves([])
|
||||
user.setShips([])
|
||||
user.setHits([])
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { useSession } from "~/hooks/useSession"
|
||||
|
||||
import { useGameProps } from "./useGameProps"
|
||||
|
||||
function useIndex() {
|
||||
const { payload } = useGameProps()
|
||||
const session = useSession()
|
||||
|
||||
const selfIndex =
|
||||
payload?.users.findIndex((e) => e?.id === session.latest?.user.id) ?? -1
|
||||
const activeIndex = payload?.activeIndex ?? -1
|
||||
const isActiveIndex = selfIndex >= 0 && payload?.activeIndex === selfIndex
|
||||
const selfUser = payload?.users[selfIndex]
|
||||
const activeUser = payload?.users[activeIndex === 0 ? 1 : 0]
|
||||
|
||||
return {
|
||||
selfIndex,
|
||||
activeIndex,
|
||||
isActiveIndex,
|
||||
selfUser,
|
||||
activeUser,
|
||||
}
|
||||
}
|
||||
|
||||
export default useIndex
|
|
@ -1,12 +0,0 @@
|
|||
import { getSession } from "@auth/solid-start"
|
||||
import { createServerData$ } from "solid-start/server"
|
||||
import { authOptions } from "~/server/auth"
|
||||
|
||||
export const useSession = () => {
|
||||
return createServerData$(
|
||||
async (_, { request }) => {
|
||||
return await getSession(request, authOptions)
|
||||
},
|
||||
{ key: () => ["auth_user"] },
|
||||
)
|
||||
}
|
77
leaky-ships/src/hooks/useSession.tsx
Normal file
77
leaky-ships/src/hooks/useSession.tsx
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { Session } from "@auth/core/types"
|
||||
import { getSession } from "@auth/solid-start"
|
||||
import {
|
||||
Accessor,
|
||||
JSX,
|
||||
createContext,
|
||||
createSignal,
|
||||
useContext,
|
||||
} from "solid-js"
|
||||
import { createServerData$ } from "solid-start/server"
|
||||
import { ShipProps } from "~/interfaces/frontend"
|
||||
import { initialUser } from "~/lib/utils/helpers"
|
||||
import { authOptions } from "~/server/auth"
|
||||
import { activeIndex, users } from "./useGameProps"
|
||||
|
||||
interface Concext {
|
||||
session: Accessor<Session | null>
|
||||
selfIndex: () => 0 | 1 | -1
|
||||
activeIndex: Accessor<0 | 1>
|
||||
isActiveIndex: () => boolean
|
||||
selfUser: () => ReturnType<typeof initialUser> | null
|
||||
activeUser: () => ReturnType<typeof initialUser>
|
||||
ships: () => ShipProps[]
|
||||
}
|
||||
|
||||
const [state, setState] = createSignal<Session | null>(null)
|
||||
const selfIndex = () => {
|
||||
switch (state()?.user?.id) {
|
||||
case users[0].id():
|
||||
return 0
|
||||
case users[1].id():
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
const isActiveIndex = () => {
|
||||
const sI = selfIndex()
|
||||
return sI >= 0 && activeIndex() === sI
|
||||
}
|
||||
const selfUser = () => {
|
||||
const i = selfIndex()
|
||||
if (i === -1) return null
|
||||
return users[i]
|
||||
}
|
||||
const activeUser = () => users[activeIndex() === 0 ? 1 : 0]
|
||||
|
||||
const ships = () => selfUser()?.ships() ?? []
|
||||
const contextValue = {
|
||||
session: state,
|
||||
selfIndex,
|
||||
activeIndex,
|
||||
isActiveIndex,
|
||||
selfUser,
|
||||
activeUser,
|
||||
ships,
|
||||
}
|
||||
export const SessionCtx = createContext<Concext>(contextValue)
|
||||
|
||||
export function SessionProvider(props: { children: JSX.Element }) {
|
||||
const session = createServerData$(
|
||||
async (_, { request }) => {
|
||||
return await getSession(request, authOptions)
|
||||
},
|
||||
{ key: () => ["auth_user"] },
|
||||
)()
|
||||
setState(session ?? null)
|
||||
|
||||
return (
|
||||
<SessionCtx.Provider value={contextValue}>
|
||||
{props.children}
|
||||
</SessionCtx.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSession = () => useContext(SessionCtx)
|
|
@ -1,17 +0,0 @@
|
|||
import { ShipProps } from "../interfaces/frontend"
|
||||
import { useGameProps } from "./useGameProps"
|
||||
import useIndex from "./useIndex"
|
||||
|
||||
function useShips() {
|
||||
const gameProps = useGameProps()
|
||||
const { selfIndex } = useIndex()
|
||||
|
||||
const ships = () =>
|
||||
gameProps.payload?.users.find((e) => e?.index === selfIndex)?.ships ?? []
|
||||
const setShips = (ships: ShipProps[]) => gameProps.setShips(ships, selfIndex)
|
||||
const removeShip = (ship: ShipProps) => gameProps.removeShip(ship, selfIndex)
|
||||
|
||||
return { ships, setShips, removeShip }
|
||||
}
|
||||
|
||||
export default useShips
|
|
@ -6,35 +6,41 @@ import { socket } from "~/lib/socket"
|
|||
import { GamePropsSchema } from "~/lib/zodSchemas"
|
||||
import { isAuthenticated } from "~/routes/start"
|
||||
import { GameSettings, PlayerEvent } from "../interfaces/frontend"
|
||||
import { useGameProps } from "./useGameProps"
|
||||
import useIndex from "./useIndex"
|
||||
import {
|
||||
DispatchMove,
|
||||
full,
|
||||
gameId,
|
||||
gameState,
|
||||
setActiveIndex,
|
||||
setIsConnectedFor,
|
||||
setIsReadyFor,
|
||||
setMenu,
|
||||
setMode,
|
||||
setPlayer,
|
||||
setSetting,
|
||||
setShips,
|
||||
users,
|
||||
} from "./useGameProps"
|
||||
import { useSession } from "./useSession"
|
||||
|
||||
/** This function should only be called once per page, otherwise there will be multiple socket connections and duplicate event listeners. */
|
||||
function useSocket() {
|
||||
const [isConnectedState, setIsConnectedState] = createSignal(false)
|
||||
const { selfIndex } = useIndex()
|
||||
const {
|
||||
payload,
|
||||
userStates,
|
||||
setPlayer,
|
||||
setSetting,
|
||||
full,
|
||||
setIsReady,
|
||||
gameState,
|
||||
setIsConnected,
|
||||
setActiveIndex,
|
||||
DispatchMove,
|
||||
setShips,
|
||||
} = useGameProps()
|
||||
const { selfIndex } = useSession()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const isConnected = () =>
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected() : isConnectedState()
|
||||
const isConnected = () => {
|
||||
const i = selfIndex()
|
||||
return i !== -1
|
||||
? users[i].isConnected() && isConnectedState()
|
||||
: isConnectedState()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (selfIndex < 0) return
|
||||
setIsConnected({
|
||||
i: selfIndex,
|
||||
const i = selfIndex()
|
||||
if (i === -1) return
|
||||
setIsConnectedFor({
|
||||
i,
|
||||
isConnected: isConnectedState(),
|
||||
})
|
||||
})
|
||||
|
@ -63,39 +69,51 @@ function useSocket() {
|
|||
|
||||
const playerEvent = (event: PlayerEvent) => {
|
||||
const { type, i } = event
|
||||
// let message: string
|
||||
let message: string
|
||||
console.log("playerEvent", type)
|
||||
switch (type) {
|
||||
case "disconnect":
|
||||
setIsConnected({
|
||||
setIsConnectedFor({
|
||||
i,
|
||||
isConnected: false,
|
||||
})
|
||||
// message = "Player is disconnected."
|
||||
message = "Player is disconnected."
|
||||
break
|
||||
|
||||
case "leave":
|
||||
// message = "Player has left the lobby."
|
||||
message = "Player has left the lobby."
|
||||
break
|
||||
|
||||
case "connect":
|
||||
setIsConnected({
|
||||
setIsConnectedFor({
|
||||
i,
|
||||
isConnected: true,
|
||||
})
|
||||
socket.emit("isReady", userStates[selfIndex].isReady())
|
||||
// message = "Player has joined the lobby."
|
||||
const index = selfIndex()
|
||||
if (index !== -1) socket.emit("isReady", users[index].isReady())
|
||||
message = "Player has joined the lobby."
|
||||
break
|
||||
|
||||
default:
|
||||
// message = "Not defined yet."
|
||||
message = "Not defined yet."
|
||||
break
|
||||
}
|
||||
// toast.info(message, { toastId: message })
|
||||
console.log(message)
|
||||
if (type === "disconnect") return
|
||||
const { payload, hash } = event
|
||||
const newHash = setPlayer(payload)
|
||||
console.log(newHash, hash, !newHash, newHash === hash)
|
||||
|
||||
const { hash } = event
|
||||
const newHash = setPlayer(event.users)
|
||||
if (!newHash || newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", (body) => {
|
||||
console.log("Update is needed after ", type)
|
||||
full(body)
|
||||
})
|
||||
}
|
||||
|
||||
const gameSetting = (newSettings: GameSettings, hash: string) => {
|
||||
const newHash = setSetting(newSettings)
|
||||
if (!newHash || newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", (body) => {
|
||||
|
@ -104,18 +122,17 @@ function useSocket() {
|
|||
})
|
||||
}
|
||||
|
||||
const gameSetting = (payload: GameSettings, hash: string) => {
|
||||
const newHash = setSetting(payload)
|
||||
if (!newHash || newHash === hash) return
|
||||
console.log("hash", hash, newHash)
|
||||
socket.emit("update", (body) => {
|
||||
console.log("update")
|
||||
full(body)
|
||||
})
|
||||
const activeIndex = (i: 0 | 1) => {
|
||||
setActiveIndex(i)
|
||||
if (i === selfIndex()) {
|
||||
setMenu("moves")
|
||||
setMode(0)
|
||||
} else {
|
||||
setMenu("main")
|
||||
setMode(-1)
|
||||
}
|
||||
}
|
||||
|
||||
const activeIndex = (i: number) => setActiveIndex(i, selfIndex)
|
||||
|
||||
const disconnect = () => {
|
||||
console.log("disconnect")
|
||||
setIsConnectedState(false)
|
||||
|
@ -125,7 +142,7 @@ function useSocket() {
|
|||
socket.on("connect_error", connectError)
|
||||
socket.on("gameSetting", gameSetting)
|
||||
socket.on("playerEvent", playerEvent)
|
||||
socket.on("isReady", setIsReady)
|
||||
socket.on("isReady", setIsReadyFor)
|
||||
socket.on("gameState", gameState)
|
||||
socket.on("dispatchMove", DispatchMove)
|
||||
socket.on("activeIndex", activeIndex)
|
||||
|
@ -137,7 +154,7 @@ function useSocket() {
|
|||
socket.off("connect_error", connectError)
|
||||
socket.off("gameSetting", gameSetting)
|
||||
socket.off("playerEvent", playerEvent)
|
||||
socket.off("isReady", setIsReady)
|
||||
socket.off("isReady", setIsReadyFor)
|
||||
socket.off("gameState", gameState)
|
||||
socket.off("dispatchMove", DispatchMove)
|
||||
socket.off("activeIndex", activeIndex)
|
||||
|
@ -147,7 +164,7 @@ function useSocket() {
|
|||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!payload?.game?.id) {
|
||||
if (!gameId()) {
|
||||
socket.disconnect()
|
||||
fetch("/api/game/running", {
|
||||
method: "GET",
|
||||
|
@ -168,8 +185,7 @@ function useSocket() {
|
|||
})
|
||||
|
||||
return {
|
||||
isConnected:
|
||||
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
||||
isConnected,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Session } from "@auth/core/types"
|
||||
import type { Server as HTTPServer } from "http"
|
||||
import type { Socket as NetSocket } from "net"
|
||||
import http from "http"
|
||||
import type {
|
||||
Server as IOServer,
|
||||
Server,
|
||||
|
@ -17,33 +16,25 @@ import {
|
|||
ShipProps,
|
||||
} from "./frontend"
|
||||
|
||||
interface SocketServer extends HTTPServer {
|
||||
export interface SocketServer extends http.Server {
|
||||
io?: IOServer
|
||||
}
|
||||
|
||||
interface SocketWithIO extends NetSocket {
|
||||
server: SocketServer
|
||||
}
|
||||
|
||||
export interface ResponseWithSocket extends Response {
|
||||
socket: SocketWithIO
|
||||
}
|
||||
|
||||
export interface ServerToClientEvents {
|
||||
// noArg: () => void
|
||||
// basicEmit: (a: number, b: string, c: Buffer) => void
|
||||
// withAck: (d: string, ) => void
|
||||
gameSetting: (payload: GameSettings, hash: string) => void
|
||||
playerEvent: (event: PlayerEvent) => void
|
||||
isReady: (payload: { i: number; isReady: boolean }) => void
|
||||
isConnected: (payload: { i: number; isConnected: boolean }) => void
|
||||
isReady: (payload: { i: 0 | 1; isReady: boolean }) => void
|
||||
isConnected: (payload: { i: 0 | 1; isConnected: boolean }) => void
|
||||
"get-canvas-state": () => void
|
||||
"canvas-state-from-server": (state: string, userIndex: number) => void
|
||||
"draw-line": (props: DrawLineProps, userIndex: number) => void
|
||||
"canvas-clear": () => void
|
||||
gameState: (newState: GameState) => void
|
||||
ships: (ships: ShipProps[], index: number) => void
|
||||
activeIndex: (index: number) => void
|
||||
activeIndex: (index: 0 | 1) => void
|
||||
dispatchMove: (props: MoveDispatchProps, i: number) => void
|
||||
}
|
||||
|
||||
|
@ -75,7 +66,7 @@ interface SocketData {
|
|||
}
|
||||
user: Session["user"]
|
||||
gameId: string
|
||||
index: number
|
||||
index: 0 | 1
|
||||
}
|
||||
|
||||
export type sServer = Server<
|
|
@ -76,15 +76,16 @@ export type GameSettingKeys =
|
|||
| "allowChat"
|
||||
| "allowMarkDraw"
|
||||
|
||||
export type GameSettings = { [key in GameSettingKeys]?: boolean }
|
||||
export type GameSettings = Partial<Record<GameSettingKeys, boolean>>
|
||||
export type PlayerEvent =
|
||||
| {
|
||||
type: "connect" | "leave"
|
||||
i: number
|
||||
payload: { users: PlayerSchema[] }
|
||||
i: 0 | 1
|
||||
users: NewUsers
|
||||
hash: string
|
||||
}
|
||||
| {
|
||||
type: "disconnect"
|
||||
i: number
|
||||
i: 0 | 1
|
||||
}
|
||||
export type NewUsers = { 0: PlayerSchema | null; 1: PlayerSchema | null }
|
||||
|
|
|
@ -8,7 +8,7 @@ const pinBodySchema = z.object({
|
|||
|
||||
async function getPinFromBody(request: APIEvent["request"]) {
|
||||
try {
|
||||
const body = request.json()
|
||||
const body = await request.json()
|
||||
const { pin } = pinBodySchema.parse(body)
|
||||
return pin
|
||||
} catch {
|
||||
|
|
|
@ -59,10 +59,8 @@ async function logging(
|
|||
const xForwardedFor =
|
||||
typeof request.headers.get === "function"
|
||||
? request.headers.get("x-forwarded-for")
|
||||
: "0.0.0.0" // request.headers
|
||||
if (typeof request.headers.get !== "function")
|
||||
console.log("IncomingHttpHeaders", request.headers)
|
||||
// ("x-forwarded-for")
|
||||
: // @ts-expect-error Bad IncomingHttpHeaders Type
|
||||
request.headers["x-forwarded-for"]
|
||||
const ip = (xForwardedFor || "127.0.0.1, 192.168.178.1").split(",")
|
||||
const route = request.url
|
||||
messages.console = [ip[0].yellow, route?.green, messages.console].join(
|
||||
|
|
35
leaky-ships/src/lib/getPayloadFromProps.ts
Normal file
35
leaky-ships/src/lib/getPayloadFromProps.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import {
|
||||
activeIndex,
|
||||
allowChat,
|
||||
allowMarkDraw,
|
||||
allowSpecials,
|
||||
allowSpectators,
|
||||
gameId,
|
||||
gamePin,
|
||||
gameState,
|
||||
users,
|
||||
} from "~/hooks/useGameProps"
|
||||
|
||||
export function getPayloadFromProps() {
|
||||
return {
|
||||
game: {
|
||||
id: gameId(),
|
||||
state: gameState(),
|
||||
allowChat: allowChat(),
|
||||
allowMarkDraw: allowMarkDraw(),
|
||||
allowSpecials: allowSpecials(),
|
||||
allowSpectators: allowSpectators(),
|
||||
},
|
||||
gamePin: gamePin(),
|
||||
users: users.map((user, i) => ({
|
||||
index: i,
|
||||
id: user.id(),
|
||||
name: user.name(),
|
||||
chats: user.chats(),
|
||||
moves: user.moves(),
|
||||
ships: user.ships(),
|
||||
hits: user.hits(),
|
||||
})),
|
||||
activeIndex: activeIndex(),
|
||||
}
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
import crypto from "crypto"
|
||||
import hash from "object-hash"
|
||||
import { GamePropsSchema } from "./zodSchemas"
|
||||
|
||||
export function getPayloadwithChecksum(
|
||||
export const getPayloadwithChecksum = (
|
||||
payload: GamePropsSchema["payload"],
|
||||
): GamePropsSchema {
|
||||
const objString = JSON.stringify(payload)
|
||||
const hash = crypto.createHash("md5").update(objString).digest("hex")
|
||||
return { payload, hash }
|
||||
}
|
||||
): GamePropsSchema => ({ payload, hash: hash(payload) })
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { io } from "socket.io-client"
|
||||
import { cSocket } from "../interfaces/NextApiSocket"
|
||||
import { cSocket } from "../interfaces/ApiSocket"
|
||||
|
||||
export const socket: cSocket = io({
|
||||
path: "/api/ws",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { createSignal } from "solid-js"
|
||||
import { count } from "~/components/Gamefield/Gamefield"
|
||||
import type {
|
||||
Hit,
|
||||
|
@ -10,7 +11,7 @@ import type {
|
|||
TargetList,
|
||||
TargetPreview,
|
||||
} from "../../interfaces/frontend"
|
||||
import { Orientation } from "../zodSchemas"
|
||||
import { ChatSchema, MoveSchema, Orientation } from "../zodSchemas"
|
||||
|
||||
export function borderCN(count: number, x: number, y: number) {
|
||||
if (x === 0) return "left"
|
||||
|
@ -129,6 +130,34 @@ export const initlialMouseCursor = {
|
|||
x: 0,
|
||||
y: 0,
|
||||
}
|
||||
export function initialUser() {
|
||||
const [isReady, setIsReady] = createSignal(false)
|
||||
const [isConnected, setIsConnected] = createSignal(false)
|
||||
const [id, setId] = createSignal<string>("")
|
||||
const [name, setName] = createSignal<string>("")
|
||||
const [chats, setChats] = createSignal<ChatSchema[]>([])
|
||||
const [moves, setMoves] = createSignal<MoveSchema[]>([])
|
||||
const [ships, setShips] = createSignal<ShipProps[]>([])
|
||||
const [hits, setHits] = createSignal<Hit[]>([])
|
||||
return {
|
||||
isReady,
|
||||
setIsReady,
|
||||
isConnected,
|
||||
setIsConnected,
|
||||
id,
|
||||
setId,
|
||||
name,
|
||||
setName,
|
||||
chats,
|
||||
setChats,
|
||||
moves,
|
||||
setMoves,
|
||||
ships,
|
||||
setShips,
|
||||
hits,
|
||||
setHits,
|
||||
}
|
||||
}
|
||||
|
||||
export const shipProps = (
|
||||
ships: ShipProps[],
|
||||
|
|
|
@ -34,44 +34,52 @@ export const movesSchema = createSelectSchema(moves)
|
|||
export const chatsSchema = createSelectSchema(chats)
|
||||
export const user_gamesSchema = createSelectSchema(user_games)
|
||||
|
||||
export const ChatSchema = z.object({
|
||||
id: z.string(),
|
||||
event: z.string().nullable(),
|
||||
message: z.string().nullable(),
|
||||
createdAt: z.coerce.date(),
|
||||
})
|
||||
|
||||
export type ChatSchema = z.infer<typeof ChatSchema>
|
||||
|
||||
export const MoveSchema = z.object({
|
||||
index: z.number(),
|
||||
type: MoveType,
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
orientation: Orientation,
|
||||
})
|
||||
|
||||
export type MoveSchema = z.infer<typeof MoveSchema>
|
||||
|
||||
export const ShipShema = z.object({
|
||||
size: z.number(),
|
||||
variant: z.number(),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
orientation: Orientation,
|
||||
})
|
||||
|
||||
export type ShipShema = z.infer<typeof ShipShema>
|
||||
|
||||
export const HitSchema = z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
hit: z.boolean(),
|
||||
})
|
||||
|
||||
export type HitSchema = z.infer<typeof HitSchema>
|
||||
|
||||
export const PlayerSchema = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string().nullable(),
|
||||
name: z.string(),
|
||||
index: z.number(),
|
||||
chats: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
event: z.string().nullable(),
|
||||
message: z.string().nullable(),
|
||||
createdAt: z.coerce.date(),
|
||||
})
|
||||
.array(),
|
||||
moves: z
|
||||
.object({
|
||||
index: z.number(),
|
||||
type: MoveType,
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
orientation: Orientation,
|
||||
})
|
||||
.array(),
|
||||
ships: z
|
||||
.object({
|
||||
size: z.number(),
|
||||
variant: z.number(),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
orientation: Orientation,
|
||||
})
|
||||
.array(),
|
||||
hits: z
|
||||
.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
hit: z.boolean(),
|
||||
})
|
||||
.array(),
|
||||
chats: ChatSchema.array(),
|
||||
moves: MoveSchema.array(),
|
||||
ships: ShipShema.array(),
|
||||
hits: HitSchema.array(),
|
||||
})
|
||||
.nullable()
|
||||
|
||||
|
@ -89,8 +97,8 @@ export const CreateSchema = z.object({
|
|||
})
|
||||
.nullable(),
|
||||
gamePin: z.string().nullable(),
|
||||
users: PlayerSchema.array(),
|
||||
activeIndex: z.number().optional(),
|
||||
users: z.object({ 0: PlayerSchema, 1: PlayerSchema }),
|
||||
activeIndex: z.literal(0).or(z.literal(1)),
|
||||
})
|
||||
|
||||
export const GamePropsSchema = z.object({
|
||||
|
@ -102,5 +110,6 @@ export const optionalGamePropsSchema = z.object({
|
|||
hash: z.string().nullable(),
|
||||
})
|
||||
|
||||
export type CreateSchema = z.infer<typeof CreateSchema>
|
||||
export type GamePropsSchema = z.infer<typeof GamePropsSchema>
|
||||
export type optionalGamePropsSchema = z.infer<typeof optionalGamePropsSchema>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
Scripts,
|
||||
Title,
|
||||
} from "solid-start"
|
||||
import { SessionProvider } from "./hooks/useSession"
|
||||
import "./styles/App.scss"
|
||||
import "./styles/globals.scss"
|
||||
import "./styles/grid.scss"
|
||||
|
@ -41,9 +42,11 @@ export default function Root() {
|
|||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<Suspense fallback={<div>Loading</div>}>
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
<FileRoutes />
|
||||
</Routes>
|
||||
<SessionProvider>
|
||||
<Routes>
|
||||
<FileRoutes />
|
||||
</Routes>
|
||||
</SessionProvider>
|
||||
</ErrorBoundary>
|
||||
</Suspense>
|
||||
<Scripts />
|
||||
|
|
5
leaky-ships/src/routes/[...404].tsx
Normal file
5
leaky-ships/src/routes/[...404].tsx
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { redirect } from "solid-start"
|
||||
|
||||
export function GET() {
|
||||
return redirect("/")
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { getSession } from "@auth/solid-start"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { APIEvent } from "solid-start"
|
||||
import db from "~/drizzle"
|
||||
|
@ -30,17 +31,35 @@ export async function POST({ request }: APIEvent) {
|
|||
message: "Running game already exists.",
|
||||
})
|
||||
} else {
|
||||
const gameId = (await db.insert(games).values({}).returning())[0].id
|
||||
const gameId = (
|
||||
await db
|
||||
.insert(games)
|
||||
.values({
|
||||
id: createId(),
|
||||
})
|
||||
.returning()
|
||||
)[0].id
|
||||
const user_Game = (
|
||||
await db
|
||||
.insert(user_games)
|
||||
.values({ gameId, userId: id, index: 0 })
|
||||
.values({
|
||||
id: createId(),
|
||||
gameId,
|
||||
userId: id,
|
||||
index: 0,
|
||||
})
|
||||
.returning()
|
||||
)[0]
|
||||
await db.insert(gamepins).values({ gameId, pin })
|
||||
await db
|
||||
.insert(chats)
|
||||
.values({ user_game_id: user_Game.id, event: "created" })
|
||||
await db.insert(gamepins).values({
|
||||
id: createId(),
|
||||
gameId,
|
||||
pin,
|
||||
})
|
||||
await db.insert(chats).values({
|
||||
id: createId(),
|
||||
user_game_id: user_Game.id,
|
||||
event: "created",
|
||||
})
|
||||
game = await db.query.games.findFirst({
|
||||
where: eq(games.id, gameId),
|
||||
...gameSelects,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { getSession } from "@auth/solid-start"
|
||||
import { and, eq, exists, inArray, ne } from "drizzle-orm"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
import { and, eq } from "drizzle-orm"
|
||||
import { APIEvent } from "solid-start"
|
||||
import db from "~/drizzle"
|
||||
import { user_games, users } from "~/drizzle/schemas/Tables"
|
||||
import { user_games } from "~/drizzle/schemas/Tables"
|
||||
import { rejectionErrors } from "~/lib/backend/errors"
|
||||
import getPinFromBody from "~/lib/backend/getPinFromBody"
|
||||
import logging from "~/lib/backend/logging"
|
||||
|
@ -34,28 +35,15 @@ export async function POST({ request }: APIEvent) {
|
|||
})
|
||||
}
|
||||
|
||||
let game = await db.query.games.findFirst({
|
||||
where: (game) =>
|
||||
and(
|
||||
ne(game.state, "ended"),
|
||||
exists(
|
||||
db
|
||||
.select()
|
||||
.from(user_games)
|
||||
.where(
|
||||
inArray(
|
||||
user_games.userId,
|
||||
db
|
||||
.select({ data: users.id })
|
||||
.from(users)
|
||||
.where(eq(users.id, id)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...gameSelects,
|
||||
const user_Game = await db.query.user_games.findFirst({
|
||||
where: and(
|
||||
eq(user_games.userId, id),
|
||||
eq(user_games.gameId, gamePin.gameId),
|
||||
),
|
||||
with: { game: gameSelects },
|
||||
})
|
||||
if (!game) {
|
||||
|
||||
if (user_Game) {
|
||||
return sendResponse(request, {
|
||||
message: "Spieler ist bereits in Spiel!",
|
||||
redirectUrl: "/api/game/running",
|
||||
|
@ -63,18 +51,17 @@ export async function POST({ request }: APIEvent) {
|
|||
})
|
||||
}
|
||||
|
||||
const gameId = (
|
||||
await db
|
||||
.insert(user_games)
|
||||
.values({
|
||||
gameId: game.id,
|
||||
userId: id,
|
||||
index: 1,
|
||||
})
|
||||
.returning()
|
||||
)[0].gameId
|
||||
await db
|
||||
.insert(user_games)
|
||||
.values({
|
||||
id: createId(),
|
||||
gameId: gamePin.gameId,
|
||||
userId: id,
|
||||
index: 1,
|
||||
})
|
||||
.returning()
|
||||
|
||||
game = await getGameById(gameId)
|
||||
const game = await getGameById(gamePin.gameId)
|
||||
|
||||
if (!game) return
|
||||
|
||||
|
@ -87,11 +74,8 @@ export async function POST({ request }: APIEvent) {
|
|||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
await logging(
|
||||
"HERE".red + err.code + err.meta + err.message,
|
||||
["error"],
|
||||
request,
|
||||
)
|
||||
await logging(err.code + err.meta + err.message, ["error"], request)
|
||||
console.log(err)
|
||||
throw sendError(request, rejectionErrors.gameNotFound)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { getSession } from "@auth/solid-start"
|
|||
import { and, eq, exists, ne } from "drizzle-orm"
|
||||
import { APIEvent } from "solid-start/api"
|
||||
import db from "~/drizzle"
|
||||
import { games, user_games, users } from "~/drizzle/schemas/Tables"
|
||||
import { games, user_games } from "~/drizzle/schemas/Tables"
|
||||
import { rejectionErrors } from "~/lib/backend/errors"
|
||||
import sendResponse from "~/lib/backend/sendResponse"
|
||||
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
||||
|
@ -87,10 +87,7 @@ export const getRunningGameToUser = async (userId: string) => {
|
|||
and(
|
||||
ne(game.state, "ended"),
|
||||
exists(
|
||||
db
|
||||
.select()
|
||||
.from(user_games)
|
||||
.where(exists(db.select().from(users).where(eq(users.id, userId)))),
|
||||
db.select().from(user_games).where(eq(user_games.userId, userId)),
|
||||
),
|
||||
),
|
||||
...gameSelects,
|
||||
|
@ -100,23 +97,41 @@ export const getRunningGameToUser = async (userId: string) => {
|
|||
export function composeBody(
|
||||
gameDB: NonNullable<Awaited<ReturnType<typeof getRunningGameToUser>>>,
|
||||
): GamePropsSchema {
|
||||
const { gamePin, ...game } = gameDB
|
||||
const users = gameDB.users
|
||||
.map(({ user, ...props }) => ({
|
||||
...props,
|
||||
...user,
|
||||
}))
|
||||
.sort((user1, user2) => user1.index - user2.index)
|
||||
let activeIndex = undefined
|
||||
const { gamePin, users, ...game } = gameDB
|
||||
const mappedUsers = users.map(({ user, ...props }) => ({
|
||||
...props,
|
||||
...user,
|
||||
}))
|
||||
const composedUsers = {
|
||||
0: mappedUsers.find((e) => e.index === 0) ?? {
|
||||
index: 0,
|
||||
id: "",
|
||||
name: "",
|
||||
chats: [],
|
||||
moves: [],
|
||||
ships: [],
|
||||
hits: [],
|
||||
},
|
||||
1: mappedUsers.find((e) => e.index === 1) ?? {
|
||||
index: 1,
|
||||
id: "",
|
||||
name: "",
|
||||
chats: [],
|
||||
moves: [],
|
||||
ships: [],
|
||||
hits: [],
|
||||
},
|
||||
}
|
||||
let activeIndex: 0 | 1 = 0
|
||||
if (game.state === "running") {
|
||||
const l1 = game.users[0].moves.length
|
||||
const l2 = game.users[1].moves.length
|
||||
const l1 = users[0]?.moves.length ?? 0
|
||||
const l2 = users[1]?.moves.length ?? 0
|
||||
activeIndex = l1 > l2 ? 1 : 0
|
||||
}
|
||||
const payload = {
|
||||
game: game,
|
||||
gamePin: gamePin?.pin ?? null,
|
||||
users,
|
||||
users: composedUsers,
|
||||
activeIndex,
|
||||
}
|
||||
return getPayloadwithChecksum(payload)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { getSession } from "@auth/solid-start"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
import colors from "colors"
|
||||
import { and, eq } from "drizzle-orm"
|
||||
import status from "http-status"
|
||||
|
@ -6,7 +7,7 @@ import { Server } from "socket.io"
|
|||
import { APIEvent } from "solid-start"
|
||||
import db from "~/drizzle"
|
||||
import { games, moves, ships, user_games } from "~/drizzle/schemas/Tables"
|
||||
import { ResponseWithSocket, sServer } from "~/interfaces/NextApiSocket"
|
||||
import { SocketServer, sServer } from "~/interfaces/ApiSocket"
|
||||
import logging from "~/lib/backend/logging"
|
||||
import { GamePropsSchema } from "~/lib/zodSchemas"
|
||||
import { authOptions } from "~/server/auth"
|
||||
|
@ -19,27 +20,33 @@ import {
|
|||
|
||||
colors.enable()
|
||||
|
||||
const res = new Response() as ResponseWithSocket
|
||||
|
||||
export async function GET({ request }: APIEvent) {
|
||||
if (res.socket.server.io) {
|
||||
export async function GET({
|
||||
request,
|
||||
httpServer,
|
||||
}: APIEvent & { httpServer: SocketServer }) {
|
||||
if (httpServer.io) {
|
||||
logging("Socket is already running " + request.url, ["infoCyan"], request)
|
||||
} else {
|
||||
logging("Socket is initializing " + request.url, ["infoCyan"], request)
|
||||
const io: sServer = new Server(res.socket.server, {
|
||||
const io: sServer = new Server(httpServer, {
|
||||
path: "/api/ws",
|
||||
cors: {
|
||||
origin: "https://leaky-ships.mal-noh.de",
|
||||
},
|
||||
})
|
||||
|
||||
res.socket.server.io = io
|
||||
httpServer.io = io
|
||||
|
||||
// io.use(authenticate)
|
||||
io.use(async (socket, next) => {
|
||||
try {
|
||||
// @ts-expect-error TODO add correct server
|
||||
const session = await getSession(socket.request, authOptions)
|
||||
const url = process.env.AUTH_URL! + socket.request.url
|
||||
const session = await getSession(
|
||||
new Request(url, {
|
||||
headers: socket.request.headers as Record<string, string>,
|
||||
}),
|
||||
authOptions,
|
||||
)
|
||||
if (!session) return next(new Error(status["401"]))
|
||||
socket.data.user = session.user
|
||||
|
||||
|
@ -55,18 +62,15 @@ export async function GET({ request }: APIEvent) {
|
|||
}
|
||||
|
||||
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"]))
|
||||
const index = payload.users[0]?.id === socket.data.user?.id ? 0 : 1
|
||||
if (index !== 0 && index !== 1) 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 },
|
||||
users: payload.users,
|
||||
hash,
|
||||
})
|
||||
|
||||
|
@ -89,14 +93,16 @@ export async function GET({ request }: APIEvent) {
|
|||
socket.on("update", async (cb) => {
|
||||
const game = await getGameById(socket.data.gameId ?? "")
|
||||
if (!game) return
|
||||
if (socket.data.index === 1 && game.users.length === 1)
|
||||
socket.data.index = 0
|
||||
const body = composeBody(game)
|
||||
cb(body)
|
||||
})
|
||||
|
||||
socket.on("gameSetting", async (payload, cb) => {
|
||||
socket.on("gameSetting", async (newSettings, cb) => {
|
||||
const game = await db
|
||||
.update(games)
|
||||
.set(payload)
|
||||
.set(newSettings)
|
||||
.where(eq(games.id, socket.data.gameId))
|
||||
.returning()
|
||||
.then((updatedGame) =>
|
||||
|
@ -109,41 +115,41 @@ export async function GET({ request }: APIEvent) {
|
|||
const { hash } = composeBody(game)
|
||||
if (!hash) return
|
||||
cb(hash)
|
||||
socket.to(game?.id).emit("gameSetting", payload, hash)
|
||||
socket.to(game?.id).emit("gameSetting", newSettings, 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 db
|
||||
.delete(user_games)
|
||||
.where(
|
||||
and(
|
||||
eq(user_games.gameId, socket.data.gameId),
|
||||
eq(user_games.userId, socket.data.user?.id ?? ""),
|
||||
),
|
||||
)
|
||||
.returning()
|
||||
const user_Game = (
|
||||
await db
|
||||
.delete(user_games)
|
||||
.where(
|
||||
and(
|
||||
eq(user_games.gameId, socket.data.gameId),
|
||||
eq(user_games.userId, socket.data.user?.id ?? ""),
|
||||
),
|
||||
)
|
||||
.returning()
|
||||
)[0]
|
||||
if (!user_Game) return
|
||||
const enemy = await db.query.user_games.findFirst({
|
||||
where: eq(user_games.gameId, socket.data.gameId),
|
||||
})
|
||||
let body: GamePropsSchema
|
||||
if (user_Game[0].index === 1 && enemy) {
|
||||
if (user_Game.index === 0 && enemy) {
|
||||
const game = await db
|
||||
.update(user_games)
|
||||
.set({
|
||||
index: 1,
|
||||
index: 0,
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(user_games.gameId, socket.data.gameId),
|
||||
eq(user_games.index, 2),
|
||||
eq(user_games.index, 1),
|
||||
),
|
||||
)
|
||||
.returning()
|
||||
|
||||
.then((user_Game) =>
|
||||
db.query.games.findFirst({
|
||||
where: eq(games.id, user_Game[0].gameId),
|
||||
|
@ -161,17 +167,16 @@ export async function GET({ request }: APIEvent) {
|
|||
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 },
|
||||
users: payload.users,
|
||||
hash,
|
||||
})
|
||||
socket.data.gameId = ""
|
||||
cb(true)
|
||||
|
||||
if (!payload.users.length) {
|
||||
if (!payload.users[0] && !payload.users[1]) {
|
||||
await db.delete(games).where(eq(games.id, socket.data.gameId))
|
||||
}
|
||||
})
|
||||
|
@ -239,11 +244,13 @@ export async function GET({ request }: APIEvent) {
|
|||
})
|
||||
|
||||
if (!user_Game) return
|
||||
await db
|
||||
.insert(ships)
|
||||
.values(
|
||||
shipsData.map((ship) => ({ ...ship, user_game_id: user_Game.id })),
|
||||
)
|
||||
await db.insert(ships).values(
|
||||
shipsData.map((ship) => ({
|
||||
id: createId(),
|
||||
user_game_id: user_Game.id,
|
||||
...ship,
|
||||
})),
|
||||
)
|
||||
|
||||
socket
|
||||
.to(socket.data.gameId)
|
||||
|
@ -270,7 +277,7 @@ export async function GET({ request }: APIEvent) {
|
|||
if (!user_Game?.game) return
|
||||
await db
|
||||
.insert(moves)
|
||||
.values({ ...props, user_game_id: user_Game.id })
|
||||
.values({ ...props, id: createId(), user_game_id: user_Game.id })
|
||||
.returning()
|
||||
|
||||
const game = user_Game.game
|
||||
|
@ -286,7 +293,7 @@ export async function GET({ request }: APIEvent) {
|
|||
["debug"],
|
||||
socket.request,
|
||||
)
|
||||
if (socket.data.index === undefined || !socket.data.gameId) return
|
||||
if (!socket.data.gameId) return
|
||||
socket.to(socket.data.gameId).emit("playerEvent", {
|
||||
type: "disconnect",
|
||||
i: socket.data.index,
|
||||
|
@ -298,5 +305,5 @@ export async function GET({ request }: APIEvent) {
|
|||
})
|
||||
})
|
||||
}
|
||||
return res
|
||||
return new Response()
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
// import { toast } from "react-toastify"
|
||||
// import { createEffect } from "solid-js"
|
||||
// import { useNavigate } from "solid-start"
|
||||
// import { useGameProps } from "~/hooks/useGameProps"
|
||||
// import { useSession } from "~/hooks/useSession"
|
||||
|
||||
export default function Game() {
|
||||
// const { payload } = useGameProps()
|
||||
// const navigate = useNavigate()
|
||||
// const session = useSession()
|
||||
|
||||
// createEffect(() => {
|
||||
// const gameId = payload?.game?.id
|
||||
// const path = gameId ? "/game" : "/start"
|
||||
// toast.promise(navigate(path), {
|
||||
// pending: {
|
||||
// render: "Wird weitergeleitet...",
|
||||
// toastId: "pageLoad",
|
||||
// },
|
||||
// success: {
|
||||
// render: gameId
|
||||
// ? "Spiel gefunden!"
|
||||
// : session?.user.id
|
||||
// ? "Kein laufendes Spiel."
|
||||
// : "Kein laufendes Spiel. Bitte anmelden.",
|
||||
// toastId: "pageLoad",
|
||||
// theme: session?.user.id ? "dark" : undefined,
|
||||
// type: gameId ? "success" : "info",
|
||||
// },
|
||||
// error: {
|
||||
// render: "Es ist ein Fehler aufgetreten 🤯",
|
||||
// type: "error",
|
||||
// toastId: "pageLoad",
|
||||
// theme: "colored",
|
||||
// },
|
||||
// })
|
||||
// })
|
||||
|
||||
return (
|
||||
<div class="h-full bg-theme">
|
||||
<div class="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly" />
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -3,7 +3,7 @@ import BurgerMenu from "~/components/BurgerMenu"
|
|||
import Logo from "~/components/Logo"
|
||||
|
||||
export default function Home() {
|
||||
const navigate = useNavigate()
|
||||
const navigator = useNavigate()
|
||||
|
||||
return (
|
||||
<div class="h-full bg-theme">
|
||||
|
@ -18,7 +18,7 @@ export default function Home() {
|
|||
class="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
navigate("/start")
|
||||
navigator("/start")
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// import Head from "next/head"
|
||||
import classNames from "classnames"
|
||||
import { createSignal } from "solid-js"
|
||||
import { Show, createSignal } from "solid-js"
|
||||
import BurgerMenu from "~/components/BurgerMenu"
|
||||
import LobbyFrame from "~/components/Lobby/LobbyFrame"
|
||||
import Settings from "~/components/Lobby/SettingsFrame/Settings"
|
||||
|
@ -26,16 +26,16 @@ export default function Lobby() {
|
|||
<div
|
||||
class={classNames(
|
||||
"mx-auto flex h-full max-w-screen-2xl flex-col items-center justify-evenly",
|
||||
{ "blur-sm": settings },
|
||||
{ "blur-sm": settings() },
|
||||
)}
|
||||
>
|
||||
<Logo small={true} />
|
||||
<LobbyFrame openSettings={() => setSettings(true)} />
|
||||
</div>
|
||||
<BurgerMenu blur={settings()} />
|
||||
{settings() ? (
|
||||
<Show when={settings()}>
|
||||
<Settings closeSettings={() => setSettings(false)} />
|
||||
) : null}
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { signIn } from "@auth/solid-start/client"
|
||||
import { faLeftLong } from "@fortawesome/pro-solid-svg-icons"
|
||||
import classNames from "classnames"
|
||||
import { createEffect, createSignal } from "solid-js"
|
||||
import { Show, createEffect, createSignal } from "solid-js"
|
||||
import { useNavigate, useSearchParams } from "solid-start"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
|
@ -39,19 +39,20 @@ const errors: Record<SignInErrorTypes, string> = {
|
|||
|
||||
function Login() {
|
||||
const [email, setEmail] = createSignal("")
|
||||
const { latest } = useSession()
|
||||
const navigate = useNavigate()
|
||||
const { session } = useSession()
|
||||
const navigator = useNavigate()
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const errorType = searchParams["error"] as SignInErrorTypes
|
||||
|
||||
createEffect(() => {
|
||||
if (!errorType) return
|
||||
console.error(errors[errorType] ?? errors.default)
|
||||
// toast.error(errors[errorType] ?? errors.default, { theme: "colored" })
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (latest?.user?.id) navigate("/")
|
||||
if (session()) navigator("/signout")
|
||||
})
|
||||
|
||||
const login = (provider: "email" | "azure-ad") =>
|
||||
|
@ -79,7 +80,14 @@ function Login() {
|
|||
</div>
|
||||
{errorType && <hr class="mb-8 border-gray-400" />}
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col">
|
||||
<form
|
||||
class="flex flex-col"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
if (!email()) return
|
||||
login("email")
|
||||
}}
|
||||
>
|
||||
<label for="email" class="mx-2 text-lg">
|
||||
Email
|
||||
</label>
|
||||
|
@ -95,12 +103,11 @@ function Login() {
|
|||
<button
|
||||
id="email-submit"
|
||||
type="submit"
|
||||
onClick={() => login("email")}
|
||||
class="my-1 rounded-lg bg-blue-500 bg-opacity-75 px-10 py-3 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:bg-blue-600"
|
||||
>
|
||||
Sign in with Email
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<hr class="w-full" />
|
||||
|
@ -137,21 +144,19 @@ function Login() {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{errorType ? (
|
||||
<>
|
||||
<hr class="mt-8 border-gray-400" />
|
||||
<div class="flex flex-col items-center">
|
||||
<button
|
||||
id="back"
|
||||
onClick={() => navigate("/")}
|
||||
class="mt-10 rounded-lg border-2 border-gray-400 bg-gray-500 bg-opacity-75 px-16 py-2 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:border-blue-600"
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
<span class="mx-4 font-bold">Return</span>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<Show when={errorType}>
|
||||
<hr class="mt-8 border-gray-400" />
|
||||
<div class="flex flex-col items-center">
|
||||
<button
|
||||
id="back"
|
||||
onClick={() => navigator("/")}
|
||||
class="mt-10 rounded-lg border-2 border-gray-400 bg-gray-500 bg-opacity-75 px-16 py-2 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:border-blue-600"
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
<span class="mx-4 font-bold">Return</span>
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { signOut } from "@auth/solid-start/client"
|
||||
import { faLeftLong } from "@fortawesome/pro-solid-svg-icons"
|
||||
import { createEffect } from "solid-js"
|
||||
import { useNavigate } from "solid-start"
|
||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
|
||||
function Logout() {
|
||||
const { state } = useSession()
|
||||
|
||||
const { session } = useSession()
|
||||
const navigator = useNavigate()
|
||||
|
||||
createEffect(() => {
|
||||
if (state === "ready") navigator("/signin") // TODO
|
||||
if (!session()) navigator("/signin")
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -36,6 +37,17 @@ function Logout() {
|
|||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
<hr class="mt-8 border-gray-400" />
|
||||
<div class="flex flex-col items-center">
|
||||
<button
|
||||
id="back"
|
||||
onClick={() => navigator("/")}
|
||||
class="mt-10 rounded-lg border-2 border-gray-400 bg-gray-500 bg-opacity-75 px-16 py-2 text-white shadow-inner drop-shadow-md backdrop-blur-md transition-colors duration-300 hover:border-blue-600"
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
<span class="mx-4 font-bold">Return</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@ import BurgerMenu from "~/components/BurgerMenu"
|
|||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||
import Logo from "~/components/Logo"
|
||||
import OptionButton from "~/components/OptionButton"
|
||||
import { useGameProps } from "~/hooks/useGameProps"
|
||||
import { full } from "~/hooks/useGameProps"
|
||||
import { useSession } from "~/hooks/useSession"
|
||||
|
||||
export function isAuthenticated(res: Response) {
|
||||
|
@ -44,11 +44,11 @@ export function isAuthenticated(res: Response) {
|
|||
// }
|
||||
|
||||
export default function Start() {
|
||||
const [otp] = createSignal("")
|
||||
const gameProps = useGameProps()
|
||||
const [otp, setOtp] = createSignal("")
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const session = useSession()
|
||||
const navigator = useNavigate()
|
||||
const { session } = useSession()
|
||||
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const query = () => {
|
||||
|
@ -82,7 +82,8 @@ export default function Start() {
|
|||
// hideProgressBar: true,
|
||||
// closeButton: false,
|
||||
// })
|
||||
const res = await gameRequestPromise.catch(() => {
|
||||
const res = await gameRequestPromise.catch((err) => {
|
||||
console.log(err)
|
||||
// toast.update(toastId, {
|
||||
// render: "Es ist ein Fehler aufgetreten bei der Anfrage 🤯",
|
||||
// type: "error",
|
||||
|
@ -94,13 +95,13 @@ export default function Start() {
|
|||
// })
|
||||
})
|
||||
if (!res) return
|
||||
gameProps.full(res)
|
||||
full(res)
|
||||
|
||||
// toast.update(toastId, {
|
||||
// render: "Weiterleitung",
|
||||
// })
|
||||
|
||||
navigate("/lobby")
|
||||
navigator("/lobby")
|
||||
// .then(() =>
|
||||
// toast.update(toastId, {
|
||||
// render: "Raum begetreten 👌",
|
||||
|
@ -141,19 +142,19 @@ export default function Start() {
|
|||
class="-mt-2 h-14 w-20 self-start rounded-xl border-b-4 border-shield-gray bg-voidDark text-2xl text-grayish duration-100 active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-5xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
navigate("/")
|
||||
navigator("/")
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faLeftLong} />
|
||||
</button>
|
||||
{!session.latest?.user?.id && (
|
||||
{!session()?.user?.id && (
|
||||
<button
|
||||
id="login"
|
||||
class="-mt-2 h-14 w-20 self-start rounded-xl border-b-4 border-orange-500 bg-yellow-500 text-2xl active:border-b-0 active:border-t-4 sm:-mt-6 sm:w-40 sm:px-2 sm:text-4xl"
|
||||
onClick={() =>
|
||||
setTimeout(() => {
|
||||
navigate("/signin")
|
||||
navigator("/signin")
|
||||
}, 200)
|
||||
}
|
||||
>
|
||||
|
@ -166,12 +167,12 @@ export default function Start() {
|
|||
text="Raum erstellen"
|
||||
callback={gameFetch}
|
||||
icon={faPlus}
|
||||
disabled={!session.latest}
|
||||
disabled={!session()}
|
||||
/>
|
||||
<OptionButton
|
||||
text="Raum beitreten"
|
||||
callback={() =>
|
||||
navigate(
|
||||
navigator(
|
||||
location.pathname.concat(
|
||||
"?",
|
||||
new URLSearchParams({ q: "join" }).toString(),
|
||||
|
@ -179,32 +180,30 @@ export default function Start() {
|
|||
)
|
||||
}
|
||||
icon={faUserPlus}
|
||||
disabled={!session.latest}
|
||||
nodeWhen={query().join && !!session.latest}
|
||||
disabled={!session()}
|
||||
nodeWhen={query().join && !!session()}
|
||||
node={
|
||||
<>
|
||||
{
|
||||
// <OtpInput
|
||||
// shouldAutoFocus
|
||||
// containerStyle={{ color: "initial" }}
|
||||
// value={otp}
|
||||
// onChange={setOtp}
|
||||
// numInputs={4}
|
||||
// inputType="number"
|
||||
// inputStyle="inputStyle"
|
||||
// placeholder="0000"
|
||||
// renderSeparator={<span>-</span>}
|
||||
// renderInput={(props) => <input {...props} />}
|
||||
// />
|
||||
}
|
||||
</>
|
||||
<input value={otp()} onInput={(e) => setOtp(e.target.value)} />
|
||||
|
||||
// <OtpInput
|
||||
// shouldAutoFocus
|
||||
// containerStyle={{ color: "initial" }}
|
||||
// value={otp}
|
||||
// onChange={setOtp}
|
||||
// numInputs={4}
|
||||
// inputType="number"
|
||||
// inputStyle="inputStyle"
|
||||
// placeholder="0000"
|
||||
// renderSeparator={<span>-</span>}
|
||||
// renderInput={(props) => <input {...props} />}
|
||||
// />
|
||||
}
|
||||
/>
|
||||
<OptionButton
|
||||
text="Zuschauen"
|
||||
icon={faEye}
|
||||
callback={() =>
|
||||
navigate(
|
||||
navigator(
|
||||
location.pathname +
|
||||
"?" +
|
||||
new URLSearchParams({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue