I'm trying to follow the TicTacToe tutorial using Typescript, and when I try to use this.props.moves.clickCell(id);
I get This expression is not callable.
Not all constituents of type 'Move<GameInterface, Ctx>' are callable.
Type 'LongFormMove<GameInterface, Ctx>' has no call signatures.ts(2349)
.
Here is my code in Board.tsx:
interface BoardProps {
G: GameInterface;
ctx: Ctx;
moves: MoveMap<GameInterface, Ctx>;
playerID: PlayerID;
isActive: boolean;
}
export default class TicTacToeBoard extends React.Component<BoardProps, {}> {
onClick = (id: number) => () => {
this.props.moves.clickCell(id);
Here is the GameInterface in game.ts
export interface GameInterface {
players: { [key: string]: Player };
cells: Array<PlayerID>;
}
I looked at types.d.ts
and found export declare type Move<G extends any = any, CtxWithPlugins extends Ctx = Ctx> = MoveFn<G, CtxWithPlugins> | LongFormMove<G, CtxWithPlugins>;
. I saw this: boardgameio/boardgame.io#867 and looked at what the OP did with their project and they ended up making their own move dispatcher (https://github.com/dadiaogames/cspr-app/blob/master/src/Board.tsx).
Any guidance would be much appreciated!
I am a board game based on React client.
I just can't find any way to update the MatchID. I refereed to the documents.
https://github.com/boardgameio/boardgame.io/blob/master/docs/documentation/api/Client.md
This document gives an API to update the match Id with Plain JS Client , with this call
updateMatchID(id)
However I can't find anything like this for React.
Also, I would like to set the property of the match unlisted = true after the match has been started.
How do I change that attribute as well?
Any guidance would be much appreciated. Thanks
Hello, after "startplay" phase end (endIf) next phase is null, not "waitng "
const game = {
name: 'tic-tac-toe',
setup: (ctx, setupData) => {
let r=new PlayersRingWhithEmpty(3);
return {
cells: new Array(9).fill(null),
ring:r
}},
phases: {
///////////// phase
waiting: {
onBegin: (G, ctx) => {
G.cells = new Array(9).fill(null);
},
turn: {
activePlayers: { all: "iddle" },
stages: {
iddle: {
moves: {
seat:{
client: false,
move:seat
}
}
},
iddle_waiting: {
moves: {
leave:{
client: false,
move:leave
}
}
}
}
},
start:true,
next: 'startplay',
endIf: (G, ctx) => {
return getTableUsersCount(G) > 1;
},
onEnd:(G,ctx)=>{
setAllUsersInGame(G);
}
},
///////////// phase
startplay: {
endIf: (G, ctx) => {
if (IsVictory(G.cells)) {
return { next:"waiting",winner: ctx.currentPlayer };
}
if (G.cells.filter(c => c === null).length == 0) {
return { next:"waiting",draw: true };
}
},
next: 'waiting',
turn :{
activePlayers: { currentPlayer: "game", others:"game_iddle",moveLimit:1 },
stages:
{
game:{
moves:{
clickCell
}
},
game_iddle:{
moves:{
leave
}
}
},
order: {
first: (G, ctx) => 0,
next: (G, ctx) => { return (ctx.playOrderPos+1)<ctx.playOrder.length?(ctx.playOrderPos+1):0;},
playOrder: (G, ctx) => G.ring.getOrderArray(),
}
}
}
}
};
Using socketio . That code is correct?
Is there a known issue with processes leaking on the server end? Today I tried to use my boardgame.io service and it wasn't responding. I went in with cpanel and discovered that I had reached my process limit. Possibly as a result of that I couldn't run the node.js selector to try to shut down the apps and I couldn't ssh in either ("fork failed"). About the only thing I could do was to look at my process history and it seems to have gone up by 5-10 processes on those dates when I was messing with the boardgame app. Once my ISP kills some processes for me I can go in and experiment and see if it happens when I start/stop the app, or install new files, or ...?
One possibility, given the timing on some of the process increases - can this be related to when cpanel tries to put the app to sleep? It seemed to happen a few hours after my weekly call with my family who play one of the games a lot. It seems common for shared hosting plans (and heroku as well) to sleep node.js apps after a period of inactivity.
ctx.allowedMoves
but it doesn't seem to exist anymore. props.moves
seems to always be a list of all the moves described in code (even in other phases or stages). So I don't know how to check if it's allowed before I call it, and as such always end up with something like ERROR: disallowed move: playCardMove
in the console.
endTurn
from the phase onBegin hook? My game is a card game where the first phase is everyone playing more or less simultaneously doing some hand management. The second phase should then start where the first player is the player who has a specific card. I'm ignoring ctx.currentPlayer for the first phase (because it's simultaneous play). Then I call ctx.events.endPhase()
to go to the primary play phase. I have code in phase.onBegin
that detects who has the special card and calls ctx.events.endTurn({ next: firstPlayer });
, but it seems like it's not doing anything. The currentPlayer is always set to player 1 (not player 0, which is also interested). Does anyone have any ideas how I can handle this?
I'm trying to understand what I'm doing wrong. When I callctx.events.endTurn({next: nextPlayerID})
from a move it always goes to the next player in the turn order. Are there conditions that need to be met for it to work?
I'm using a custom turn order by calling:
order: {
...TurnOrder.DEFAULT,
first: findStartPlayer
},
Any ideas why setting the next player isn't working?
INVALID_MOVE
can't be caught on the client side, and I can't return any values from moves. Has anyone come up with a workaround? Would it be a terrible idea to set a boolean on G, listen for it in my update(state)
function, and if it's true
, do whatever I need and then call a move to clear it again?
@urschrei Not a terrible idea, but it might have some limitations (for example you could no longer use numMoves
/moveLimit
reliably). For consistency you could potentially use a custom plugin to manage this flag for you:
import { INVALID_MOVE } from 'boardgame.io/core';
InvalidMovePlugin = {
name: 'invalid-move-workaround',
fnWrap: (move) => (G, ctx, ...args) => {
const newG = move(G, ctx, ...args);
if (newG === INVALID_MOVE) {
return { ...G, madeInvalidMove: true };
}
return { ...newG, madeInvalidMove: false };
},
};
const game = {
plugins: [InvalidMovePlugin],
};
(I’m wondering if this accounts for our use of Immer, some more work might be needed to handle that.)
Hey folks, just come across boardgame.io and it looks really awesome. I'm hoping to make a multiplayer game using it for an event in June. Really looking forward to trying it out. One thing I'm trying to figure out is around the Lobby feature numPlayers and also how you would start a game after people have joined?
What I'm looking to achieve is to create matches which could have up to a max of 8 players, but have it so that whoever created the match can start the game with as many number of players have joined at that time (up to 8 max). At that point no more players can join, only watch. And once the match creator "starts the game" the game should begin for everyone.
Anyone able to provide a bit of an outline how this might be achieved? as I'm not sure if the numPlayers in a match is a fixed requirement for a game, and it's also not clear how a game begins once people have joined. Any advice would be really appreciated.
G
can have 'simple' objects (i.e. {'foo': 'bar'}
as properties? I noticed one of the issue comments said that Map
s weren't allowed, so I'm just trying to get clarity on exactly what types I can use as properties.
I'm having some odd problems publishing this on a web provider using Cpanel. Over time the account is showing an increasing number of running processes that seem to correlate to when we use the game. I'm guessing that the server tries to put the Nodejs app to sleep, but the processes remain running. Later when another game session starts, a whole new set of server processes is forked.
What makes this more curious - my family went to play the game again this weekend and we ended up with 2 matches created - one group could only see the one match and the other group could only see the other match. I used the lobby web API to inspect the matches and only the match that I created existed on the server. I'm guessing that one of the old instances that was running was serving the one group and mine was being served by the fresh service. I thought we'd be stuck - evenly reloading the page didn't kick them over to the new server, but then someone who was in the other group decided to click on the link I had posted in Zoom chat and suddenly they saw our match - so when they used "URL autocomplete" in their browser, they connected to the server instance we had been using last week, and when they clicked on the link, somehow that got them to the server that had just woken up.
Any suggestions on how to debug something like this? Could the standard Server.js code from the tutorials somehow not be recognizing the Cpanel's attempts to get it to sleep?
(node:9476) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'playerID' of undefined
at forEachClient (H:\GITHUB\azicardgame\node_modules\boardgame.io\dist\cjs\server.js:3496:24)
at clientIDs.forEach (H:\GITHUB\azicardgame\node_modules\boardgame.io\dist\cjs\server.js:3484:17)
at Set.forEach (<anonymous>)
at forEachClient (H:\GITHUB\azicardgame\node_modules\boardgame.io\dist\cjs\server.js:3482:23)
at Object.send (H:\GITHUB\azicardgame\node_modules\boardgame.io\dist\cjs\server.js:3495:9)
at Master.onSync (H:\GITHUB\azicardgame\node_modules\boardgame.io\dist\cjs\server.js:3382:27)
at process._tickCallback (internal/process/next_tick.js:68:7)
A have an error on server when testing and calling client.stop()... searching solution . On disconnect client is not deleted and master trying to send to all clients.
INVALID_MOVE
in a move, versus returning nothing (i.e. return;
)? Or rather, is there any added value in returning INVALID_MOVE
?
If I've got my log from the store (for instance:
[{"action":{"type":"MAKE_MOVE","payload":{"type":"makeBid","args":[5],"playerID":"0"}},"_stateID":0,"turn":1,"phase":"initialAuction"},{"action":{"type":"GAME_EVENT","payload":{"type":"endTurn","args":true}},"_stateID":0,"turn":1,"phase":"initialAuction","automatic":true}]
and the initial state, is there any good ways of creating a client and forcing that data into it, step by step?
I want to do that, and copy the state after each entry, so I can run some analytics on the state through the length of the game