user states improvements and fixes
This commit is contained in:
parent
252f6f6028
commit
efcb61b1ed
19 changed files with 309 additions and 273 deletions
|
@ -30,6 +30,7 @@
|
||||||
"drizzle-orm": "^0.28.6",
|
"drizzle-orm": "^0.28.6",
|
||||||
"drizzle-zod": "^0.5.1",
|
"drizzle-zod": "^0.5.1",
|
||||||
"http-status": "^1.7.0",
|
"http-status": "^1.7.0",
|
||||||
|
"json-stable-stringify": "^1.0.2",
|
||||||
"nodemailer": "^6.9.5",
|
"nodemailer": "^6.9.5",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"postgres": "^3.3.5",
|
"postgres": "^3.3.5",
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.37.1",
|
"@playwright/test": "^1.37.1",
|
||||||
"@total-typescript/ts-reset": "^0.4.2",
|
"@total-typescript/ts-reset": "^0.4.2",
|
||||||
|
"@types/json-stable-stringify": "^1.0.34",
|
||||||
"@types/node": "^20.5.9",
|
"@types/node": "^20.5.9",
|
||||||
"@types/nodemailer": "^6.4.9",
|
"@types/nodemailer": "^6.4.9",
|
||||||
"@types/object-hash": "^3.0.4",
|
"@types/object-hash": "^3.0.4",
|
||||||
|
|
20
leaky-ships/pnpm-lock.yaml
generated
20
leaky-ships/pnpm-lock.yaml
generated
|
@ -66,6 +66,9 @@ dependencies:
|
||||||
http-status:
|
http-status:
|
||||||
specifier: ^1.7.0
|
specifier: ^1.7.0
|
||||||
version: 1.7.0
|
version: 1.7.0
|
||||||
|
json-stable-stringify:
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.0.2
|
||||||
nodemailer:
|
nodemailer:
|
||||||
specifier: ^6.9.5
|
specifier: ^6.9.5
|
||||||
version: 6.9.5
|
version: 6.9.5
|
||||||
|
@ -101,6 +104,9 @@ devDependencies:
|
||||||
'@total-typescript/ts-reset':
|
'@total-typescript/ts-reset':
|
||||||
specifier: ^0.4.2
|
specifier: ^0.4.2
|
||||||
version: 0.4.2
|
version: 0.4.2
|
||||||
|
'@types/json-stable-stringify':
|
||||||
|
specifier: ^1.0.34
|
||||||
|
version: 1.0.34
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.5.9
|
specifier: ^20.5.9
|
||||||
version: 20.5.9
|
version: 20.5.9
|
||||||
|
@ -2050,6 +2056,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/json-stable-stringify@1.0.34:
|
||||||
|
resolution: {integrity: sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/ms@0.7.31:
|
/@types/ms@0.7.31:
|
||||||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||||
|
|
||||||
|
@ -4008,6 +4018,12 @@ packages:
|
||||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/json-stable-stringify@1.0.2:
|
||||||
|
resolution: {integrity: sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==}
|
||||||
|
dependencies:
|
||||||
|
jsonify: 0.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/json5@2.2.3:
|
/json5@2.2.3:
|
||||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -4020,6 +4036,10 @@ packages:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
|
|
||||||
|
/jsonify@0.0.1:
|
||||||
|
resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsx-ast-utils@3.3.5:
|
/jsx-ast-utils@3.3.5:
|
||||||
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
|
|
|
@ -31,18 +31,22 @@ type TilesType = {
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function BorderTiles() {
|
function settingTarget(
|
||||||
const { selfIndex, ships } = useSession()
|
isGameTile: boolean,
|
||||||
|
x: number,
|
||||||
const settingTarget = (isGameTile: boolean, x: number, y: number) => {
|
y: number,
|
||||||
const sIndex = selfIndex()
|
index: 0 | 1,
|
||||||
if (sIndex === -1) return
|
{
|
||||||
|
activeIndex,
|
||||||
|
ships,
|
||||||
|
}: Pick<ReturnType<typeof useSession>, "activeIndex" | "ships">,
|
||||||
|
) {
|
||||||
if (gameProps.gameState === "running") {
|
if (gameProps.gameState === "running") {
|
||||||
const list = targetList(targetPreview(), gameProps.mode)
|
const list = targetList(targetPreview(), gameProps.mode)
|
||||||
if (
|
if (
|
||||||
!isGameTile ||
|
!isGameTile ||
|
||||||
!list.filter(
|
!list.filter(
|
||||||
({ x, y }) => !isAlreadyHit(x, y, compiledHits(sIndex === 0 ? 1 : 0)),
|
({ x, y }) => !isAlreadyHit(x, y, compiledHits(activeIndex())),
|
||||||
).length
|
).length
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
@ -64,10 +68,67 @@ function BorderTiles() {
|
||||||
setMouseCursor((e) => ({ ...e, shouldShow: false }))
|
setMouseCursor((e) => ({ ...e, shouldShow: false }))
|
||||||
setShips(
|
setShips(
|
||||||
[...ships(), shipProps(ships(), gameProps.mode, targetPreview())],
|
[...ships(), shipProps(ships(), gameProps.mode, targetPreview())],
|
||||||
sIndex,
|
index,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
function onClick(
|
||||||
|
props: TilesType,
|
||||||
|
{ selfIndex, activeIndex, ships }: ReturnType<typeof useSession>,
|
||||||
|
) {
|
||||||
|
const sIndex = selfIndex()
|
||||||
|
if (!sIndex) return
|
||||||
|
if (gameProps.gameState === "running") {
|
||||||
|
settingTarget(props.isGameTile, props.x, props.y, sIndex.i, {
|
||||||
|
activeIndex,
|
||||||
|
ships,
|
||||||
|
})
|
||||||
|
} else if (gameProps.gameState === "starting") {
|
||||||
|
const { index } = intersectingShip(ships(), {
|
||||||
|
...mouseCursor(),
|
||||||
|
size: 1,
|
||||||
|
variant: 0,
|
||||||
|
orientation: "h",
|
||||||
|
})
|
||||||
|
if (typeof index === "undefined")
|
||||||
|
settingTarget(props.isGameTile, props.x, props.y, sIndex.i, {
|
||||||
|
activeIndex,
|
||||||
|
ships,
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
const ship = ships()[index]
|
||||||
|
setGameProps("mode", ship.size - 2)
|
||||||
|
removeShip(ship, sIndex.i)
|
||||||
|
setMouseCursor((e) => ({ ...e, shouldShow: true }))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseEnter(
|
||||||
|
props: TilesType,
|
||||||
|
{ ships }: ReturnType<typeof useSession>,
|
||||||
|
) {
|
||||||
|
setMouseCursor({
|
||||||
|
x: props.x,
|
||||||
|
y: props.y,
|
||||||
|
shouldShow:
|
||||||
|
props.isGameTile &&
|
||||||
|
(gameProps.gameState === "starting"
|
||||||
|
? intersectingShip(
|
||||||
|
ships(),
|
||||||
|
shipProps(ships(), gameProps.mode, {
|
||||||
|
x: props.x,
|
||||||
|
y: props.y,
|
||||||
|
orientation: targetPreview().orientation,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
).score < 2
|
||||||
|
: true),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function BorderTiles() {
|
||||||
|
const sessionProps = useSession()
|
||||||
|
|
||||||
const tilesProperties: TilesType[] = []
|
const tilesProperties: TilesType[] = []
|
||||||
|
|
||||||
|
@ -97,47 +158,8 @@ function BorderTiles() {
|
||||||
<div
|
<div
|
||||||
class={props.className}
|
class={props.className}
|
||||||
style={{ "--x": props.x, "--y": props.y }}
|
style={{ "--x": props.x, "--y": props.y }}
|
||||||
onClick={() => {
|
onClick={() => onClick(props, sessionProps)}
|
||||||
const sIndex = selfIndex()
|
onMouseEnter={() => onMouseEnter(props, sessionProps)}
|
||||||
if (sIndex === -1) return
|
|
||||||
if (gameProps.gameState === "running") {
|
|
||||||
settingTarget(props.isGameTile, props.x, props.y)
|
|
||||||
} else if (gameProps.gameState === "starting") {
|
|
||||||
const { index } = intersectingShip(ships(), {
|
|
||||||
...mouseCursor(),
|
|
||||||
size: 1,
|
|
||||||
variant: 0,
|
|
||||||
orientation: "h",
|
|
||||||
})
|
|
||||||
if (typeof index === "undefined")
|
|
||||||
settingTarget(props.isGameTile, props.x, props.y)
|
|
||||||
else {
|
|
||||||
const ship = ships()[index]
|
|
||||||
setGameProps("mode", ship.size - 2)
|
|
||||||
removeShip(ship, sIndex)
|
|
||||||
setMouseCursor((e) => ({ ...e, shouldShow: true }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseEnter={() =>
|
|
||||||
setMouseCursor({
|
|
||||||
x: props.x,
|
|
||||||
y: props.y,
|
|
||||||
shouldShow:
|
|
||||||
props.isGameTile &&
|
|
||||||
(gameProps.gameState === "starting"
|
|
||||||
? intersectingShip(
|
|
||||||
ships(),
|
|
||||||
shipProps(ships(), gameProps.mode, {
|
|
||||||
x: props.x,
|
|
||||||
y: props.y,
|
|
||||||
orientation: targetPreview().orientation,
|
|
||||||
}),
|
|
||||||
true,
|
|
||||||
).score < 2
|
|
||||||
: true),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|
|
@ -44,7 +44,7 @@ import Item from "./Item"
|
||||||
|
|
||||||
function EventBar(props: { clear: () => void }) {
|
function EventBar(props: { clear: () => void }) {
|
||||||
const { shouldHide, setShouldHide, setEnable, color } = useDrawProps
|
const { shouldHide, setShouldHide, setEnable, color } = useDrawProps
|
||||||
const { selfIndex, isActiveIndex, selfUser, ships } = useSession()
|
const { selfIndex, selfIsActiveIndex, selfUser, ships } = useSession()
|
||||||
const navigator = useNavigate()
|
const navigator = useNavigate()
|
||||||
|
|
||||||
const items = (): EventBarModes => ({
|
const items = (): EventBarModes => ({
|
||||||
|
@ -260,7 +260,7 @@ function EventBar(props: { clear: () => void }) {
|
||||||
// if (gameProps.gameState !== "running") return
|
// if (gameProps.gameState !== "running") return
|
||||||
|
|
||||||
// const toastId = "otherPlayer"
|
// const toastId = "otherPlayer"
|
||||||
// if (isActiveIndex) toast.dismiss(toastId)
|
// if (selfIsActiveIndex) toast.dismiss(toastId)
|
||||||
// else
|
// else
|
||||||
// toast.info("Waiting for other player...", {
|
// toast.info("Waiting for other player...", {
|
||||||
// toastId,
|
// toastId,
|
||||||
|
@ -299,7 +299,7 @@ function EventBar(props: { clear: () => void }) {
|
||||||
<For each={items()[gameProps.menu]}>
|
<For each={items()[gameProps.menu]}>
|
||||||
{(e, i) => (
|
{(e, i) => (
|
||||||
<Show
|
<Show
|
||||||
when={isActiveIndex() || gameProps.menu !== "main" || i() !== 1}
|
when={selfIsActiveIndex() || gameProps.menu !== "main" || i() !== 1}
|
||||||
>
|
>
|
||||||
<Item {...e} />
|
<Item {...e} />
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -319,12 +319,12 @@ function EventBar(props: { clear: () => void }) {
|
||||||
gameProps.mode >= 0 &&
|
gameProps.mode >= 0 &&
|
||||||
target().show,
|
target().show,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const i = selfIndex()
|
const sIndex = selfIndex()
|
||||||
if (i === -1) return
|
if (!sIndex) return
|
||||||
switch (gameProps.gameState) {
|
switch (gameProps.gameState) {
|
||||||
case "starting":
|
case "starting":
|
||||||
const isReady = !users[i]?.isReady
|
const isReady = !users[sIndex.i]?.isReady
|
||||||
setIsReadyFor({ isReady, i })
|
setIsReadyFor({ isReady, i: sIndex.i })
|
||||||
socket.emit("isReady", isReady)
|
socket.emit("isReady", isReady)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,16 @@ import { faBurst, faXmark } from "@fortawesome/pro-solid-svg-icons"
|
||||||
import { For } from "solid-js"
|
import { For } from "solid-js"
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||||
import { useSession } from "~/hooks/useSession"
|
import { useSession } from "~/hooks/useSession"
|
||||||
|
import { compiledHits } from "~/lib/utils/helpers"
|
||||||
import { Hit } from "../../interfaces/frontend"
|
import { Hit } from "../../interfaces/frontend"
|
||||||
|
|
||||||
function HitElems(props: { hits?: Hit[]; colorOverride?: string }) {
|
function HitElems(props: { hits?: Hit[]; colorOverride?: string }) {
|
||||||
const { activeUser } = useSession()
|
const { activeIndex } = useSession()
|
||||||
const hits = () => props?.hits
|
const hits = () => props?.hits
|
||||||
const colorOverride = () => props?.colorOverride
|
const colorOverride = () => props?.colorOverride
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<For each={hits() ?? activeUser()?.hits()}>
|
<For each={hits() ?? compiledHits(activeIndex())}>
|
||||||
{(props) => (
|
{(props) => (
|
||||||
<div class="hit-svg" style={{ "--x": props.x, "--y": props.y }}>
|
<div class="hit-svg" style={{ "--x": props.x, "--y": props.y }}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
|
|
@ -18,7 +18,7 @@ function Ship(
|
||||||
) {
|
) {
|
||||||
const { selfIndex } = useSession()
|
const { selfIndex } = useSession()
|
||||||
const filename = () =>
|
const filename = () =>
|
||||||
`ship_${selfIndex() > 0 ? "red" : "blue"}_${props.size}x_${
|
`ship_${selfIndex()?.i === 1 ? "red" : "blue"}_${props.size}x_${
|
||||||
props.variant
|
props.variant
|
||||||
}.gif`
|
}.gif`
|
||||||
let canvasRef: HTMLCanvasElement
|
let canvasRef: HTMLCanvasElement
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { useSession } from "~/hooks/useSession"
|
||||||
import Ship from "./Ship"
|
import Ship from "./Ship"
|
||||||
|
|
||||||
function Ships() {
|
function Ships() {
|
||||||
const { isActiveIndex, selfUser } = useSession()
|
const { selfIsActiveIndex, selfUser } = useSession()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={gameProps.gameState !== "running" || !isActiveIndex()}>
|
<Show when={gameProps.gameState !== "running" || !selfIsActiveIndex()}>
|
||||||
<For each={selfUser()?.ships}>{(props) => <Ship {...props} />}</For>
|
<For each={selfUser()?.ships}>{(props) => <Ship {...props} />}</For>
|
||||||
</Show>
|
</Show>
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,7 +24,7 @@ function Targets() {
|
||||||
each={composeTargetTiles(
|
each={composeTargetTiles(
|
||||||
target(),
|
target(),
|
||||||
gameProps.mode,
|
gameProps.mode,
|
||||||
compiledHits(activeIndex() === 0 ? 1 : 0),
|
compiledHits(activeIndex()),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{(props) => <GamefieldPointer {...props} />}
|
{(props) => <GamefieldPointer {...props} />}
|
||||||
|
@ -33,7 +33,7 @@ function Targets() {
|
||||||
each={composeTargetTiles(
|
each={composeTargetTiles(
|
||||||
targetPreview(),
|
targetPreview(),
|
||||||
gameProps.mode,
|
gameProps.mode,
|
||||||
compiledHits(activeIndex() === 0 ? 1 : 0),
|
compiledHits(activeIndex()),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{(props) => <GamefieldPointer {...props} preview />}
|
{(props) => <GamefieldPointer {...props} preview />}
|
||||||
|
|
|
@ -103,17 +103,18 @@ function LobbyFrame(props: { openSettings: () => void }) {
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<>
|
|
||||||
<Player src="player_blue.png" i={0} userId={session()?.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>
|
<p class="font-farro m-4 text-6xl font-semibold">VS</p>
|
||||||
{users[1] ? (
|
<Show
|
||||||
<Player src="player_red.png" i={1} userId={session()?.user?.id} />
|
when={users[1]}
|
||||||
) : (
|
fallback={
|
||||||
<p class="font-farro w-96 text-center text-4xl font-medium">
|
<p class="font-farro w-96 text-center text-4xl font-medium">
|
||||||
<WithDots>Warte auf Spieler 2</WithDots>
|
<WithDots>Warte auf Spieler 2</WithDots>
|
||||||
</p>
|
</p>
|
||||||
)}
|
}
|
||||||
</>
|
>
|
||||||
|
<Player src="player_red.png" i={1} userId={session()?.user?.id} />
|
||||||
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-around border-t-2 border-slate-900 p-4">
|
<div class="flex items-center justify-around border-t-2 border-slate-900 p-4">
|
||||||
|
|
|
@ -74,25 +74,25 @@ function Player(props: { src: string; i: 0 | 1; userId?: string }) {
|
||||||
<Button
|
<Button
|
||||||
type={
|
type={
|
||||||
player()?.isConnected
|
player()?.isConnected
|
||||||
? player()?.isReady
|
? users[props.i]?.isReady
|
||||||
? "green"
|
? "green"
|
||||||
: "orange"
|
: "orange"
|
||||||
: "gray"
|
: "gray"
|
||||||
}
|
}
|
||||||
latching
|
latching
|
||||||
isLatched={player()?.isReady}
|
isLatched={users[props.i]?.isReady}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!player()) return
|
if (!player()) return
|
||||||
socket.emit("isReady", !player()?.isReady)
|
socket.emit("isReady", !users[props.i]?.isReady)
|
||||||
setIsReadyFor({
|
setIsReadyFor({
|
||||||
i: props.i,
|
i: props.i,
|
||||||
isReady: !player()?.isReady,
|
isReady: !users[props.i]?.isReady,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
disabled={!primary()}
|
disabled={!primary()}
|
||||||
>
|
>
|
||||||
Ready
|
Ready
|
||||||
{player()?.isReady && player()?.isConnected ? (
|
{users[props.i]?.isReady && player()?.isConnected ? (
|
||||||
<FontAwesomeIcon icon={faCheck} class="ml-4 w-12" />
|
<FontAwesomeIcon icon={faCheck} class="ml-4 w-12" />
|
||||||
) : primary() ? (
|
) : primary() ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
|
|
@ -5,25 +5,11 @@ import {
|
||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
import { JSX } from "solid-js"
|
import { JSX } from "solid-js"
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
|
||||||
import {
|
import { gameProps, setGameSetting } from "~/hooks/useGameProps"
|
||||||
allowChat,
|
|
||||||
allowMarkDraw,
|
|
||||||
allowSpecials,
|
|
||||||
allowSpectators,
|
|
||||||
setGameSetting,
|
|
||||||
} from "~/hooks/useGameProps"
|
|
||||||
import { GameSettingKeys } from "../../../interfaces/frontend"
|
import { GameSettingKeys } from "../../../interfaces/frontend"
|
||||||
|
|
||||||
function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
function Setting(props: { children: JSX.Element; key: GameSettingKeys }) {
|
||||||
const state = () => {
|
const state = () => gameProps[props.key]
|
||||||
const gameProps = {
|
|
||||||
allowChat,
|
|
||||||
allowMarkDraw,
|
|
||||||
allowSpecials,
|
|
||||||
allowSpectators,
|
|
||||||
}
|
|
||||||
return gameProps[props.key]()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label class="flex items-center justify-between" for={props.key}>
|
<label class="flex items-center justify-between" for={props.key}>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { socket } from "~/lib/socket"
|
||||||
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
|
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
|
||||||
// import { toast } from "react-toastify"
|
// import { toast } from "react-toastify"
|
||||||
import { createSignal } from "solid-js"
|
import { createSignal } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore, produce } from "solid-js/store"
|
||||||
import { getPayloadFromProps } from "~/lib/getPayloadFromProps"
|
import { getPayloadFromProps } from "~/lib/getPayloadFromProps"
|
||||||
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
|
||||||
import {
|
import {
|
||||||
|
@ -16,6 +16,7 @@ import {
|
||||||
GameSettings,
|
GameSettings,
|
||||||
MouseCursor,
|
MouseCursor,
|
||||||
MoveDispatchProps,
|
MoveDispatchProps,
|
||||||
|
Players,
|
||||||
ShipProps,
|
ShipProps,
|
||||||
Target,
|
Target,
|
||||||
TargetPreview,
|
TargetPreview,
|
||||||
|
@ -53,7 +54,43 @@ export const [gameProps, setGameProps] =
|
||||||
createStore<GameProps>(initialGameProps)
|
createStore<GameProps>(initialGameProps)
|
||||||
|
|
||||||
const initialUsers = { 0: null, 1: null }
|
const initialUsers = { 0: null, 1: null }
|
||||||
export const [users, setUsers] = createStore<Users>(initialUsers)
|
export const [users, setUsersStore] = createStore<Users>(initialUsers)
|
||||||
|
|
||||||
|
export function setUsers(
|
||||||
|
i: 0 | 1,
|
||||||
|
userInput: Players | Partial<NonNullable<User>>,
|
||||||
|
): void {
|
||||||
|
setUsersStore(i, (state) => {
|
||||||
|
if (0 in userInput) {
|
||||||
|
const user = userInput[i]
|
||||||
|
if (!user && state) return null
|
||||||
|
else if (!state)
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
isReady: false,
|
||||||
|
isConnected: false,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return produce<NonNullable<typeof state>>((u) => {
|
||||||
|
Object.assign(u, user)
|
||||||
|
})(state)
|
||||||
|
} else {
|
||||||
|
const user = userInput
|
||||||
|
if (!state) {
|
||||||
|
console.error(
|
||||||
|
"Everything is fine! Still, this was unexpected and should probably be fixed. 😁",
|
||||||
|
userInput,
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
console.trace(userInput)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return produce<NonNullable<typeof state>>((u) => {
|
||||||
|
Object.assign(u, user)
|
||||||
|
})(state)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const [target, setTarget] = createSignal<Target>(initlialTarget)
|
export const [target, setTarget] = createSignal<Target>(initlialTarget)
|
||||||
export const [targetPreview, setTargetPreview] = createSignal<TargetPreview>(
|
export const [targetPreview, setTargetPreview] = createSignal<TargetPreview>(
|
||||||
|
@ -63,28 +100,34 @@ export const [mouseCursor, setMouseCursor] =
|
||||||
createSignal<MouseCursor>(initlialMouseCursor)
|
createSignal<MouseCursor>(initlialMouseCursor)
|
||||||
|
|
||||||
export function DispatchMove(move: MoveDispatchProps, index: 0 | 1) {
|
export function DispatchMove(move: MoveDispatchProps, index: 0 | 1) {
|
||||||
setUsers(index, "moves", (e) => [...e, move])
|
setUsers(index, {
|
||||||
|
moves: [...(users[index]?.moves ?? []), move],
|
||||||
|
isReady: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setShips(ships: ShipProps[], index: 0 | 1) {
|
export function setShips(ships: ShipProps[], index: 0 | 1) {
|
||||||
setUsers(index, "ships", ships)
|
setUsers(index, { ships })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeShip({ size, variant, x, y }: ShipProps, index: 0 | 1) {
|
export function removeShip({ size, variant, x, y }: ShipProps, index: 0 | 1) {
|
||||||
setUsers(index, "ships", (ships) => {
|
const ships = users[index]?.ships ?? []
|
||||||
const indexToRemove = ships.findIndex(
|
const indexToRemove = users[index]?.ships.findIndex(
|
||||||
(ship) =>
|
(ship) =>
|
||||||
ship.size === size &&
|
ship.size === size &&
|
||||||
ship.variant === variant &&
|
ship.variant === variant &&
|
||||||
ship.x === x &&
|
ship.x === x &&
|
||||||
ship.y === y,
|
ship.y === y,
|
||||||
)
|
)
|
||||||
|
setUsers(index, {
|
||||||
return ships.filter((_, i) => i !== indexToRemove)
|
ships: ships.filter((_, i) => i !== indexToRemove),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setPlayer(newUsers: Users): string | null {
|
export function setPlayer(newUsers: Players): string | null {
|
||||||
let hash: string | null = null
|
let hash: string | null = null
|
||||||
setUsers(newUsers)
|
setUsers(0, newUsers)
|
||||||
|
setUsers(1, newUsers)
|
||||||
const body = getPayloadwithChecksum(getPayloadFromProps())
|
const body = getPayloadwithChecksum(getPayloadFromProps())
|
||||||
if (!body.hash) {
|
if (!body.hash) {
|
||||||
console.log("Something is wrong... ")
|
console.log("Something is wrong... ")
|
||||||
|
@ -98,6 +141,7 @@ export function setPlayer(newUsers: Users): string | null {
|
||||||
setGameProps("hash", hash)
|
setGameProps("hash", hash)
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSetting(newSettings: GameSettings): string | null {
|
export function setSetting(newSettings: GameSettings): string | null {
|
||||||
let hash: string | null = null
|
let hash: string | null = null
|
||||||
setGameProps("allowChat", (e) => newSettings.allowChat ?? e)
|
setGameProps("allowChat", (e) => newSettings.allowChat ?? e)
|
||||||
|
@ -152,21 +196,11 @@ export function full(newProps: GamePropsSchema) {
|
||||||
allowSpecials: newProps.payload.game?.allowSpecials ?? false,
|
allowSpecials: newProps.payload.game?.allowSpecials ?? false,
|
||||||
allowSpectators: newProps.payload.game?.allowSpectators ?? false,
|
allowSpectators: newProps.payload.game?.allowSpectators ?? false,
|
||||||
})
|
})
|
||||||
const compiledUsers = [
|
setUsers(0, newProps.payload.users)
|
||||||
newProps.payload.users[0],
|
setUsers(1, newProps.payload.users)
|
||||||
newProps.payload.users[1],
|
|
||||||
].map((user) =>
|
|
||||||
user
|
|
||||||
? {
|
|
||||||
...user,
|
|
||||||
isReady: false,
|
|
||||||
isConnected: false,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
) as [User, User]
|
|
||||||
setUsers({ 0: compiledUsers[0], 1: compiledUsers[1] })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function leave(cb: () => void) {
|
export function leave(cb: () => void) {
|
||||||
socket.emit("leave", (ack) => {
|
socket.emit("leave", (ack) => {
|
||||||
if (!ack) {
|
if (!ack) {
|
||||||
|
@ -176,24 +210,28 @@ export function leave(cb: () => void) {
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setIsReadyFor({ i, isReady }: { i: 0 | 1; isReady: boolean }) {
|
export function setIsReadyFor({ i, isReady }: { i: 0 | 1; isReady: boolean }) {
|
||||||
setUsers(i, (e) => ({ ...e, isReady, isConnected: true }))
|
setUsers(i, {
|
||||||
|
isReady: isReady,
|
||||||
|
isConnected: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newGameState(newState: GameState) {
|
export function newGameState(newState: GameState) {
|
||||||
setGameProps("gameState", newState)
|
setGameProps("gameState", newState)
|
||||||
setUsers(0, (e) => (e && e.isReady ? { isReady: false } : e))
|
setUsers(0, { isReady: false })
|
||||||
setUsers(1, (e) => (e && e.isReady ? { isReady: false } : e))
|
setUsers(1, { isReady: false })
|
||||||
}
|
}
|
||||||
export function setIsConnectedFor({
|
|
||||||
i,
|
export function setIsConnectedFor(props: { i: 0 | 1; isConnected: boolean }) {
|
||||||
isConnected,
|
setUsers(props.i, {
|
||||||
}: {
|
isConnected: props.isConnected,
|
||||||
i: 0 | 1
|
})
|
||||||
isConnected: boolean
|
if (props.isConnected) return
|
||||||
}) {
|
setUsers(props.i, {
|
||||||
setUsers(i, "isConnected", isConnected)
|
isReady: false,
|
||||||
if (isConnected) return
|
})
|
||||||
setUsers(i, "isReady", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reset() {
|
export function reset() {
|
||||||
|
@ -201,5 +239,5 @@ export function reset() {
|
||||||
setTarget(initlialTarget)
|
setTarget(initlialTarget)
|
||||||
setTargetPreview(initlialTargetPreview)
|
setTargetPreview(initlialTargetPreview)
|
||||||
setMouseCursor(initlialMouseCursor)
|
setMouseCursor(initlialMouseCursor)
|
||||||
setUsers(initialUsers)
|
setUsersStore(initialUsers)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Session } from "@auth/core/types"
|
import { Session } from "@auth/core/types"
|
||||||
import objectHash from "object-hash"
|
import stringify from "json-stable-stringify"
|
||||||
import {
|
import {
|
||||||
JSX,
|
JSX,
|
||||||
createContext,
|
createContext,
|
||||||
|
@ -13,14 +13,14 @@ import { gameProps, setGameProps, users } from "./useGameProps"
|
||||||
|
|
||||||
const [state, setState] = createSignal<Session | null | undefined>(undefined)
|
const [state, setState] = createSignal<Session | null | undefined>(undefined)
|
||||||
|
|
||||||
const selfIndex = () => {
|
function selfIndex(): { i: 0 | 1 } | null {
|
||||||
switch (state()?.user?.id) {
|
switch (state()?.user?.id) {
|
||||||
case users[0]?.id:
|
case users[0]?.id:
|
||||||
return 0
|
return { i: 0 }
|
||||||
case users[1]?.id:
|
case users[1]?.id:
|
||||||
return 1
|
return { i: 1 }
|
||||||
default:
|
default:
|
||||||
return -1
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,23 +31,22 @@ const activeIndex = () => {
|
||||||
return l1 > l2 ? 1 : 0
|
return l1 > l2 ? 1 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const isActiveIndex = () => {
|
const selfIsActiveIndex = () => {
|
||||||
const sI = selfIndex()
|
const sIndex = selfIndex()
|
||||||
return sI >= 0 && activeIndex() === sI
|
return !!sIndex && activeIndex() === sIndex.i
|
||||||
}
|
}
|
||||||
|
|
||||||
const selfUser = () => {
|
const selfUser = () => {
|
||||||
const i = selfIndex()
|
const sIndex = selfIndex()
|
||||||
if (i === -1) return null
|
return sIndex ? users[sIndex.i] : null
|
||||||
return users[i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It should be the opposite of `activeIndex`.
|
* It should be the opposite of `activeIndex`.
|
||||||
*
|
*
|
||||||
* This is because `activeIndex` is attacking the `activeUser`.
|
* This is because `activeIndex` is attacking the `enemyUser`.
|
||||||
*/
|
*/
|
||||||
const activeUser = () => users[activeIndex() === 0 ? 1 : 0]
|
const enemyUser = () => users[activeIndex() === 0 ? 1 : 0]
|
||||||
|
|
||||||
const ships = () => selfUser()?.ships ?? []
|
const ships = () => selfUser()?.ships ?? []
|
||||||
|
|
||||||
|
@ -55,9 +54,9 @@ const contextValue = {
|
||||||
session: state,
|
session: state,
|
||||||
selfIndex,
|
selfIndex,
|
||||||
activeIndex,
|
activeIndex,
|
||||||
isActiveIndex,
|
selfIsActiveIndex,
|
||||||
selfUser,
|
selfUser,
|
||||||
activeUser,
|
activeUser: enemyUser,
|
||||||
ships,
|
ships,
|
||||||
}
|
}
|
||||||
export const SessionCtx = createContext(contextValue)
|
export const SessionCtx = createContext(contextValue)
|
||||||
|
@ -77,7 +76,7 @@ export function SessionProvider(props: { children: JSX.Element }) {
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const session = data()
|
const session = data()
|
||||||
const hashDiff = objectHash(session ?? null) !== objectHash(state() ?? null)
|
const hashDiff = stringify(session) !== stringify(state())
|
||||||
if (session === undefined || data.loading || !hashDiff) return
|
if (session === undefined || data.loading || !hashDiff) return
|
||||||
console.log("Session updated.")
|
console.log("Session updated.")
|
||||||
setState(session)
|
setState(session)
|
||||||
|
@ -85,7 +84,9 @@ export function SessionProvider(props: { children: JSX.Element }) {
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (gameProps.gameState !== "running") return
|
if (gameProps.gameState !== "running") return
|
||||||
if (activeIndex() === selfIndex()) {
|
|
||||||
|
const sIndex = selfIndex()
|
||||||
|
if (activeIndex() === sIndex?.i) {
|
||||||
setGameProps("menu", "moves")
|
setGameProps("menu", "moves")
|
||||||
setGameProps("mode", 0)
|
setGameProps("mode", 0)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import status from "http-status"
|
||||||
import { createEffect, createSignal, onCleanup } from "solid-js"
|
import { createEffect, createSignal, onCleanup } from "solid-js"
|
||||||
import { useNavigate } from "solid-start"
|
import { useNavigate } from "solid-start"
|
||||||
import { socket } from "~/lib/socket"
|
import { socket } from "~/lib/socket"
|
||||||
import { frontendUsers } from "~/lib/utils/helpers"
|
|
||||||
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
|
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
|
||||||
import { isAuthenticated } from "~/routes/start"
|
import { isAuthenticated } from "~/routes/start"
|
||||||
import { GameSettings, PlayerEvent } from "../interfaces/frontend"
|
import { GameSettings, PlayerEvent } from "../interfaces/frontend"
|
||||||
|
@ -28,17 +27,18 @@ function useSocket() {
|
||||||
const navigator = useNavigate()
|
const navigator = useNavigate()
|
||||||
|
|
||||||
const isConnected = () => {
|
const isConnected = () => {
|
||||||
const i = selfIndex()
|
const sIndex = selfIndex()
|
||||||
return i !== -1
|
return sIndex
|
||||||
? users[i]?.isConnected && isConnectedState()
|
? users[sIndex.i]?.isConnected && isConnectedState()
|
||||||
: isConnectedState()
|
: isConnectedState()
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const i = selfIndex()
|
const sIndex = selfIndex()
|
||||||
if (i === -1) return
|
if (!sIndex) return
|
||||||
|
if (!users[sIndex.i]) return
|
||||||
setIsConnectedFor({
|
setIsConnectedFor({
|
||||||
i,
|
i: sIndex.i,
|
||||||
isConnected: isConnectedState(),
|
isConnected: isConnectedState(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -68,7 +68,17 @@ function useSocket() {
|
||||||
const playerEvent = (event: PlayerEvent) => {
|
const playerEvent = (event: PlayerEvent) => {
|
||||||
const { type, i } = event
|
const { type, i } = event
|
||||||
let message: string
|
let message: string
|
||||||
console.log("playerEvent", type)
|
if (type !== "disconnect") {
|
||||||
|
const { hash } = event
|
||||||
|
const newHash = setPlayer(event.users)
|
||||||
|
if (newHash && newHash !== hash) {
|
||||||
|
console.log("hash", hash, newHash)
|
||||||
|
socket.emit("update", (body) => {
|
||||||
|
console.log("Update is needed after", type)
|
||||||
|
full(body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "disconnect":
|
case "disconnect":
|
||||||
setIsConnectedFor({
|
setIsConnectedFor({
|
||||||
|
@ -87,9 +97,8 @@ function useSocket() {
|
||||||
i,
|
i,
|
||||||
isConnected: true,
|
isConnected: true,
|
||||||
})
|
})
|
||||||
const index = selfIndex()
|
const sIndex = selfIndex()
|
||||||
if (index !== -1)
|
if (sIndex) socket.emit("isReady", users[sIndex.i]?.isReady ?? false)
|
||||||
socket.emit("isReady", users[index]?.isReady ?? false)
|
|
||||||
message = "Player has joined the lobby."
|
message = "Player has joined the lobby."
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -99,19 +108,13 @@ function useSocket() {
|
||||||
}
|
}
|
||||||
// toast.info(message, { toastId: message })
|
// toast.info(message, { toastId: message })
|
||||||
console.log(message)
|
console.log(message)
|
||||||
if (type === "disconnect") return
|
|
||||||
|
|
||||||
const { hash } = event
|
|
||||||
const newHash = setPlayer(frontendUsers(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 setGameState = (state: GameState) => setGameProps("gameState", state)
|
const setGameState = (state: GameState) => {
|
||||||
|
setGameProps("gameState", state)
|
||||||
|
setIsReadyFor({ i: 0, isReady: false })
|
||||||
|
setIsReadyFor({ i: 1, isReady: false })
|
||||||
|
}
|
||||||
|
|
||||||
const gameSetting = (newSettings: GameSettings, hash: string) => {
|
const gameSetting = (newSettings: GameSettings, hash: string) => {
|
||||||
const newHash = setSetting(newSettings)
|
const newHash = setSetting(newSettings)
|
||||||
|
|
|
@ -55,14 +55,9 @@ interface InterServerEvents {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SocketData {
|
interface SocketData {
|
||||||
props: {
|
|
||||||
userId: string
|
|
||||||
gameId: string
|
|
||||||
index: 0 | 1
|
|
||||||
}
|
|
||||||
user: Session["user"]
|
user: Session["user"]
|
||||||
gameId: string
|
gameId: string
|
||||||
index: 0 | 1
|
index: { i: 0 | 1 } | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type sServer = Server<
|
export type sServer = Server<
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import hash from "object-hash"
|
import stringify from "json-stable-stringify"
|
||||||
|
import objectHash from "object-hash"
|
||||||
import { GamePropsSchema } from "./zodSchemas"
|
import { GamePropsSchema } from "./zodSchemas"
|
||||||
|
|
||||||
export const getPayloadwithChecksum = (
|
export const getPayloadwithChecksum = (
|
||||||
payload: GamePropsSchema["payload"],
|
payload: GamePropsSchema["payload"],
|
||||||
): GamePropsSchema => ({ payload, hash: hash(payload) })
|
): GamePropsSchema => ({ payload, hash: objectHash(stringify(payload)) })
|
||||||
|
|
|
@ -4,14 +4,12 @@ import type {
|
||||||
Hit,
|
Hit,
|
||||||
IndexedPosition,
|
IndexedPosition,
|
||||||
Mode,
|
Mode,
|
||||||
Players,
|
|
||||||
PointerProps,
|
PointerProps,
|
||||||
Position,
|
Position,
|
||||||
ShipProps,
|
ShipProps,
|
||||||
Target,
|
Target,
|
||||||
TargetList,
|
TargetList,
|
||||||
TargetPreview,
|
TargetPreview,
|
||||||
User,
|
|
||||||
} from "../../interfaces/frontend"
|
} from "../../interfaces/frontend"
|
||||||
import { MoveType, Orientation } from "../zodSchemas"
|
import { MoveType, Orientation } from "../zodSchemas"
|
||||||
|
|
||||||
|
@ -227,14 +225,14 @@ export function intersectingShip(
|
||||||
|
|
||||||
export function compiledHits(i: 0 | 1) {
|
export function compiledHits(i: 0 | 1) {
|
||||||
return (
|
return (
|
||||||
users[i === 0 ? 1 : 0]?.moves.reduce((hits, move) => {
|
users[i]?.moves.reduce((hits, move) => {
|
||||||
const list = targetList(move, move.type)
|
const list = targetList(move, move.type)
|
||||||
return move.type === MoveType.Enum.radar
|
return move.type === MoveType.Enum.radar
|
||||||
? hits
|
? hits
|
||||||
: [
|
: [
|
||||||
...hits,
|
...hits,
|
||||||
...list.map(({ x, y }) => ({
|
...list.map(({ x, y }) => ({
|
||||||
hit: !!intersectingShip(users[i]?.ships ?? [], {
|
hit: !!intersectingShip(users[i === 0 ? 1 : 0]?.ships ?? [], {
|
||||||
...move,
|
...move,
|
||||||
size: 1,
|
size: 1,
|
||||||
variant: 0,
|
variant: 0,
|
||||||
|
@ -246,16 +244,3 @@ export function compiledHits(i: 0 | 1) {
|
||||||
}, [] as Hit[]) ?? []
|
}, [] as Hit[]) ?? []
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const frontendUsers = (users: Players) => {
|
|
||||||
const compiledUsers = [users[0], users[1]].map((user) =>
|
|
||||||
user
|
|
||||||
? {
|
|
||||||
...user,
|
|
||||||
isReady: false,
|
|
||||||
isConnected: false,
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
) as [User, User]
|
|
||||||
return { 0: compiledUsers[0], 1: compiledUsers[1] }
|
|
||||||
}
|
|
||||||
|
|
|
@ -104,22 +104,9 @@ export function composeBody(
|
||||||
...props,
|
...props,
|
||||||
...user,
|
...user,
|
||||||
}))
|
}))
|
||||||
const emptyUser = {
|
|
||||||
id: "",
|
|
||||||
name: "",
|
|
||||||
chats: [],
|
|
||||||
moves: [],
|
|
||||||
ships: [],
|
|
||||||
}
|
|
||||||
const composedUsers = {
|
const composedUsers = {
|
||||||
0: mappedUsers.find((e) => e.index === 0) ?? {
|
0: mappedUsers.find((e) => e.index === 0) ?? null,
|
||||||
index: 0,
|
1: mappedUsers.find((e) => e.index === 1) ?? null,
|
||||||
...emptyUser,
|
|
||||||
},
|
|
||||||
1: mappedUsers.find((e) => e.index === 1) ?? {
|
|
||||||
index: 1,
|
|
||||||
...emptyUser,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
const payload = {
|
const payload = {
|
||||||
game: game,
|
game: game,
|
||||||
|
|
|
@ -65,12 +65,12 @@ export async function GET({
|
||||||
const { payload, hash } = composeBody(game)
|
const { payload, hash } = composeBody(game)
|
||||||
const index = payload.users[0]?.id === socket.data.user?.id ? 0 : 1
|
const index = payload.users[0]?.id === socket.data.user?.id ? 0 : 1
|
||||||
if (index !== 0 && index !== 1) return next(new Error(status["401"]))
|
if (index !== 0 && index !== 1) return next(new Error(status["401"]))
|
||||||
socket.data.index = index
|
socket.data.index = { i: index }
|
||||||
socket.data.gameId = game.id
|
socket.data.gameId = game.id
|
||||||
socket.join(game.id)
|
socket.join(game.id)
|
||||||
socket.to(game.id).emit("playerEvent", {
|
socket.to(game.id).emit("playerEvent", {
|
||||||
type: "connect",
|
type: "connect",
|
||||||
i: socket.data.index,
|
i: socket.data.index.i,
|
||||||
users: payload.users,
|
users: payload.users,
|
||||||
hash,
|
hash,
|
||||||
})
|
})
|
||||||
|
@ -93,9 +93,9 @@ export async function GET({
|
||||||
|
|
||||||
socket.on("update", async (cb) => {
|
socket.on("update", async (cb) => {
|
||||||
const game = await getGameById(socket.data.gameId ?? "")
|
const game = await getGameById(socket.data.gameId ?? "")
|
||||||
if (!game) return
|
if (!game || !socket.data.index) return
|
||||||
if (socket.data.index === 1 && game.users.length === 1)
|
if (socket.data.index.i === 1 && game.users.length === 1)
|
||||||
socket.data.index = 0
|
socket.data.index.i = 0
|
||||||
const body = composeBody(game)
|
const body = composeBody(game)
|
||||||
cb(body)
|
cb(body)
|
||||||
})
|
})
|
||||||
|
@ -122,6 +122,7 @@ export async function GET({
|
||||||
socket.on("ping", (callback) => callback())
|
socket.on("ping", (callback) => callback())
|
||||||
|
|
||||||
socket.on("leave", async (cb) => {
|
socket.on("leave", async (cb) => {
|
||||||
|
if (!socket.data.index) return
|
||||||
const user_Game = (
|
const user_Game = (
|
||||||
await db
|
await db
|
||||||
.delete(user_games)
|
.delete(user_games)
|
||||||
|
@ -170,7 +171,7 @@ export async function GET({
|
||||||
const { payload, hash } = body
|
const { payload, hash } = body
|
||||||
socket.to(socket.data.gameId).emit("playerEvent", {
|
socket.to(socket.data.gameId).emit("playerEvent", {
|
||||||
type: "leave",
|
type: "leave",
|
||||||
i: socket.data.index,
|
i: socket.data.index.i,
|
||||||
users: payload.users,
|
users: payload.users,
|
||||||
hash,
|
hash,
|
||||||
})
|
})
|
||||||
|
@ -183,13 +184,10 @@ export async function GET({
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("isReady", async (isReady) => {
|
socket.on("isReady", async (isReady) => {
|
||||||
if (socket.data.index === undefined || !socket.data.gameId) return
|
if (!socket.data.index || !socket.data.gameId) return
|
||||||
socket
|
socket
|
||||||
.to(socket.data.gameId)
|
.to(socket.data.gameId)
|
||||||
.emit("isReady", { i: socket.data.index, isReady })
|
.emit("isReady", { i: socket.data.index.i, isReady })
|
||||||
socket
|
|
||||||
.to(socket.data.gameId)
|
|
||||||
.emit("isConnected", { i: socket.data.index, isConnected: true })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("canvas-state", (state) => {
|
socket.on("canvas-state", (state) => {
|
||||||
|
@ -197,7 +195,7 @@ export async function GET({
|
||||||
console.log("received canvas state")
|
console.log("received canvas state")
|
||||||
socket
|
socket
|
||||||
.to(socket.data.gameId)
|
.to(socket.data.gameId)
|
||||||
.emit("canvas-state-from-server", state, socket.data.index)
|
.emit("canvas-state-from-server", state, socket.data.index.i)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("draw-line", ({ prevPoint, currentPoint, color }) => {
|
socket.on("draw-line", ({ prevPoint, currentPoint, color }) => {
|
||||||
|
@ -207,7 +205,7 @@ export async function GET({
|
||||||
.emit(
|
.emit(
|
||||||
"draw-line",
|
"draw-line",
|
||||||
{ prevPoint, currentPoint, color },
|
{ prevPoint, currentPoint, color },
|
||||||
socket.data.index,
|
socket.data.index.i,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -217,7 +215,7 @@ export async function GET({
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("gameState", async (newState) => {
|
socket.on("gameState", async (newState) => {
|
||||||
if (socket.data.index !== 0 || !socket.data.gameId) return
|
if (socket.data.index?.i !== 0 || !socket.data.gameId) return
|
||||||
await db
|
await db
|
||||||
.update(games)
|
.update(games)
|
||||||
.set({
|
.set({
|
||||||
|
@ -228,11 +226,7 @@ export async function GET({
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("ships", async (shipsData) => {
|
socket.on("ships", async (shipsData) => {
|
||||||
if (
|
if (!socket.data.gameId || !socket.data.user?.id || !socket.data.index)
|
||||||
!socket.data.gameId ||
|
|
||||||
!socket.data.user?.id ||
|
|
||||||
typeof socket.data.index === "undefined"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
const user_Game = await db.query.user_games.findFirst({
|
const user_Game = await db.query.user_games.findFirst({
|
||||||
|
@ -254,15 +248,11 @@ export async function GET({
|
||||||
|
|
||||||
socket
|
socket
|
||||||
.to(socket.data.gameId)
|
.to(socket.data.gameId)
|
||||||
.emit("ships", shipsData, socket.data.index)
|
.emit("ships", shipsData, socket.data.index.i)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("dispatchMove", async (props) => {
|
socket.on("dispatchMove", async (props) => {
|
||||||
if (
|
if (!socket.data.gameId || !socket.data.user?.id || !socket.data.index)
|
||||||
!socket.data.gameId ||
|
|
||||||
!socket.data.user?.id ||
|
|
||||||
typeof socket.data.index === "undefined"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
const user_Game = await db.query.user_games.findFirst({
|
const user_Game = await db.query.user_games.findFirst({
|
||||||
|
@ -280,7 +270,11 @@ export async function GET({
|
||||||
.values({ ...props, id: createId(), user_game_id: user_Game.id })
|
.values({ ...props, id: createId(), user_game_id: user_Game.id })
|
||||||
.returning()
|
.returning()
|
||||||
|
|
||||||
io.to(socket.data.gameId).emit("dispatchMove", props, socket.data.index)
|
io.to(socket.data.gameId).emit(
|
||||||
|
"dispatchMove",
|
||||||
|
props,
|
||||||
|
socket.data.index.i,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("disconnecting", async () => {
|
socket.on("disconnecting", async () => {
|
||||||
|
@ -289,10 +283,10 @@ export async function GET({
|
||||||
["debug"],
|
["debug"],
|
||||||
request,
|
request,
|
||||||
)
|
)
|
||||||
if (!socket.data.gameId) return
|
if (!socket.data.gameId || !socket.data.index) return
|
||||||
socket.to(socket.data.gameId).emit("playerEvent", {
|
socket.to(socket.data.gameId).emit("playerEvent", {
|
||||||
type: "disconnect",
|
type: "disconnect",
|
||||||
i: socket.data.index,
|
i: socket.data.index.i,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue