1
0

State loading through game-order

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-03-12 01:58:16 -08:00
parent dce9297aab
commit 3224aca2b2
7 changed files with 191 additions and 66 deletions

View File

@ -11,6 +11,7 @@ const Actions = () => {
const [state, setState] = useState('lobby');
const [color, setColor] = useState(undefined);
const [player, setPlayer] = useState(undefined);
const [players, setPlayers] = useState(undefined);
const [turn, setTurn] = useState({});
const [active, setActive] = useState(0);
const [edit, setEdit] = useState(name);
@ -28,12 +29,22 @@ const Actions = () => {
}
if ('color' in data.update && data.update.color !== color) {
setColor(data.update.color);
if (players && color in players) {
setPlayer(players[color]);
} else if (player) {
setPlayer(undefined);
}
}
if ('name' in data.update && data.update.name !== edit) {
setEdit(data.update.name);
}
if ('player' in data.update) {
setPlayer(data.update.player);
if ('players' in data.update) {
setPlayers(data.update.players);
if (color in data.update.players) {
setPlayer(data.update.players[color]);
} else if (player) {
setPlayer(undefined);
}
}
if ('turn' in data.update) {
setTurn(data.update.turn);
@ -79,11 +90,10 @@ const Actions = () => {
};
const setName = (update) => {
if (!update) {
setEdit(name);
} else if (update !== name) {
if (update !== name) {
sendMessage({ type: 'player-name', name: update });
}
setEdit(name);
}
const changeNameClick = (event) => {
@ -132,6 +142,8 @@ const Actions = () => {
const inLobby = state === 'lobby',
inGame = state === 'normal',
inGameOrder = state === 'game-order',
hasGameOrderRolled = (player && player.orderRoll) ? true : false,
hasRolled = (turn && turn.roll) ? true : false,
isTurn = (turn && turn.color === color) ? true : false,
robberActions = (turn && turn.robberInAction),
@ -141,7 +153,7 @@ const Actions = () => {
return (
<Paper className="Actions">
{ edit === "" && <PlayerName name={edit} setName={setName}/> }
{ edit === "" && <PlayerName name={name} setName={setName}/> }
<div>
{ name && inLobby && <>
<Button disabled={(color && active >= 2) ? false : true } onClick={startClick}>Start game</Button>
@ -149,7 +161,12 @@ const Actions = () => {
<Button disabled={color ? true : false} onClick={changeNameClick}>Change name</Button>
</> }
{ name && !inLobby && <>
<Button disabled={robberActions || !isTurn || hasRolled || !inGame} onClick={rollClick}>Roll Dice</Button>
<Button disabled={
robberActions ||
(inGame && (!isTurn || hasRolled)) ||
(inGameOrder && hasGameOrderRolled) ||
(!inGame && !inGameOrder)
} onClick={rollClick}>Roll Dice</Button>
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled || !haveResources} onClick={tradeClick}>Trade</Button>
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled || !haveResources} onClick={buildClicked}>Build</Button>
{ turn && turn.roll === 7 && player && player.mustDiscard > 0 &&

View File

@ -1,8 +1,8 @@
import React, { useState } from "react";
import React, { useState, useContext, useMemo, useEffect, useRef } from "react";
import "./Activities.css";
import { getPlayerName } from './Common.js';
import PlayerColor from './PlayerColor.js';
import Dice from './Dice.js';
import { PlayerColor } from './PlayerColor.js';
import { Dice } from './Dice.js';
import { GlobalContext } from "./GlobalContext.js";
const Activity = ({ activity }) => {
const [animation, setAnimation] = useState('open');
@ -52,33 +52,93 @@ const Activity = ({ activity }) => {
}</>;
}
const Activities = ({ table }) => {
if (!table.game) {
const Activities = () => {
const { ws } = useContext(GlobalContext);
const [activities, setActivities] = useState([]);
const [turn, setTurn] = useState();
const [color, setColor] = useState();
const [players, setPlayers] = useState({});
const [timestamp, setTimestamp] = useState(0);
const [state, setState] = useState('');
const fields = useMemo(() => [
'activities', 'turn', 'players', 'timestamp', 'color',
'state'
], []);
const onWsMessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'game-update':
if ('state' in data.update
&& data.update.state !== state) {
setState(data.update.state);
}
if ('activities' in data.update
&& data.update.activities.length !== activities.length) {
setActivities(data.update.activities);
}
if ('turn' in data.update) {
setTurn(data.update.turn);
}
if ('players' in data.update) {
setPlayers(data.update.players);
}
if ('timestamp' in data.update
&& data.update.timestamp !== timestamp) {
setTimestamp(data.update.timestamp);
}
if ('color' in data.update && data.update.color !== color) {
setColor(data.update.color);
}
break;
default:
break;
}
};
const refWsMessage = useRef(onWsMessage);
useEffect(() => { refWsMessage.current = onWsMessage; });
useEffect(() => {
if (!ws) { return; }
const cbMessage = e => refWsMessage.current(e);
ws.addEventListener('message', cbMessage);
return () => {
ws.removeEventListener('message', cbMessage);
}
}, [ws, refWsMessage]);
useEffect(() => {
if (!ws) { return; }
ws.send(JSON.stringify({
type: 'get',
fields
}));
}, [ws, fields]);
if (!timestamp || !color) {
return <></>;
}
const
game = table.game,
isTurn = (game.turn && game.turn.color === game.color) ? true : false,
normalPlay = (game.state === 'initial-placement' || game.state === 'normal'),
mustPlaceRobber = (game.turn && !game.turn.placedRobber && game.turn.robberInAction),
placement = (game.state === 'initial-placement' || game.turn.active === 'road-building'),
placeRoad = placement && game.turn && game.turn.actions && game.turn.actions.indexOf('place-road') !== -1,
mustStealResource = game.turn && game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1;
isTurn = (turn && turn.color === color) ? true : false,
normalPlay = (state === 'initial-placement' || state === 'normal'),
mustPlaceRobber = (turn && !turn.placedRobber && turn.robberInAction),
placement = (state === 'initial-placement' || (turn && turn.active === 'road-building')),
placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1,
mustStealResource = turn && turn.actions && turn.actions.indexOf('steal-resource') !== -1,
rollForOrder = (state === 'game-order');
let discarders = [], mustDiscard = false;
for (let color in table.game.players) {
const player = table.game.players[color];
for (let key in players) {
const player = players[key];
if (!player.mustDiscard) {
continue;
}
mustDiscard = true;
const name = (game.color === color) ? 'You' : getPlayerName(table.game.sessions, color);
const name = (color === key) ? 'You' : player.name;
discarders.push(<div key={name} className="Requirement">{name} must discard <b>{player.mustDiscard}</b> cards.</div>);
}
const list = game.activities
.filter(activity => game.timestamp - activity.date < 11000)
const list = activities
.filter(activity => timestamp - activity.date < 11000)
.map(activity => {
return <Activity key={activity.date} activity={activity}/>;
});
@ -87,8 +147,13 @@ const Activities = ({ table }) => {
if (isTurn) {
who = 'You';
} else {
who = <><PlayerColor color={table.game.turn.color}/> {table.game.turn.name}</>
if (!turn || !turn.name) {
who = 'Everyone';
} else {
who = <><PlayerColor color={turn.color}/> {turn.name}</>
}
}
return (
<div className="Activities">
{ list }
@ -104,17 +169,21 @@ const Activities = ({ table }) => {
<div className="Requirement">{who} must select a player to steal from.</div>
}
{ normalPlay && mustDiscard && <> { discarders } </> }
{ !isTurn && normalPlay &&
<div>It is <PlayerColor color={table.game.turn.color}/> {table.game.turn.name}'s turn.</div>
{ rollForOrder &&
<div className="Requirement">{who} must roll for game order.</div>
}
{ isTurn && normalPlay &&
<div className="Go"><PlayerColor color={game.turn.color}/> It is your turn.</div>
{ normalPlay && mustDiscard && <> { discarders } </> }
{ !isTurn && normalPlay && turn &&
<div>It is <PlayerColor color={turn.color}/> {turn.name}'s turn.</div>
}
{ isTurn && normalPlay && turn &&
<div className="Go"><PlayerColor color={turn.color}/> It is your turn.</div>
}
</div>
);
};
export default Activities;
export {Activities};

View File

@ -32,6 +32,12 @@ body {
}
.Table .ErrorDialog .Error {
display: flex;
flex-direction: column;
padding: 0.25rem;
}
.Table .ErrorDialog .Error > div {
display: flex;
padding: 1rem;
}

View File

@ -1,6 +1,4 @@
import React, { useCallback, useState,
useReducer, useContext, useEffect,
useRef } from "react";
import React, { useState, useContext, useEffect, useRef } from "react";
import {
BrowserRouter as Router,
Route,
@ -9,9 +7,10 @@ import {
} from "react-router-dom";
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import { GlobalContext } from "./GlobalContext.js";
import { PingPong } from "./PingPong.js";
//import { PingPong } from "./PingPong.js";
import { PlayerList } from "./PlayerList.js";
import { Chat } from "./Chat.js";
import { MediaAgent } from "./MediaControl.js";
@ -19,15 +18,16 @@ import { Board } from "./Board.js";
import { Actions } from "./Actions.js";
import { base, gamesPath } from './Common.js';
import { GameOrder } from "./GameOrder.js";
import { Activities } from "./Activities.js";
import history from "./history.js";
import "./App.css";
const Table = () => {
const params = useParams();
const global = useContext(GlobalContext);
const [ gameId, setGameId ] = useState(params.gameId ? params.gameId : undefined);
const [ ws, setWs ] = useState(global.ws);
const [ name, setName ] = useState(global.name);
const [ ws, setWs ] = useState();
const [ name, setName ] = useState("");
const [ error, setError ] = useState(undefined);
const [ warning, setWarning ] = useState(undefined);
const [ peers, setPeers ] = useState({});
@ -35,7 +35,9 @@ const Table = () => {
const [connecting, setConnecting] = useState(undefined);
const [state, setState] = useState(undefined);
const [color, setColor] = useState(undefined);
const fields = [ 'name', 'id', 'state', 'color', 'name' ];
const [players, setPlayers] = useState(undefined);
const [player, setPlayer] = useState(undefined);
const fields = [ 'id', 'state', 'color', 'name' ];
/*
useEffect(() => {
@ -70,13 +72,17 @@ const Table = () => {
switch (data.type) {
case 'error':
console.error(`App - error`, data.error);
window.alert(data.error);
setError(data.error);
break;
case 'warning':
setWarning(`App - warning`, data.warning);
console.warn(`App - warning`, data.warning);
setWarning(data.warning);
setTimeout(() => {
if (data.warning === warning) {
console.log(`app - clearing warning`);
setWarning("");
} else {
console.log(`app - a different warning is being displayed`);
}
}, 3000);
break;
@ -96,6 +102,21 @@ const Table = () => {
setColor(data.update.color);
}
}
if ('players' in data.update) {
setPlayers(data.update.players);
if (color in data.update.players) {
if (player !== data.update.players[color]) {
setPlayer(data.update.players[color]);
}
} else {
if (player) {
setPlayer(undefined);
}
if (color) {
setColor(undefined);
}
}
}
if ('name' in data.update && data.update.name !== name) {
console.log(`App - setting name: ${data.update.name}`);
setName(data.update.name);
@ -111,6 +132,9 @@ const Table = () => {
if ('color' in data.update && data.update.color !== color) {
console.log(`App - setting color: ${color}`);
setColor(data.update.color);
if (players && players[data.update.color] !== player) {
setPlayer(players[data.update.color]);
}
}
break;
default:
@ -258,10 +282,16 @@ const Table = () => {
return <GlobalContext.Provider value={{ ws: connecting, name, gameId, peers, setPeers }}>
<MediaAgent/>
<PingPong/>
{ /* <PingPong/> */ }
<div className="Table">
<Activities/>
<div className="Game">
{ error && <div className="ErrorDialog"><Paper className="Error">{ error }</Paper></div> }
{ error && <div className="ErrorDialog">
<Paper className="Error">
<div>{ error }</div>
<Button onClick={() => { setError("")}}>dismiss</Button>
</Paper>
</div> }
{ warning && <div className="WarningDialog"><Paper className="Warning">{ warning }</Paper></div> }
<Board/>
{ color && state === 'game-order' &&

View File

@ -566,7 +566,7 @@ const Board = () => {
setGenerated(signature);
}, [
signature,
signature, generated,
setPipElements, setBorderElements, setTileElements,
setCornerElements, setRoadElements,
borderOrder, borders, pipOrder, pips, tileOrder, tiles

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useContext, useRef } from "react";
import React, { useState } from "react";
import "./PlayerName.css";
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
@ -7,10 +7,8 @@ const PlayerName = ({ name, setName }) => {
const [edit, setEdit] = useState(name);
const sendName = () => {
if (edit !== name) {
setName(edit);
}
}
const nameChange = (event) => {
setEdit(event.target.value);
@ -18,7 +16,7 @@ const PlayerName = ({ name, setName }) => {
const nameKeyPress = (event) => {
if (event.key === "Enter") {
sendName();
setName(edit);
}
}
@ -29,7 +27,7 @@ const PlayerName = ({ name, setName }) => {
onKeyPress={nameKeyPress}
label="Enter your name"
variant="outlined"
value={edit}
value={edit ? edit : name}
/>
<Button onClick={sendName}>Set</Button>
</div>

View File

@ -129,9 +129,11 @@ const processTies = (players) => {
player.orderRoll = 0;
player.order = order;
player.orderStatus = `Tied.`;
player.tied = true;
});
} else {
dice[0].order = order;
dice[0].tied = false;
dice[0].orderStatus = `Placed in ${order+1}.`;
}
order += dice.length
@ -153,6 +155,9 @@ const playerFromName = (game, name) => {
const processGameOrder = (game, player, dice) => {
if (player.orderRoll) {
return `You have already rolled for game order and are not in a tie.`;
}
player.orderRoll = dice;
const players = [];
@ -224,8 +229,7 @@ const roll = (game, session) => {
}
const dice = Math.ceil(Math.random() * 6);
addChatMessage(game, session, `${name} rolled ${dice}.`);
processGameOrder(game, player, dice);
return;
return processGameOrder(game, player, dice);
case "normal":
if (game.turn.color !== session.color) {
@ -429,7 +433,7 @@ const getSession = (game, reqSession) => {
/* If this session is not yet in the game, add it and set the player's name */
if (!(id in game.sessions)) {
game.sessions[id] = {
id: `[${id.substring(0, 10)}]`,
id: `[${id.substring(0, 8)}]`,
name: undefined,
color: undefined,
player: undefined,
@ -450,7 +454,7 @@ const getSession = (game, reqSession) => {
/* 60 minutes */
const age = Date.now() - _session.lastActive;
if (age > 60 * 60 * 1000) {
console.log(`Expiring old session ${_id}: ${age/(60 * 60 * 1000)} minutes`);
console.log(`${_session.id}: Expiring old session ${_id}: ${age/(60 * 1000)} minutes`);
delete game.sessions[_id];
if (_id in game.sessions) {
console.log('delete DID NOT WORK!');
@ -535,7 +539,7 @@ const loadGame = async (id) => {
/* Populate the 'unselected' list from the session table */
if (!game.sessions[id].color && game.sessions[id].name) {
game.unselected.push(game.sessions[id].name);
game.unselected.push(game.sessions[id]);
}
}
@ -847,13 +851,13 @@ const setPlayerName = (game, session, name) => {
game.unselected = [];
for (let id in game.sessions) {
if (!game.sessions[id].color && game.sessions[id].name) {
game.unselected.push(game.sessions[id].name);
game.unselected.push(game.sessions[id]);
}
}
sendUpdateToPlayers(game, {
players: game.players,
unselected: game.unselected,
unselected: getFilteredUnselected(game),
chat: game.chat
});
session.ws.send(JSON.stringify({
@ -927,7 +931,7 @@ const setPlayerColor = (game, session, color) => {
if (!color) {
addChatMessage(game, null,
`${session.name} is no longer ${colorToWord(session.color)}.`);
game.unselected.push(session.name);
game.unselected.push(session);
game.active = active;
if (active === 1) {
addChatMessage(game, null,
@ -935,7 +939,7 @@ const setPlayerColor = (game, session, color) => {
}
sendUpdateToPlayers(game, {
active: game.active,
unselected: game.unselected,
unselected: getFilteredUnselected(game),
players: game.players,
chat: game.chat
});
@ -972,12 +976,12 @@ const setPlayerColor = (game, session, color) => {
const unselected = [];
for (let id in game.sessions) {
if (!game.sessions[id].color && game.sessions[id].name) {
unselected.push(game.sessions[id].name);
unselected.push(game.sessions[id]);
}
}
if (unselected.length !== game.unselected.length) {
game.unselected = unselected;
update.unselected = game.unselected;
update.unselected = getFilteredUnselected(game);
}
if (game.active !== active) {
@ -2875,7 +2879,7 @@ const departLobby = (game, session, color) => {
const sendUpdateToPlayers = async (game, update) => {
const keys = Object.getOwnPropertyNames(update);
console.log(`${game.id} - sendUpdateToPlayers - ${keys.join(',')}`);
console.log(`[ all ]:${game.id} - sendUpdateToPlayers - ${keys.join(',')}`);
const message = JSON.stringify({
type: 'game-update',
update
@ -3160,7 +3164,8 @@ router.ws("/ws/:id", async (ws, req) => {
break;
}
});
console.log(`${short}:${id} - sending update: `, update);
console.log(`${short}:${id} - sending update: ` +
Object.getOwnPropertyNames(update).join(','));
message = JSON.stringify({
type: 'game-update',
update
@ -3702,9 +3707,9 @@ router.post("/", (req, res/*, next*/) => {
const game = createGame();
if (!req.session.player_id) {
req.session.player_id = crypto.randomBytes(16).toString('hex');
}
const session = getSession(game, req.session);
console.log(`${session.id}: - load game via http`);
saveGame(game);
return res.status(200).send(getFilteredGameForPlayer(game, session));
});