Replace gameProp signals with store

This commit is contained in:
aronmal 2023-09-08 18:55:59 +02:00
parent 4390269ed1
commit 252f6f6028
Signed by: aronmal
GPG key ID: 816B7707426FC612
14 changed files with 331 additions and 339 deletions

View file

@ -1,10 +1,9 @@
import { For } from "solid-js"
import {
gameState,
mode,
gameProps,
mouseCursor,
removeShip,
setMode,
setGameProps,
setMouseCursor,
setShips,
setTarget,
@ -13,6 +12,7 @@ import {
import { useSession } from "~/hooks/useSession"
import {
borderCN,
compiledHits,
cornerCN,
fieldIndex,
intersectingShip,
@ -32,19 +32,21 @@ type TilesType = {
}
function BorderTiles() {
const { selfIndex, activeUser, ships } = useSession()
const { selfIndex, ships } = useSession()
const settingTarget = (isGameTile: boolean, x: number, y: number) => {
if (gameState() === "running") {
const list = targetList(targetPreview(), mode())
const sIndex = selfIndex()
if (sIndex === -1) return
if (gameProps.gameState === "running") {
const list = targetList(targetPreview(), gameProps.mode)
if (
!isGameTile ||
!list.filter(
({ x, y }) => !isAlreadyHit(x, y, activeUser()?.hits() ?? []),
({ x, y }) => !isAlreadyHit(x, y, compiledHits(sIndex === 0 ? 1 : 0)),
).length
)
return
if (!overlapsWithAnyBorder(targetPreview(), mode()))
if (!overlapsWithAnyBorder(targetPreview(), gameProps.mode))
setTarget({
show: true,
x,
@ -52,15 +54,17 @@ function BorderTiles() {
orientation: targetPreview().orientation,
})
} else if (
gameState() === "starting" &&
gameProps.gameState === "starting" &&
targetPreview().show &&
!intersectingShip(ships(), shipProps(ships(), mode(), targetPreview()))
.score
!intersectingShip(
ships(),
shipProps(ships(), gameProps.mode, targetPreview()),
).score
) {
setMouseCursor((e) => ({ ...e, shouldShow: false }))
setShips(
[...ships(), shipProps(ships(), mode(), targetPreview())],
selfIndex(),
[...ships(), shipProps(ships(), gameProps.mode, targetPreview())],
sIndex,
)
}
}
@ -94,9 +98,11 @@ function BorderTiles() {
class={props.className}
style={{ "--x": props.x, "--y": props.y }}
onClick={() => {
if (gameState() === "running") {
const sIndex = selfIndex()
if (sIndex === -1) return
if (gameProps.gameState === "running") {
settingTarget(props.isGameTile, props.x, props.y)
} else if (gameState() === "starting") {
} else if (gameProps.gameState === "starting") {
const { index } = intersectingShip(ships(), {
...mouseCursor(),
size: 1,
@ -107,8 +113,8 @@ function BorderTiles() {
settingTarget(props.isGameTile, props.x, props.y)
else {
const ship = ships()[index]
setMode(ship.size - 2)
removeShip(ship, selfIndex())
setGameProps("mode", ship.size - 2)
removeShip(ship, sIndex)
setMouseCursor((e) => ({ ...e, shouldShow: true }))
}
}
@ -119,10 +125,10 @@ function BorderTiles() {
y: props.y,
shouldShow:
props.isGameTile &&
(gameState() === "starting"
(gameProps.gameState === "starting"
? intersectingShip(
ships(),
shipProps(ships(), mode(), {
shipProps(ships(), gameProps.mode, {
x: props.x,
y: props.y,
orientation: targetPreview().orientation,

View file

@ -28,18 +28,11 @@ import { For, Show, createEffect } from "solid-js"
import { useNavigate } from "solid-start"
import { useDrawProps } from "~/hooks/useDrawProps"
import {
allowChat,
allowMarkDraw,
allowSpecials,
allowSpectators,
gameState,
menu,
mode,
gameProps,
reset,
setGameProps,
setGameSetting,
setIsReadyFor,
setMenu,
setMode,
setTarget,
setTargetPreview,
target,
@ -60,36 +53,36 @@ function EventBar(props: { clear: () => void }) {
icon: "burger-menu",
text: "Menu",
callback: () => {
setMenu("menu")
setGameProps("menu", "menu")
},
},
gameState() === "running"
gameProps.gameState === "running"
? {
icon: faSwords,
text: "Attack",
callback: () => {
setMenu("moves")
setGameProps("menu", "moves")
},
}
: {
icon: faShip,
text: "Ships",
callback: () => {
setMenu("moves")
setGameProps("menu", "moves")
},
},
{
icon: "pen",
text: "Draw",
callback: () => {
setMenu("draw")
setGameProps("menu", "draw")
},
},
{
icon: "gear",
text: "Settings",
callback: () => {
setMenu("settings")
setGameProps("menu", "settings")
},
},
],
@ -99,48 +92,46 @@ function EventBar(props: { clear: () => void }) {
text: "Surrender",
iconColor: "darkred",
callback: () => {
setMenu("surrender")
setGameProps("menu", "surrender")
},
},
],
moves:
gameState() === "running"
gameProps.gameState === "running"
? [
{
icon: "scope",
text: "Fire missile",
enabled: mode() === 0,
enabled: gameProps.mode === 0,
callback: () => {
setMode(0)
setGameProps("mode", 0)
setTarget((t) => ({ ...t, show: false }))
},
},
{
icon: "torpedo",
text: "Fire torpedo",
enabled: mode() === 1 || mode() === 2,
enabled: gameProps.mode === 1 || gameProps.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: () => {
setMode(1)
setGameProps("mode", 1)
setTarget((t) => ({ ...t, show: false }))
},
},
{
icon: "radar",
text: "Radar scan",
enabled: mode() === 3,
enabled: gameProps.mode === 3,
amount:
1 -
(selfUser()
?.moves()
.filter((e) => e.type === "radar").length ?? 0),
(selfUser()?.moves.filter((e) => e.type === "radar").length ??
0),
callback: () => {
setMode(3)
setGameProps("mode", 3)
setTarget((t) => ({ ...t, show: false }))
},
},
@ -152,7 +143,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
setMode(0)
setGameProps("mode", 0)
},
},
{
@ -161,7 +152,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
setMode(1)
setGameProps("mode", 1)
},
},
{
@ -170,7 +161,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
setMode(2)
setGameProps("mode", 2)
},
},
{
@ -199,31 +190,31 @@ function EventBar(props: { clear: () => void }) {
{
icon: faGlasses,
text: "Spectators",
disabled: !allowSpectators(),
disabled: !gameProps.allowSpectators,
callback: setGameSetting({
allowSpectators: !allowSpectators(),
allowSpectators: !gameProps.allowSpectators,
}),
},
{
icon: faSparkles,
text: "Specials",
disabled: !allowSpecials(),
disabled: !gameProps.allowSpecials,
callback: setGameSetting({
allowSpecials: !allowSpecials(),
allowSpecials: !gameProps.allowSpecials,
}),
},
{
icon: faComments,
text: "Chat",
disabled: !allowChat(),
callback: setGameSetting({ allowChat: !allowChat() }),
disabled: !gameProps.allowChat,
callback: setGameSetting({ allowChat: !gameProps.allowChat }),
},
{
icon: faScribble,
text: "Mark/Draw",
disabled: !allowMarkDraw(),
disabled: !gameProps.allowMarkDraw,
callback: setGameSetting({
allowMarkDraw: !allowMarkDraw(),
allowMarkDraw: !gameProps.allowMarkDraw,
}),
},
],
@ -243,7 +234,7 @@ function EventBar(props: { clear: () => void }) {
text: "No",
iconColor: "red",
callback: () => {
setMenu("main")
setGameProps("menu", "main")
},
},
],
@ -251,22 +242,22 @@ function EventBar(props: { clear: () => void }) {
createEffect(() => {
if (
menu() !== "moves" ||
gameState() !== "starting" ||
mode() < 0 ||
items().moves[mode()].amount
gameProps.menu !== "moves" ||
gameProps.gameState !== "starting" ||
gameProps.mode < 0 ||
items().moves[gameProps.mode].amount
)
return
const index = items().moves.findIndex((e) => e.amount)
setMode(index)
setGameProps("mode", index)
})
createEffect(() => {
setEnable(menu() === "draw")
setEnable(gameProps.menu === "draw")
})
// createEffect(() => {
// if (gameState() !== "running") return
// if (gameProps.gameState !== "running") return
// const toastId = "otherPlayer"
// if (isActiveIndex) toast.dismiss(toastId)
@ -293,47 +284,55 @@ function EventBar(props: { clear: () => void }) {
return (
<div class="event-bar">
<Show when={menu() !== "main"}>
<Show when={gameProps.menu !== "main"}>
<Item
{...{
icon: faReply,
text: "Return",
iconColor: "#555",
callback: () => {
setMenu("main")
setGameProps("menu", "main")
},
}}
/>
</Show>
<For each={items()[menu()]}>
<For each={items()[gameProps.menu]}>
{(e, i) => (
<Show when={isActiveIndex() || menu() !== "main" || i() !== 1}>
<Show
when={isActiveIndex() || gameProps.menu !== "main" || i() !== 1}
>
<Item {...e} />
</Show>
)}
</For>
<Show when={menu() === "moves"}>
<Show when={gameProps.menu === "moves"}>
<Item
{...{
icon: selfUser()?.isReady() ? faLock : faCheck,
text: selfUser()?.isReady() ? "unready" : "Done",
disabled: gameState() === "starting" ? mode() >= 0 : undefined,
enabled: gameState() === "running" && mode() >= 0 && target().show,
icon: selfUser()?.isReady ? faLock : faCheck,
text: selfUser()?.isReady ? "unready" : "Done",
disabled:
gameProps.gameState === "starting"
? gameProps.mode >= 0
: undefined,
enabled:
gameProps.gameState === "running" &&
gameProps.mode >= 0 &&
target().show,
callback: () => {
const i = selfIndex()
if (i === -1) return
switch (gameState()) {
switch (gameProps.gameState) {
case "starting":
const isReady = !users[i].isReady()
const isReady = !users[i]?.isReady
setIsReadyFor({ isReady, i })
socket.emit("isReady", isReady)
break
case "running":
const moves = selfUser()?.moves()
const moves = selfUser()?.moves
const length = moves?.length
const props = {
type: modes[mode()].type,
type: modes[gameProps.mode].type,
x: target().x,
y: target().y,
orientation: target().orientation,

View file

@ -11,12 +11,10 @@ import { useDraw } from "~/hooks/useDraw"
import { useDrawProps } from "~/hooks/useDrawProps"
import {
full,
gameId,
gameState,
mode,
gameProps,
mouseCursor,
reset,
setMode,
setGameProps,
setMouseCursor,
setTargetPreview,
target,
@ -40,27 +38,28 @@ function Gamefield() {
createEffect(() => {
if (
gameState() !== "starting" ||
!users[0].isReady() ||
!users[1].isReady()
gameProps.gameState !== "starting" ||
!users[0]?.isReady ||
!users[1]?.isReady
)
return
socket.emit("ships", ships() ?? [])
socket.emit("ships", ships())
socket.emit("gameState", "running")
})
createEffect(() => {
if (gameId() || !isConnected()) return
if (gameProps.gameId || !isConnected) return
socket.emit("update", full)
})
createEffect(() => {
if (mode() < 0) return
if (gameProps.mode < 0) return
const { x, y, show } = target()
const { shouldShow, ...position } = mouseCursor()
if (
!shouldShow ||
(gameState() === "running" && overlapsWithAnyBorder(position, mode()))
(gameProps.gameState === "running" &&
overlapsWithAnyBorder(position, gameProps.mode))
)
setTargetPreview((t) => ({ ...t, show: false }))
else {
@ -71,14 +70,17 @@ function Gamefield() {
}))
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key !== "r") return
if (gameState() === "starting") {
if (gameProps.gameState === "starting") {
setTargetPreview((t) => ({
...t,
orientation: t.orientation === "h" ? "v" : "h",
}))
}
if (gameState() === "running" && (mode() === 1 || mode() === 2))
setMode(mode() === 1 ? 2 : 1)
if (
gameProps.gameState === "running" &&
(gameProps.mode === 1 || gameProps.mode === 2)
)
setGameProps("mode", gameProps.mode === 1 ? 2 : 1)
}
document.addEventListener("keydown", handleKeyPress)
onCleanup(() => {
@ -88,7 +90,7 @@ function Gamefield() {
})
createEffect(() => {
if (gameState() !== "aborted") return
if (gameProps.gameState !== "aborted") return
// toast.info("Enemy gave up!")
navigator("/")
reset()

View file

@ -1,5 +1,5 @@
import { For, Show } from "solid-js"
import { gameState } from "~/hooks/useGameProps"
import { gameProps } from "~/hooks/useGameProps"
import { useSession } from "~/hooks/useSession"
import Ship from "./Ship"
@ -7,8 +7,8 @@ function Ships() {
const { isActiveIndex, selfUser } = useSession()
return (
<Show when={gameState() !== "running" || !isActiveIndex()}>
<For each={selfUser()?.ships()}>{(props) => <Ship {...props} />}</For>
<Show when={gameProps.gameState !== "running" || !isActiveIndex()}>
<For each={selfUser()?.ships}>{(props) => <Ship {...props} />}</For>
</Show>
)
}

View file

@ -1,7 +1,8 @@
import { For, Match, Switch } from "solid-js"
import { gameState, mode, target, targetPreview } from "~/hooks/useGameProps"
import { gameProps, target, targetPreview } from "~/hooks/useGameProps"
import { useSession } from "~/hooks/useSession"
import {
compiledHits,
composeTargetTiles,
intersectingShip,
shipProps,
@ -11,29 +12,39 @@ import HitElems from "./HitElems"
import Ship from "./Ship"
function Targets() {
const { activeUser, ships } = useSession()
const { activeIndex, ships } = useSession()
const ship = () => shipProps(ships(), mode(), targetPreview())
const ship = () => shipProps(ships(), gameProps.mode, targetPreview())
const intersectionProps = () => intersectingShip(ships(), ship())
return (
<Switch>
<Match when={gameState() === "running"}>
<For each={composeTargetTiles(target(), mode(), activeUser().hits())}>
<Match when={gameProps.gameState === "running"}>
<For
each={composeTargetTiles(
target(),
gameProps.mode,
compiledHits(activeIndex() === 0 ? 1 : 0),
)}
>
{(props) => <GamefieldPointer {...props} />}
</For>
<For
each={composeTargetTiles(
targetPreview(),
mode(),
activeUser().hits(),
gameProps.mode,
compiledHits(activeIndex() === 0 ? 1 : 0),
)}
>
{(props) => <GamefieldPointer {...props} preview />}
</For>
</Match>
<Match
when={gameState() === "starting" && mode() >= 0 && targetPreview().show}
when={
gameProps.gameState === "starting" &&
gameProps.mode >= 0 &&
targetPreview().show
}
>
<Ship
{...ship()}

View file

@ -5,15 +5,7 @@ import {
import { JSX, Show, createEffect, createSignal, onCleanup } from "solid-js"
import { useNavigate } from "solid-start"
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon"
import {
full,
gameId,
gamePin,
gameState,
leave,
reset,
users,
} from "~/hooks/useGameProps"
import { full, gameProps, leave, reset, users } from "~/hooks/useGameProps"
import { useSession } from "~/hooks/useSession"
import useSocket from "~/hooks/useSocket"
import { socket } from "~/lib/socket"
@ -45,7 +37,7 @@ function LobbyFrame(props: { openSettings: () => void }) {
const navigator = useNavigate()
const { session } = useSession()
const [launchTime, setLaunchTime] = createSignal(3)
const launching = () => users[0].isReady() && users[1].isReady()
const launching = () => users[0]?.isReady && users[1]?.isReady
createEffect(() => {
if (!launching() || launchTime() > 0) return
@ -64,12 +56,13 @@ function LobbyFrame(props: { openSettings: () => void }) {
})
createEffect(() => {
if (gameId() || !isConnected()) return
if (gameProps.gameId || !isConnected) return
socket.emit("update", full)
})
createEffect(() => {
if (gameState() === "unknown" || gameState() === "lobby") return
if (gameProps.gameState === "unknown" || gameProps.gameState === "lobby")
return
navigator("/gamefield")
})
@ -90,10 +83,10 @@ function LobbyFrame(props: { openSettings: () => void }) {
>
{"Game-PIN: "}
<Show
when={isConnected()}
when={isConnected}
fallback={<FontAwesomeIcon icon={faSpinnerThird} spin />}
>
<span class="underline">{gamePin() ?? "----"}</span>
<span class="underline">{gameProps.gamePin ?? "----"}</span>
</Show>
</Show>
</h1>
@ -103,7 +96,7 @@ function LobbyFrame(props: { openSettings: () => void }) {
</div>
<div class="flex items-center justify-around">
<Show
when={isConnected()}
when={isConnected}
fallback={
<p class="font-farro m-48 text-center text-6xl font-medium">
Warte auf Verbindung
@ -113,7 +106,7 @@ function LobbyFrame(props: { openSettings: () => void }) {
<>
<Player src="player_blue.png" i={0} userId={session()?.user?.id} />
<p class="font-farro m-4 text-6xl font-semibold">VS</p>
{users[1].id() ? (
{users[1] ? (
<Player src="player_red.png" i={1} userId={session()?.user?.id} />
) : (
<p class="font-farro w-96 text-center text-4xl font-medium">

View file

@ -44,9 +44,7 @@ function HourGlass() {
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()
const primary = () => props.userId && props.userId === player()?.id
return (
<div class="flex w-96 flex-col items-center gap-4 p-4">
@ -56,7 +54,7 @@ function Player(props: { src: string; i: 0 | 1; userId?: string }) {
primary() ? "font-semibold" : "font-normal",
)}
>
{player().name() ?? "Spieler " + (props.i === 1 ? "2" : "1")}
{player()?.name ?? "Spieler " + (props.i === 1 ? "2" : "1")}
</p>
<div class="relative">
<img
@ -74,21 +72,27 @@ function Player(props: { src: string; i: 0 | 1; userId?: string }) {
</Show>
</div>
<Button
type={isConnected() ? (isReady() ? "green" : "orange") : "gray"}
type={
player()?.isConnected
? player()?.isReady
? "green"
: "orange"
: "gray"
}
latching
isLatched={!!isReady()}
isLatched={player()?.isReady}
onClick={() => {
if (!player()) return
socket.emit("isReady", !isReady())
socket.emit("isReady", !player()?.isReady)
setIsReadyFor({
i: props.i,
isReady: !isReady(),
isReady: !player()?.isReady,
})
}}
disabled={!primary()}
>
Ready
{isReady() && isConnected() ? (
{player()?.isReady && player()?.isConnected ? (
<FontAwesomeIcon icon={faCheck} class="ml-4 w-12" />
) : primary() ? (
<FontAwesomeIcon

View file

@ -3,11 +3,10 @@ import { socket } from "~/lib/socket"
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
// import { toast } from "react-toastify"
import { createSignal } from "solid-js"
import { createStore } from "solid-js/store"
import { getPayloadFromProps } from "~/lib/getPayloadFromProps"
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
import {
compileHits,
initialUser,
initlialMouseCursor,
initlialTarget,
initlialTargetPreview,
@ -17,81 +16,75 @@ import {
GameSettings,
MouseCursor,
MoveDispatchProps,
NewUsers,
ShipProps,
Target,
TargetPreview,
User,
Users,
} from "../interfaces/frontend"
export const [hash, setHash] = createSignal<string | null>(null)
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 interface GameProps {
hash: string | null
gamePin: string | null
gameId: string
gameState: GameState
allowChat: boolean
allowMarkDraw: boolean
allowSpecials: boolean
allowSpectators: boolean
menu: keyof EventBarModes
mode: number
}
const initialGameProps = {
hash: null,
gamePin: null,
gameId: "",
gameState: "unknown",
allowChat: false,
allowMarkDraw: false,
allowSpecials: false,
allowSpectators: false,
menu: "moves",
mode: 0,
} satisfies GameProps
export const [gameProps, setGameProps] =
createStore<GameProps>(initialGameProps)
const initialUsers = { 0: null, 1: null }
export const [users, setUsers] = createStore<Users>(initialUsers)
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(),
hits: () => compileHits(users, 0),
},
1: { ...initialUser(), hits: () => compileHits(users, 1) },
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 Users = typeof users
export function DispatchMove(move: MoveDispatchProps, index: 0 | 1) {
users[index].setMoves((e) => [...e, move])
setUsers(index, "moves", (e) => [...e, move])
}
export function setShips(ships: ShipProps[], index: number) {
users.forEach(({ setShips }, i) => {
if (index !== i) return
setShips(ships)
})
export function setShips(ships: ShipProps[], index: 0 | 1) {
setUsers(index, "ships", ships)
}
export function removeShip({ size, variant, x, y }: ShipProps, index: number) {
users.forEach((user, i) => {
if (index !== i) return
const indexToRemove = user
.ships()
.findIndex(
export function removeShip({ size, variant, x, y }: ShipProps, index: 0 | 1) {
setUsers(index, "ships", (ships) => {
const indexToRemove = ships.findIndex(
(ship) =>
ship.size === size &&
ship.variant === variant &&
ship.x === x &&
ship.y === y,
)
user.setShips((ships) => ships.filter((_, i) => i !== indexToRemove))
return ships.filter((_, i) => i !== indexToRemove)
})
}
export function setPlayer(newUsers: NewUsers): string | null {
export function setPlayer(newUsers: Users): string | null {
let hash: string | null = null
users.forEach((user, i) => {
const newUser = newUsers[i]
if (!newUser) return defaultUser(user)
user.setId(newUser.id)
user.setName(newUser.name)
user.setChats(newUser.chats)
user.setMoves(newUser.moves)
user.setShips(newUser.ships)
})
setUsers(newUsers)
const body = getPayloadwithChecksum(getPayloadFromProps())
if (!body.hash) {
console.log("Something is wrong... ")
@ -102,15 +95,15 @@ export function setPlayer(newUsers: NewUsers): string | null {
return null
}
hash = body.hash
setHash(hash)
setGameProps("hash", 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)
setGameProps("allowChat", (e) => newSettings.allowChat ?? e)
setGameProps("allowMarkDraw", (e) => newSettings.allowMarkDraw ?? e)
setGameProps("allowSpecials", (e) => newSettings.allowSpecials ?? e)
setGameProps("allowSpectators", (e) => newSettings.allowSpectators ?? e)
const body = getPayloadwithChecksum(getPayloadFromProps())
if (!body.hash) {
console.log("Something is wrong... ")
@ -121,7 +114,7 @@ export function setSetting(newSettings: GameSettings): string | null {
return null
}
hash = body.hash
setHash(hash)
setGameProps("hash", hash)
return hash
}
@ -137,36 +130,41 @@ export function setGameSetting(newSettings: GameSettings) {
}
export function full(newProps: GamePropsSchema) {
if (hash() === newProps.hash) {
if (gameProps.hash === newProps.hash) {
console.log("Everything up to date.")
} else {
console.log("Update was needed.", hash(), newProps.hash)
console.log("Update was needed.", gameProps.hash, newProps.hash)
if (gameId() !== newProps.payload?.game?.id)
if (gameProps.gameId !== newProps.payload?.game?.id)
console.warn(
"Different gameId detected on update: ",
gameId(),
gameProps.gameId,
newProps.payload?.game?.id,
)
setHash(newProps.hash)
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)
setGameProps({
hash: newProps.hash,
gamePin: newProps.payload.gamePin,
gameId: newProps.payload.game?.id ?? "",
gameState: newProps.payload.game?.state ?? "unknown",
allowChat: newProps.payload.game?.allowChat ?? false,
allowMarkDraw: newProps.payload.game?.allowMarkDraw ?? false,
allowSpecials: newProps.payload.game?.allowSpecials ?? false,
allowSpectators: newProps.payload.game?.allowSpectators ?? false,
})
const compiledUsers = [
newProps.payload.users[0],
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) {
@ -179,12 +177,12 @@ export function leave(cb: () => void) {
})
}
export function setIsReadyFor({ i, isReady }: { i: 0 | 1; isReady: boolean }) {
users[i].setIsReady(isReady)
users[i].setIsConnected(true)
setUsers(i, (e) => ({ ...e, isReady, isConnected: true }))
}
export function newGameState(newState: GameState) {
setGameState(newState)
users.forEach((e) => e.setIsReady(false))
setGameProps("gameState", newState)
setUsers(0, (e) => (e && e.isReady ? { isReady: false } : e))
setUsers(1, (e) => (e && e.isReady ? { isReady: false } : e))
}
export function setIsConnectedFor({
i,
@ -193,34 +191,15 @@ export function setIsConnectedFor({
i: 0 | 1
isConnected: boolean
}) {
users[i].setIsConnected(isConnected)
setUsers(i, "isConnected", isConnected)
if (isConnected) return
users[i].setIsReady(false)
setUsers(i, "isReady", false)
}
export function reset() {
setHash(null)
setGamePin(null)
setGameId("")
setGameState("unknown")
setallowSpectators(false)
setAllowSpecials(false)
setAllowChat(false)
setAllowMarkDraw(false)
setMenu("moves")
setMode(0)
setGameProps(initialGameProps)
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([])
setUsers(initialUsers)
}

View file

@ -9,14 +9,15 @@ import {
useContext,
} from "solid-js"
import { useIsRouting } from "solid-start"
import { gameState, setMenu, setMode, users } from "./useGameProps"
import { gameProps, setGameProps, users } from "./useGameProps"
const [state, setState] = createSignal<Session | null | undefined>(undefined)
const selfIndex = () => {
switch (state()?.user?.id) {
case users[0].id():
case users[0]?.id:
return 0
case users[1].id():
case users[1]?.id:
return 1
default:
return -1
@ -24,9 +25,9 @@ const selfIndex = () => {
}
const activeIndex = () => {
if (gameState() !== "running") return 0
const l1 = users[0].moves().length
const l2 = users[1].moves().length
if (gameProps.gameState !== "running") return 0
const l1 = users[0]?.moves.length ?? 0
const l2 = users[1]?.moves.length ?? 0
return l1 > l2 ? 1 : 0
}
@ -48,7 +49,8 @@ const selfUser = () => {
*/
const activeUser = () => users[activeIndex() === 0 ? 1 : 0]
const ships = () => selfUser()?.ships() ?? []
const ships = () => selfUser()?.ships ?? []
const contextValue = {
session: state,
selfIndex,
@ -82,13 +84,13 @@ export function SessionProvider(props: { children: JSX.Element }) {
})
createEffect(() => {
if (gameState() !== "running") return
if (gameProps.gameState !== "running") return
if (activeIndex() === selfIndex()) {
setMenu("moves")
setMode(0)
setGameProps("menu", "moves")
setGameProps("mode", 0)
} else {
setMenu("main")
setMode(-1)
setGameProps("menu", "main")
setGameProps("mode", -1)
}
})

View file

@ -3,14 +3,15 @@ import status from "http-status"
import { createEffect, createSignal, onCleanup } from "solid-js"
import { useNavigate } from "solid-start"
import { socket } from "~/lib/socket"
import { GamePropsSchema } from "~/lib/zodSchemas"
import { frontendUsers } from "~/lib/utils/helpers"
import { GamePropsSchema, GameState } from "~/lib/zodSchemas"
import { isAuthenticated } from "~/routes/start"
import { GameSettings, PlayerEvent } from "../interfaces/frontend"
import {
DispatchMove,
full,
gameId,
setGameState,
gameProps,
setGameProps,
setIsConnectedFor,
setIsReadyFor,
setPlayer,
@ -29,7 +30,7 @@ function useSocket() {
const isConnected = () => {
const i = selfIndex()
return i !== -1
? users[i].isConnected() && isConnectedState()
? users[i]?.isConnected && isConnectedState()
: isConnectedState()
}
@ -87,7 +88,8 @@ function useSocket() {
isConnected: true,
})
const index = selfIndex()
if (index !== -1) socket.emit("isReady", users[index].isReady())
if (index !== -1)
socket.emit("isReady", users[index]?.isReady ?? false)
message = "Player has joined the lobby."
break
@ -100,7 +102,7 @@ function useSocket() {
if (type === "disconnect") return
const { hash } = event
const newHash = setPlayer(event.users)
const newHash = setPlayer(frontendUsers(event.users))
if (!newHash || newHash === hash) return
console.log("hash", hash, newHash)
socket.emit("update", (body) => {
@ -109,6 +111,8 @@ function useSocket() {
})
}
const setGameState = (state: GameState) => setGameProps("gameState", state)
const gameSetting = (newSettings: GameSettings, hash: string) => {
const newHash = setSetting(newSettings)
if (!newHash || newHash === hash) return
@ -148,7 +152,7 @@ function useSocket() {
})
createEffect(() => {
if (!gameId()) {
if (!gameProps.gameId) {
socket.disconnect()
fetch("/api/game/running", {
method: "GET",

View file

@ -26,11 +26,11 @@ export interface ServerToClientEvents {
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-state-from-server": (state: string, userIndex: 0 | 1) => void
"draw-line": (props: DrawLineProps, userIndex: 0 | 1) => void
"canvas-clear": () => void
gameState: (newState: GameState) => void
ships: (ships: ShipProps[], index: number) => void
ships: (ships: ShipProps[], index: 0 | 1) => void
dispatchMove: (props: MoveDispatchProps, i: 0 | 1) => void
}
@ -58,7 +58,7 @@ interface SocketData {
props: {
userId: string
gameId: string
index: number
index: 0 | 1
}
user: Session["user"]
gameId: string

View file

@ -81,11 +81,15 @@ export type PlayerEvent =
| {
type: "connect" | "leave"
i: 0 | 1
users: NewUsers
users: Players
hash: string
}
| {
type: "disconnect"
i: 0 | 1
}
export type NewUsers = { 0: PlayerSchema | null; 1: PlayerSchema | null }
export type Players = { 0: PlayerSchema; 1: PlayerSchema }
export type User =
| (PlayerSchema & { isReady: boolean; isConnected: boolean })
| null
export type Users = { 0: User; 1: User }

View file

@ -1,32 +1,28 @@
import {
allowChat,
allowMarkDraw,
allowSpecials,
allowSpectators,
gameId,
gamePin,
gameState,
users,
} from "~/hooks/useGameProps"
import { gameProps, users } from "~/hooks/useGameProps"
export function getPayloadFromProps() {
const reducedUsers = [users[0], users[1]].map((user, i) =>
user
? {
index: i,
id: user.id,
name: user.name,
chats: user.chats,
moves: user.moves,
ships: user.ships,
}
: null,
)
return {
game: {
id: gameId(),
state: gameState(),
allowChat: allowChat(),
allowMarkDraw: allowMarkDraw(),
allowSpecials: allowSpecials(),
allowSpectators: allowSpectators(),
id: gameProps.gameId,
state: gameProps.gameState,
allowChat: gameProps.allowChat,
allowMarkDraw: gameProps.allowMarkDraw,
allowSpecials: gameProps.allowSpecials,
allowSpectators: gameProps.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(),
})),
gamePin: gameProps.gamePin,
users: { 0: reducedUsers[0], 1: reducedUsers[1] },
}
}

View file

@ -1,18 +1,19 @@
import { createSignal } from "solid-js"
import { count } from "~/components/Gamefield/Gamefield"
import { Users } from "~/hooks/useGameProps"
import { users } from "~/hooks/useGameProps"
import type {
Hit,
IndexedPosition,
Mode,
Players,
PointerProps,
Position,
ShipProps,
Target,
TargetList,
TargetPreview,
User,
} from "../../interfaces/frontend"
import { ChatSchema, MoveSchema, MoveType, Orientation } from "../zodSchemas"
import { MoveType, Orientation } from "../zodSchemas"
export function borderCN(count: number, x: number, y: number) {
if (x === 0) return "left"
@ -131,31 +132,6 @@ 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[]>([])
return {
isReady,
setIsReady,
isConnected,
setIsConnected,
id,
setId,
name,
setName,
chats,
setChats,
moves,
setMoves,
ships,
setShips,
}
}
export const shipProps = (
ships: ShipProps[],
@ -249,14 +225,16 @@ export function intersectingShip(
}
}
export function compileHits(users: Users, i: 0 | 1) {
return users[i === 0 ? 1 : 0].moves().reduce((hits, move) => {
export function compiledHits(i: 0 | 1) {
return (
users[i === 0 ? 1 : 0]?.moves.reduce((hits, move) => {
const list = targetList(move, move.type)
if (move.type === MoveType.Enum.radar) return hits
return [
return move.type === MoveType.Enum.radar
? hits
: [
...hits,
...list.map(({ x, y }) => ({
hit: !!intersectingShip(users[i].ships(), {
hit: !!intersectingShip(users[i]?.ships ?? [], {
...move,
size: 1,
variant: 0,
@ -265,5 +243,19 @@ export function compileHits(users: Users, i: 0 | 1) {
y,
})),
]
}, [] 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] }
}