Fix some game logic

This commit is contained in:
aronmal 2023-09-03 18:48:06 +02:00
parent b4fd992611
commit b067747d48
Signed by: aronmal
GPG key ID: 816B7707426FC612
15 changed files with 90 additions and 98 deletions

View file

@ -29,6 +29,8 @@ export function FontAwesomeIcon(
return (
<svg
aria-hidden="true"
role="img"
xmlns="http://www.w3.org/2000/svg"
aria-labelledby={props.titleId}
data-prefix={props.icon.prefix}
data-icon={props.icon.iconName}
@ -45,8 +47,6 @@ export function FontAwesomeIcon(
)}
color={props.color}
style={props.style}
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 ${props.icon.icon[0]} ${props.icon.icon[1]}`}
>
<Show when={props.title}>

View file

@ -234,7 +234,7 @@ function EventBar(props: { clear: () => void }) {
iconColor: "green",
callback: async () => {
socket.emit("gameState", "aborted")
await navigator("/")
navigator("/")
reset()
},
},
@ -307,7 +307,7 @@ function EventBar(props: { clear: () => void }) {
</Show>
<For each={items()[menu()]}>
{(e, i) => (
<Show when={isActiveIndex() && menu() !== "main" && i() !== 1}>
<Show when={isActiveIndex() || menu() !== "main" || i() !== 1}>
<Item {...e} />
</Show>
)}

View file

@ -94,12 +94,6 @@ function Gamefield() {
reset()
})
createEffect(() => {
if (gameId()) return
const timeout = setTimeout(() => navigator("/"), 5000)
return () => clearTimeout(timeout)
})
return (
<div id="gamefield">
{/* <Bluetooth /> */}

View file

@ -20,6 +20,7 @@ function GamefieldPointer(
"--y1": props.y - 1,
"--y2": props.y + 2,
}
return (
<div
class={classNames("hit-svg", "target", props.type, ...props.edges, {
@ -29,7 +30,7 @@ function GamefieldPointer(
})}
style={style()}
>
<FontAwesomeIcon icon={!isRadar ? faCrosshairs : faRadar} />
<FontAwesomeIcon icon={!isRadar() ? faCrosshairs : faRadar} />
</div>
)
}

View file

@ -7,7 +7,7 @@ function Ships() {
const { isActiveIndex, selfUser } = useSession()
return (
<Show when={gameState() === "running" && isActiveIndex}>
<Show when={gameState() !== "running" || !isActiveIndex()}>
<For each={selfUser()?.ships()}>{(props) => <Ship {...props} />}</For>
</Show>
)

View file

@ -13,8 +13,8 @@ import Ship from "./Ship"
function Targets() {
const { activeUser, ships } = useSession()
const ship = shipProps(ships(), mode(), targetPreview())
const { fields, borders, score } = intersectingShip(ships(), ship)
const ship = () => shipProps(ships(), mode(), targetPreview())
const { fields, borders, score } = intersectingShip(ships(), ship())
return (
<Switch>
@ -36,7 +36,7 @@ function Targets() {
when={gameState() === "starting" && mode() >= 0 && targetPreview().show}
>
<Ship
{...ship}
{...ship()}
preview
warn={score > 0}
color={fields.length ? "red" : borders.length ? "orange" : undefined}

View file

@ -25,7 +25,6 @@ import {
} from "../interfaces/frontend"
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")
@ -53,17 +52,6 @@ export const users = {
},
}
// 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) => {
@ -182,7 +170,6 @@ export function full(newProps: GamePropsSchema) {
)
setHash(newProps.hash)
setActiveIndex(newProps.payload.activeIndex)
setGamePin(newProps.payload.gamePin)
setGameId(newProps.payload.game?.id ?? "")
setGameState(newProps.payload.game?.state ?? "unknown")
@ -234,7 +221,6 @@ export function setIsConnectedFor({
export function reset() {
setHash(null)
setActiveIndex(0)
setGamePin(null)
setGameId("")
setGameState("unknown")

View file

@ -1,27 +1,15 @@
import { Session } from "@auth/core/types"
import { getSession } from "@auth/solid-start"
import {
Accessor,
JSX,
createContext,
createEffect,
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[]
}
import { gameState, setMenu, setMode, users } from "./useGameProps"
const [state, setState] = createSignal<Session | null>(null)
const selfIndex = () => {
@ -35,15 +23,29 @@ const selfIndex = () => {
}
}
const activeIndex = () => {
if (gameState() !== "running") return 0
const l1 = users[0].moves().length
const l2 = users[1].moves().length
return l1 > l2 ? 1 : 0
}
const isActiveIndex = () => {
const sI = selfIndex()
return sI >= 0 && activeIndex() === sI
}
const selfUser = () => {
const i = selfIndex()
if (i === -1) return null
return users[i]
}
/**
* It should be the opposite of `activeIndex`.
*
* This is because `activeIndex` is attacking the `activeUser`.
*/
const activeUser = () => users[activeIndex() === 0 ? 1 : 0]
const ships = () => selfUser()?.ships() ?? []
@ -56,7 +58,7 @@ const contextValue = {
activeUser,
ships,
}
export const SessionCtx = createContext<Concext>(contextValue)
export const SessionCtx = createContext(contextValue)
export function SessionProvider(props: { children: JSX.Element }) {
const session = createServerData$(
@ -67,6 +69,17 @@ export function SessionProvider(props: { children: JSX.Element }) {
)()
setState(session ?? null)
createEffect(() => {
if (gameState() !== "running") return
if (activeIndex() === selfIndex()) {
setMenu("moves")
setMode(0)
} else {
setMenu("main")
setMode(-1)
}
})
return (
<SessionCtx.Provider value={contextValue}>
{props.children}

View file

@ -10,12 +10,9 @@ import {
DispatchMove,
full,
gameId,
gameState,
setActiveIndex,
setGameState,
setIsConnectedFor,
setIsReadyFor,
setMenu,
setMode,
setPlayer,
setSetting,
setShips,
@ -27,7 +24,7 @@ import { useSession } from "./useSession"
function useSocket() {
const [isConnectedState, setIsConnectedState] = createSignal(false)
const { selfIndex } = useSession()
const navigate = useNavigate()
const navigator = useNavigate()
const isConnected = () => {
const i = selfIndex()
@ -54,7 +51,7 @@ function useSocket() {
const connectError = (error: Error) => {
console.log("Connection error:", error.message)
if (error.message === status["403"]) navigate("/")
if (error.message === status["403"]) navigator("/")
if (error.message !== "xhr poll error") return
// const toastId = "connect_error"
// const isActive = toast.isActive(toastId)
@ -122,17 +119,6 @@ function useSocket() {
})
}
const activeIndex = (i: 0 | 1) => {
setActiveIndex(i)
if (i === selfIndex()) {
setMenu("moves")
setMode(0)
} else {
setMenu("main")
setMode(-1)
}
}
const disconnect = () => {
console.log("disconnect")
setIsConnectedState(false)
@ -143,9 +129,8 @@ function useSocket() {
socket.on("gameSetting", gameSetting)
socket.on("playerEvent", playerEvent)
socket.on("isReady", setIsReadyFor)
socket.on("gameState", gameState)
socket.on("gameState", setGameState)
socket.on("dispatchMove", DispatchMove)
socket.on("activeIndex", activeIndex)
socket.on("ships", setShips)
socket.on("disconnect", disconnect)
@ -155,9 +140,8 @@ function useSocket() {
socket.off("gameSetting", gameSetting)
socket.off("playerEvent", playerEvent)
socket.off("isReady", setIsReadyFor)
socket.off("gameState", gameState)
socket.off("gameState", setGameState)
socket.off("dispatchMove", DispatchMove)
socket.off("activeIndex", activeIndex)
socket.off("ships", setShips)
socket.off("disconnect", disconnect)
}
@ -172,7 +156,10 @@ function useSocket() {
.then(isAuthenticated)
.then((game) => GamePropsSchema.parse(game))
.then((res) => full(res))
.catch((e) => console.log("Failed to get /api/game/running: ", e))
.catch((e) => {
console.log("Failed to get /api/game/running: ", e)
navigator("/")
})
return
}
if (isConnected()) return

View file

@ -21,9 +21,6 @@ export interface SocketServer extends http.Server {
}
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: 0 | 1; isReady: boolean }) => void
@ -34,8 +31,7 @@ export interface ServerToClientEvents {
"canvas-clear": () => void
gameState: (newState: GameState) => void
ships: (ships: ShipProps[], index: number) => void
activeIndex: (index: 0 | 1) => void
dispatchMove: (props: MoveDispatchProps, i: number) => void
dispatchMove: (props: MoveDispatchProps, i: 0 | 1) => void
}
export interface ClientToServerEvents {

View file

@ -14,6 +14,7 @@ export default function sendResponse<T>(
result: Result<T>,
) {
if (result.redirectUrl) {
logging("Redirect | " + result.message, result.type ?? ["debug"], request)
return redirect(result.redirectUrl)
} else {
logging(result.message, result.type ?? ["debug"], request)

View file

@ -1,5 +1,4 @@
import {
activeIndex,
allowChat,
allowMarkDraw,
allowSpecials,
@ -30,6 +29,5 @@ export function getPayloadFromProps() {
ships: user.ships(),
hits: user.hits(),
})),
activeIndex: activeIndex(),
}
}

View file

@ -98,7 +98,6 @@ export const CreateSchema = z.object({
.nullable(),
gamePin: z.string().nullable(),
users: z.object({ 0: PlayerSchema, 1: PlayerSchema }),
activeIndex: z.literal(0).or(z.literal(1)),
})
export const GamePropsSchema = z.object({

View file

@ -1,9 +1,10 @@
import { getSession } from "@auth/solid-start"
import { and, eq, exists, ne } from "drizzle-orm"
import { and, eq, exists, ne, notExists } from "drizzle-orm"
import { APIEvent } from "solid-start/api"
import db from "~/drizzle"
import { games, user_games } from "~/drizzle/schemas/Tables"
import { rejectionErrors } from "~/lib/backend/errors"
import logging from "~/lib/backend/logging"
import sendResponse from "~/lib/backend/sendResponse"
import { getPayloadwithChecksum } from "~/lib/getPayloadwithChecksum"
import { GamePropsSchema } from "~/lib/zodSchemas"
@ -87,7 +88,15 @@ export const getRunningGameToUser = async (userId: string) => {
and(
ne(game.state, "ended"),
exists(
db.select().from(user_games).where(eq(user_games.userId, userId)),
db
.select()
.from(user_games)
.where(
and(
eq(user_games.gameId, game.id),
eq(user_games.userId, userId),
),
),
),
),
...gameSelects,
@ -122,17 +131,10 @@ export function composeBody(
hits: [],
},
}
let activeIndex: 0 | 1 = 0
if (game.state === "running") {
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: composedUsers,
activeIndex,
}
return getPayloadwithChecksum(payload)
}
@ -144,6 +146,26 @@ export async function GET({ request }: APIEvent) {
return sendResponse(request, rejectionErrors.unauthorized)
}
const abandonedGames = await db
.delete(games)
.where(
notExists(
db.select().from(user_games).where(eq(user_games.gameId, games.id)),
),
)
.returning()
if (abandonedGames.length) {
logging(
"Games were deleted because of missing players. Id's: " +
JSON.stringify(
abandonedGames.map((e) => e.id),
null,
2,
),
["error"],
)
}
const { email, id } = session.user
const game = await getRunningGameToUser(id)

View file

@ -39,11 +39,12 @@ export async function GET({
// io.use(authenticate)
io.use(async (socket, next) => {
const request = socket.request
try {
const url = process.env.AUTH_URL! + socket.request.url
const url = process.env.AUTH_URL! + request.url
const session = await getSession(
new Request(url, {
headers: socket.request.headers as Record<string, string>,
headers: request.headers as Record<string, string>,
}),
authOptions,
)
@ -53,10 +54,10 @@ export async function GET({
const game = await getRunningGameToUser(socket.data.user?.id ?? "")
if (!game) {
logging(
"Forbidden, no game found: " +
JSON.stringify(Array.from(socket.rooms)),
"Authentication forbidden, no game found for user: " +
JSON.stringify([socket.data.user?.id, session.user]),
["debug"],
socket.request,
request,
)
return next(new Error(status["403"]))
}
@ -76,7 +77,7 @@ export async function GET({
next()
} catch {
logging("Unkonwn error - " + status["401"], ["warn"], socket.request)
logging("Unkonwn error - " + status["401"], ["warn"], request)
next(new Error(status["401"]))
}
})
@ -87,7 +88,7 @@ export async function GET({
", " +
socket.id.cyan,
["infoGreen"],
socket.request,
request,
)
socket.on("update", async (cb) => {
@ -224,8 +225,6 @@ export async function GET({
})
.where(eq(games.id, socket.data.gameId))
io.to(socket.data.gameId).emit("gameState", newState)
if (newState === "running")
io.to(socket.data.gameId).emit("activeIndex", 0)
})
socket.on("ships", async (shipsData) => {
@ -280,18 +279,14 @@ export async function GET({
.values({ ...props, id: createId(), user_game_id: user_Game.id })
.returning()
const game = user_Game.game
const l1 = game.users[0].moves.length
const l2 = game.users[1].moves.length
io.to(socket.data.gameId).emit("dispatchMove", props, socket.data.index)
io.to(socket.data.gameId).emit("activeIndex", l1 > l2 ? 1 : 0)
})
socket.on("disconnecting", async () => {
logging(
"Disconnecting: " + JSON.stringify(Array.from(socket.rooms)),
["debug"],
socket.request,
request,
)
if (!socket.data.gameId) return
socket.to(socket.data.gameId).emit("playerEvent", {
@ -301,7 +296,7 @@ export async function GET({
})
socket.on("disconnect", () => {
logging("Disconnect: " + socket.id, ["debug"], socket.request)
logging("Disconnect: " + socket.id, ["debug"], request)
})
})
}