This commit is contained in:
Neo 2025-03-07 23:58:16 -05:00
parent 79ef542002
commit c5c5e96427
10 changed files with 184 additions and 50 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + React + Typescript</title> <title>Inhumanity</title>
</head> </head>
<body> <body>

View File

@ -16,6 +16,6 @@ export function PlayerCard({ size, name, color, onClick, className, points}: Pla
content={name} content={name}
color={color} color={color}
onClick={onClick} onClick={onClick}
player={points ? `${points} points` : undefined} player={points != undefined ? `${points} points` : undefined}
className={[s["player-card"], className].join(" ")} /> className={[s["player-card"], className].join(" ")} />
} }

View File

@ -10,34 +10,34 @@ export function ActionScreen() {
const gameState = useGameState() const gameState = useGameState()
useEffect(() => { useEffect(() => {
console.log(gameState.winningPlayer) console.log("Game state changed: ", actionState)
}, []) }, [actionState]);
return ( return (
<div className={s["container"]}> <div className={s["container"]}>
<div className={s["top-container"]}> <div className={s["top-container"]}>
<div className={s["player-top-display"]}> <div className={s["player-top-display"]}>
<PlayerCard size={"xl"} name={"Faggot"} color={"black"}/> <PlayerCard size={"xl"} name={gameState.players.get(gameState.cardPicker)!.name} color={"black"}/>
<SizableCard size={"xl"} content={gameState.blackCard} color={"black"}/> <SizableCard size={"xl"} content={gameState.blackCard} color={"black"}/>
</div> </div>
{actionState == "reveal-winner" && <div className={s["player-top-display"]}> {actionState == "reveal-winner" && <div className={s["player-top-display"]}>
<PlayerCard size={"xl"} name={gameState.players.get(gameState.winningPlayer)!.name} color={"white"}/> <PlayerCard size={"xl"} name={gameState.winningPlayer?.name ?? "ERR"} color={"white"}/>
<SizableCard size={"xl"} content={gameState.playedWhiteCards.get(gameState.winningPlayer)!} color={"white"}/> <SizableCard size={"xl"} content={gameState.playedWhiteCards.get(gameState.winningPlayer?.uuid ?? "") ?? "ERR"} color={"white"}/>
</div>} </div>}
</div> </div>
<div className={s["what-to-do"]}>{gameHeader}</div> <div className={s["what-to-do"]}>{gameHeader}</div>
{actionState == "selecting-cards" && <div className={s["player-choosing-grid"]}> {actionState == "selecting-cards" && <div className={s["player-choosing-grid"]}>
<div> <div>
{gameState.getPlayers().slice(0, 4).map((player) => ( {gameState.getWhiteCharacters().slice(0, 4).map((player) => (
<div> <div>
<PlayerCard className={player.isThinking ? s["thinking-player"] : ""} size={"nm"} name={player.name} color={"white"}/> <PlayerCard key={player.uuid} points={player.points} className={player.isThinking ? s["thinking-player"] : ""} size={"nm"} name={player.name} color={"white"}/>
</div> </div>
))} ))}
</div> </div>
<div> <div>
{gameState.getPlayers().slice(4, 8).map((player) => ( {gameState.getWhiteCharacters().slice(4, 8).map((player) => (
<div> <div>
<PlayerCard className={player.isThinking ? s["thinking-player"] : ""} size={"nm"} name={player.name} color={"white"}/> <PlayerCard key={player.uuid} points={player.points} className={player.isThinking ? s["thinking-player"] : ""} size={"nm"} name={player.name} color={"white"}/>
</div> </div>
))} ))}
</div> </div>

View File

@ -16,12 +16,13 @@ export const useGameState = create(() => ({
cardPicker: "", cardPicker: "",
blackCard: "", blackCard: "",
playedWhiteCards: new Map<string, string>(), playedWhiteCards: new Map<string, string>(),
winningPlayer: "", winningPlayer: null as Player | null,
vip: "", vip: "",
decks: { decks: {
whites: [] as string[], whites: [] as string[],
blacks: [] as string[] blacks: [] as string[]
}, },
prevPicker: -1,
getBlackCard() { getBlackCard() {
const card = this.decks.blacks[Math.floor(Math.random() * this.decks.blacks.length)]; const card = this.decks.blacks[Math.floor(Math.random() * this.decks.blacks.length)];
@ -29,9 +30,9 @@ export const useGameState = create(() => ({
return card return card
}, },
async fetchCards() { async fetchCards() {
const blacksRes = await fetch("http://localhost:8080/api/v1/pick-card/black") const blacksRes = await fetch("https://inhumane.tv.neothedeveloper.com/api/v1/pick-card/black")
this.decks.blacks = await blacksRes.json() this.decks.blacks = await blacksRes.json()
const whitesRes = await fetch("http://localhost:8080/api/v1/pick-card/white") const whitesRes = await fetch("https://inhumane.tv.neothedeveloper.com/api/v1/pick-card/white")
this.decks.whites = await whitesRes.json() this.decks.whites = await whitesRes.json()
}, },
getWhiteCards(amount: number) { getWhiteCards(amount: number) {
@ -45,6 +46,13 @@ export const useGameState = create(() => ({
}, },
getPlayers() { getPlayers() {
return [...this.players.values()] return [...this.players.values()]
},
getWhiteCharacters() {
return [...this.players.values()].filter(c => c.uuid != this.cardPicker)
},
getNextPicker() {
this.prevPicker = (this.prevPicker + 1) % (this.players.size - 1)
return this.getPlayers()[this.prevPicker].uuid
} }
})) }))

View File

@ -4,13 +4,14 @@ import {useEffect, useState} from "react";
import {useNavigate} from "react-router"; import {useNavigate} from "react-router";
import {ActionState, GameContext, useGameState} from "./GameContext.ts"; import {ActionState, GameContext, useGameState} from "./GameContext.ts";
import {ConnectionScreen} from "./ConnectionScreen.tsx"; import {ConnectionScreen} from "./ConnectionScreen.tsx";
import useWebSocket from "react-use-websocket"; import useWebSocket, {SendMessage} from "react-use-websocket";
import {BSON} from "bson"; import {BSON} from "bson";
import {handlePacket, handlers} from "./socketHandler.ts"; import {handlePacket, handlers} from "./socketHandler.ts";
import { createPacket, GameSecretsCfg, GamePlayerJoin, GamePlayerLeave } from "../../../game-networking/src/types/socket.ts"; import { createPacket, GameSecretsCfg, GamePlayerJoin } from "../../../game-networking/src/types/socket.ts";
import {getUserSetting} from "../UserSettings.ts"; import {getUserSetting} from "../UserSettings.ts";
import {ActionScreen} from "./ActionScreen.tsx"; import {ActionScreen} from "./ActionScreen.tsx";
import {v4} from "uuid"; import {v4} from "uuid";
import {WebSocketMessage} from "react-use-websocket/dist/lib/types";
export function HumanityBoxGame() { export function HumanityBoxGame() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -56,11 +57,26 @@ export function HumanityBoxGame() {
}))) })))
}) })
function pickVip(vip: string) {
useGameState.setState(_ => ({
vip
}))
sendMessage(ser(createPacket("game.setVip", {
}, vip)))
}
const [connRelationMap, setConnRelationMap] = useState<Map<string, string>>(new Map<string, string>()) const [connRelationMap, setConnRelationMap] = useState<Map<string, string>>(new Map<string, string>())
handlers.set("game.playerJoin", (data: GamePlayerJoin, send, conn) => { handlers.set("game.playerJoin", (data: GamePlayerJoin, send, conn) => {
if (!conn) return; if (!conn) return;
if (gameState.getPlayers().findIndex((p) => p.name == data.playerName) != -1) {
send(ser(createPacket("game.forceQuit", {
reason: "Name in use"
})))
return
}
if (gameState.players.size < getUserSetting("MaxPlayers")) { if (gameState.players.size < getUserSetting("MaxPlayers")) {
const player = { const player = {
name: data.playerName, name: data.playerName,
@ -74,9 +90,22 @@ export function HumanityBoxGame() {
return new Map(relMap).set(conn, player.uuid) return new Map(relMap).set(conn, player.uuid)
}) })
useGameState.setState((state) => ({ if (gameState.players.size <= 0 ) {
players: new Map(state.players).set(player.uuid, player) pickVip(conn)
})) }
useGameState.setState((state) => {
state.players = new Map(state.players).set(player.uuid, player)
send(ser(createPacket("game.updatePlayers", {
players: state.getPlayers().map((p) => ({
name: p.name,
uuid: p.uuid
}))
})))
return state
})
send(ser(createPacket("game.playerJoin", player, conn))) send(ser(createPacket("game.playerJoin", player, conn)))
} else { } else {
@ -86,28 +115,59 @@ export function HumanityBoxGame() {
} }
}) })
handlers.set("game.playerLeave", (data: GamePlayerLeave, _, conn) => { handlers.set("game.playerLeave", (_1, send, conn) => {
const uuid = connRelationMap.get(conn!)! const uuid = connRelationMap.get(conn!)!
connRelationMap.delete(conn!)
useGameState.setState((state) => { useGameState.setState((state) => {
let players = new Map(state.players) let players = new Map(state.players)
players.delete(uuid) players.delete(uuid)
state.players = players state.players = players
if (state.vip == data.uuid) { if (state.vip == conn) {
if (state.players.size > 0) { if (state.players.size > 0) {
state.vip = [...gameState.players.values()][0].uuid state.vip = [...connRelationMap.keys()][0]
pickVip(state.vip)
} else { } else {
state.vip = "" state.vip = ""
} }
} }
if (state.players.size < 3) {
setScreenState("join-lobby")
setActionState("showing-black")
} else if (state.cardPicker == uuid) {
chooseBlackCardAndPicker(send)
}
return {...state} return {...state}
}) })
}) })
handlers.set("game.action.playCard", (data: { cardValue: string }, _, conn) => { useEffect(() => {
if (screenState == "join-lobby") {
sendMessage(ser(createPacket("game.setScreen", {
screen: "waiting-for-players"
})))
}
}, [screenState]);
function connFromUuid(uuid: string) {
for (const [_conn, _uuid] of [...connRelationMap.entries()]) {
console.log(_conn, uuid, _uuid)
if (uuid == _uuid) {
return _conn
}
}
}
handlers.set("game.action.playCard", (data: { cardValue: string }, send, conn) => {
if (!conn) return; if (!conn) return;
const uuid = connRelationMap.get(conn!)! const uuid = connRelationMap.get(conn!)!
if (gameState.cardPicker == uuid) {
console.log("black card player attempted to play card")
return;
}
if (!gameState.players.get(uuid)!.availableCards.includes(data.cardValue)) { if (!gameState.players.get(uuid)!.availableCards.includes(data.cardValue)) {
console.log("player attempted to play card that they didn't have") console.log("player attempted to play card that they didn't have")
return; return;
@ -116,43 +176,88 @@ export function HumanityBoxGame() {
useGameState.setState((state) => { useGameState.setState((state) => {
state.players.get(uuid)!.isThinking = false state.players.get(uuid)!.isThinking = false
state.playedWhiteCards.set(uuid, data.cardValue) state.playedWhiteCards.set(uuid, data.cardValue)
// TODO: Make this minus 1 or it wont ever go next. dev only
if (state.playedWhiteCards.size >= state.players.size) { const player = state.players.get(uuid)!
player.availableCards = player.availableCards.filter(c => c != data.cardValue)
player.availableCards.push(state.getWhiteCards(1)[0])
console.log("player got new card:", player.availableCards[6])
send(ser(createPacket("game.updateCards", {
cards: player.availableCards
}, conn)))
if (state.playedWhiteCards.size >= (state.players.size - 1)) {
setActionState("picking-card") setActionState("picking-card")
const state = useGameState.getState()
state.players.forEach((player) => {
if (player.uuid == state.cardPicker) {
send(ser(createPacket("game.setScreen", {
screen: "picking-card",
cards: state.playedWhiteCards
}, connFromUuid(player.uuid))))
} else {
send(ser(createPacket("game.setScreen", {
screen: "waiting-for-picker"
}, connFromUuid(player.uuid))))
}
})
} }
return {...state} return {...state}
}) })
}) })
handlers.set("game.action.pickCard", (data: { uuid: string, picked: string }) => { handlers.set("game.action.pickCard", (data: { uuid: string }, send, conn) => {
if (connRelationMap.get(conn!)! != gameState.cardPicker) {
return
}
useGameState.setState((state) => { useGameState.setState((state) => {
state.winningPlayer = data.picked const player = state.players.get(data.uuid)!
state.players.get(data.uuid)!.points++ state.winningPlayer = player
state.winningPlayer = "" player!.points = player.points + 1
state.playedWhiteCards.clear()
setActionState("reveal-winner") setActionState("reveal-winner")
send(ser(createPacket("game.setScreen", {
screen: "reveal-winner",
})))
setTimeout(() => { setTimeout(() => {
chooseBlackCardAndPicker() chooseBlackCardAndPicker(send)
}, 5000) }, 5000)
return {...state} return {...state}
}) })
}) })
function chooseBlackCardAndPicker() { function chooseBlackCardAndPicker(send: SendMessage) {
setActionState("showing-black") setActionState("showing-black")
setGameHeader("") setGameHeader("")
useGameState.setState((state) => ({ useGameState.setState((state) => ({
playedWhiteCards: new Map(),
blackCard: state.getBlackCard(), blackCard: state.getBlackCard(),
cardPicker: state.getNextPicker()
})) }))
send(ser(createPacket("game.setScreen", {
screen: "reading-black-card"
})))
setTimeout(() => { setTimeout(() => {
beginSelectCardsScreen() beginSelectCardsScreen(send)
}, 4000) }, 4000)
} }
function beginSelectCardsScreen() { function beginSelectCardsScreen(send: SendMessage) {
setActionState("selecting-cards") setActionState("selecting-cards")
const state = useGameState.getState()
console.log(state)
state.players.forEach((player) => {
if (player.uuid != state.cardPicker) {
send(ser(createPacket("game.setScreen", {
screen: "playing-card"
}, connFromUuid(player.uuid))))
} else {
send(ser(createPacket("game.setScreen", {
screen: "waiting-for-cards"
}, connFromUuid(player.uuid))))
}
})
useGameState.setState((state) => { useGameState.setState((state) => {
state.players.forEach((player) => { state.players.forEach((player) => {
player.isThinking = true player.isThinking = true
@ -162,31 +267,48 @@ export function HumanityBoxGame() {
} }
handlers.set("vip.startGame", () => { useEffect(() => {
if (gameState.players.size >= 1) { console.log(actionState)
}, [actionState]);
handlers.set("vip.startGame", (_1, send, conn) => {
if (gameState.vip != conn) {
return
}
if (gameState.players.size >= 3) {
setScreenState("action-screen") setScreenState("action-screen")
chooseBlackCardAndPicker() chooseBlackCardAndPicker(send)
} }
}); });
const { sendMessage, lastMessage } = useWebSocket("http://localhost:8080/live/start-game", { const { sendMessage, lastMessage } = useWebSocket("https://inhumane.tv.neothedeveloper.com/live/start-game", {
reconnectAttempts: 5, reconnectAttempts: 5,
retryOnError: true, retryOnError: true,
reconnectInterval: (lastAttemptNumber) => lastAttemptNumber * 6000 reconnectInterval: (lastAttemptNumber) => lastAttemptNumber * 6000
}) })
async function handleMessage(msg: MessageEvent | null) { async function handleMessage(msg: MessageEvent | null, send: SendMessage) {
if (msg) { if (msg) {
let data = await (msg.data as Blob).arrayBuffer(); let data = await (msg.data as Blob).arrayBuffer();
console.log(`Last Message Received:`, BSON.deserialize(new Uint8Array(data))); console.log(`Last Message Received:`, BSON.deserialize(new Uint8Array(data)));
handlePacket(BSON.deserialize(new Uint8Array(data)), sendMessage) handlePacket(BSON.deserialize(new Uint8Array(data)), (message: WebSocketMessage, keep?: boolean) => {
console.log("Sending message:", BSON.deserialize(new Uint8Array(data)));
send(message, keep)
})
} }
} }
useEffect(() => { useEffect(() => {
handleMessage(lastMessage) handleMessage(lastMessage, sendMessage)
}, [lastMessage]); }, [lastMessage]);
useEffect(() => {
return () => {
navigate("/")
}
}, []);
return ( return (
<GameContext.Provider value={{ <GameContext.Provider value={{
gameCode, gameCode,

View File

@ -17,7 +17,7 @@
height: 100%; height: 100%;
&::after { &::after {
content: "humanitybox.tv"; content: "inhumane.tv.neothedeveloper.com";
font-size: 2rem; font-size: 2rem;
font-weight: 900; font-weight: 900;
left: 0; left: 0;

View File

@ -20,10 +20,10 @@ export default function HumanityBoxGameCode() {
<div className={s["grid-container"]}> <div className={s["grid-container"]}>
<div className={s["players-grid"]}> <div className={s["players-grid"]}>
{[...gameState.players.values()].map((p) => { {[...gameState.players.values()].map((p) => {
return <PlayerCard className={s["card-flip"]} size={"xl"} name={p.name} color={"white"}/> return <PlayerCard key={p.uuid} className={s["card-flip"]} size={"xl"} name={p.name} color={"white"}/>
})} })}
{new Array<boolean>(getUserSetting("MaxPlayers")).fill(false, 0, 8-gameState.players.size).map(() => { {new Array<boolean>(getUserSetting("MaxPlayers")).fill(false, 0, 8-gameState.players.size).map((_, i) => {
return <SizableCard className={[s["join-card"], s["player-card"]].join(" ")} size={"xl"} content={"Join"} color={"white"}/> return <SizableCard key={i} className={[s["join-card"], s["player-card"]].join(" ")} size={"xl"} content={"Join"} color={"white"}/>
})} })}
</div> </div>
</div> </div>

View File

@ -1,14 +1,20 @@
import SizableCard from "../components/cards/SizableCard.tsx"; import SizableCard from "../components/cards/SizableCard.tsx";
import s from "./HumanityBoxHome.module.scss" import s from "./HumanityBoxHome.module.scss"
import {invoke} from "@tauri-apps/api"; import {invoke} from "@tauri-apps/api";
import {useState} from "react"; import {useEffect, useState} from "react";
import {Settings} from "../popups/SettingsPopup.tsx"; import {Settings} from "../popups/SettingsPopup.tsx";
import {useNavigate} from "react-router"; import {useNavigate} from "react-router";
import {useGameState} from "./GameContext.ts";
export default function HumanityBoxHome() { export default function HumanityBoxHome() {
const [settingsVisible, setSettingsVisible] = useState<boolean>(false) const [settingsVisible, setSettingsVisible] = useState<boolean>(false)
const [exiting, setExiting] = useState<boolean>(false) const [exiting, setExiting] = useState<boolean>(false)
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => {
useGameState.setState(useGameState.getInitialState)
}, [])
return ( return (
<div className={s["home"]} data-exiting={exiting}> <div className={s["home"]} data-exiting={exiting}>
<div className={s["settings-container"]}> <div className={s["settings-container"]}>

View File

@ -10,7 +10,7 @@ type Packet = {
} }
export function handlePacket(data: Packet, send: SendMessage) { export function handlePacket(data: Packet, send: SendMessage) {
if (data.op && data.data) { if (data.op) {
let handler: HandlerFunc | undefined; let handler: HandlerFunc | undefined;
if ((handler = handlers.get(data.op)) != undefined) { if ((handler = handlers.get(data.op)) != undefined) {
handler(data.data, send, data.conn); handler(data.data, send, data.conn);

View File

@ -4,5 +4,3 @@ import App from "./App";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<App /> <App />
); );
document.title = "Inhumane"