+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"discord.js": "^12.5.1",
"express": "^4.16.1",
"firebase": "^7.24.0",
"flatted": "^3.2.5",
"heroku-ssl-redirect": "^0.1.1",
"lodash": "^4.17.21",
"moment": "2.29.4",
Expand Down
11 changes: 5 additions & 6 deletions src/common/middleware/socketMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as fb from 'firebase';
import * as Flatted from 'flatted';
import { AnyAction, Dispatch, Middleware, MiddlewareAPI } from 'redux';

import * as ga from '../actions/global';
Expand Down Expand Up @@ -73,8 +72,8 @@ function socketMiddleware({ excludedActions }: SocketMiddlewareOpts): Middleware
if (socket && !action.fromServer && !excludedActions.includes(action.type)) {
// Either send the action or queue it to send later.
if (socket.readyState === WebSocket.OPEN) {
socket.send(Flatted.stringify(action));
logSocketMsg(`Sent ${Flatted.stringify(action)}.`);
socket.send(JSON.stringify(action));
logSocketMsg(`Sent ${JSON.stringify(action)}.`);
keepaliveNeeded = false;
} else {
sendQueue.push(action);
Expand All @@ -84,10 +83,10 @@ function socketMiddleware({ excludedActions }: SocketMiddlewareOpts): Middleware

function receive(event: { data: string }): void {
const msg = event.data;
const action = Flatted.parse(msg);
const action = JSON.parse(msg);

logSocketMsg(`Received ${msg}.`);
store.dispatch({...action, fromServer: true});
store.dispatch({ ...action, fromServer: true });
}

function keepalive(): void {
Expand All @@ -96,7 +95,7 @@ function socketMiddleware({ excludedActions }: SocketMiddlewareOpts): Middleware
if (socket.readyState === WebSocket.CLOSED) {
connect();
} else if (socket.readyState === WebSocket.OPEN && keepaliveNeeded) {
socket.send(Flatted.stringify(sa.keepalive()));
socket.send(JSON.stringify(sa.keepalive()));
}
}
keepaliveNeeded = true;
Expand Down
4 changes: 3 additions & 1 deletion src/common/reducers/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export function handleAction(
oldState: State,
{ type, payload }: w.Action = { type: '' }
): State {
// Uncomment to log errors when the game state is ever a recursive structure (which could break multiplayer).
// JSON.stringify(oldState);

// First, clone state and clean up any currently running animation (e.g. objects turning red because they took damage).
let state: State = cleanUpAnimations({ ...oldState });

Expand Down Expand Up @@ -117,7 +120,6 @@ export function handleAction(
}

case actions.DRAFT_CARDS: {

return g.draftCards(state, payload.player, payload.cards);
}

Expand Down
20 changes: 10 additions & 10 deletions src/common/reducers/handlers/game/board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export function setSelectedTile(state: State, playerColor: w.PlayerColor, tile:
const isCurrentPlayer = (playerColor === state.currentTurn);

if (isCurrentPlayer &&
player.target.choosing &&
player.target.possibleHexes.includes(tile) &&
(player.selectedCard !== null || state.callbackAfterTargetSelected !== null)) {
player.target.choosing &&
player.target.possibleHexes.includes(tile) &&
(player.selectedCard !== null || state.callbackAfterTargetSelected !== null)) {
// Target chosen for a queued action.
return setTargetAndExecuteQueuedAction(state, tile);
} else {
Expand Down Expand Up @@ -69,9 +69,9 @@ export function moveRobot(state: State, fromHex: w.HexId, toHex: w.HexId): State
movingRobot.movedThisTurn = true;

state = triggerSound(state, 'move.wav');
state = logAction(state, player, `moved |${movingRobot.card.id}|`, {[movingRobot.card.id]: movingRobot.card});
state = logAction(state, player, `moved |${movingRobot.card.id}|`, { [movingRobot.card.id]: movingRobot.card });
state = transportObject(state, fromHex, toHex);
state = triggerEvent(state, 'afterMove', {object: movingRobot});
state = triggerEvent(state, 'afterMove', { object: movingRobot });
state = applyAbilities(state);
state = updateOrDeleteObjectAtHex(state, movingRobot, toHex);
state = deselect(state);
Expand All @@ -87,7 +87,7 @@ export function moveObjectUsingAbility(state: State, fromHex: w.HexId, toHex: w.
if (!allObjectsOnBoard(state)[toHex]) {
const movingRobot: w.Object = allObjectsOnBoard(state)[fromHex];

state = logAction(state, null, `|${movingRobot.card.id}| was moved`, {[movingRobot.card.id]: movingRobot.card});
state = logAction(state, null, `|${movingRobot.card.id}| was moved`, { [movingRobot.card.id]: movingRobot.card });
state = transportObject(state, fromHex, toHex);
state = applyAbilities(state);
state = updateOrDeleteObjectAtHex(state, movingRobot, toHex);
Expand Down Expand Up @@ -126,7 +126,7 @@ export function attack(state: State, source: w.HexId, target: w.HexId): State {
[defender.card.id]: defender.card,
[attacker.card.id]: attacker.card
});
state.attack = {from: source, to: target};
state.attack = { from: source, to: target };
state = deselect(state);
}
}
Expand Down Expand Up @@ -158,12 +158,12 @@ export function attackComplete(state: State): State {
condition: ((t) => !t.defenderType || stringToType(t.defenderType) === defender.card.type || t.defenderType === 'allobjects'),
undergoer: defender
}, () => {
defender.mostRecentlyInCombatWith = attacker;
defender.mostRecentlyInCombatWith = attacker.id;
return dealDamageToObjectAtHex(state, attackerAttack, target, attacker, 'combat');
});

if (!hasEffect(defender, 'cannotfightback') && defenderAttack > 0) {
attacker.mostRecentlyInCombatWith = defender;
attacker.mostRecentlyInCombatWith = defender.id;
state = dealDamageToObjectAtHex(state, defenderAttack, source, defender, 'combat');
}

Expand Down Expand Up @@ -211,7 +211,7 @@ export function activateObject(state: State, abilityIdx: number, selectedHexId:
const target: w.Targetable | null = player.target.chosen ? player.target.chosen[0] : null;

tempState = triggerSound(tempState, 'event.wav');
tempState = logAction(tempState, player, logMsg, {[object.card.id]: object.card}, null, target);
tempState = logAction(tempState, player, logMsg, { [object.card.id]: object.card }, null, target);
tempState.memory = {}; // Clear any previously set memory in the state.

executeCmd(tempState, ability.cmd, object);
Expand Down
4 changes: 2 additions & 2 deletions src/common/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ interface _Object { // eslint-disable-line @typescript-eslint/naming-convention
tookDamageThisTurn?: boolean
beingDestroyed?: boolean
isDestroyed?: boolean
mostRecentlyInCombatWith?: _Object
mostRecentlyInCombatWith?: ObjectId
justPlayed?: boolean
}
export type Object = _Object;
Expand Down Expand Up @@ -432,7 +432,7 @@ export interface TriggeredAbility {
export interface Trigger {
type: string
targetFunc: ((state: GameState) => Target[]) | StringRepresentationOf<(state: GameState) => Target[]>
targets?: Targetable[]
targets?: TargetReference

cardType?: CardTypeQuery | CardTypeQuery[]
cause?: Cause
Expand Down
3 changes: 1 addition & 2 deletions src/common/util/formats.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { cloneDeep, groupBy, isString, times } from 'lodash';
import * as React from 'react';
import * as SafeJsonStringify from 'safe-json-stringify';
import * as seededRNG from 'seed-random';
import { shuffle } from 'seed-shuffle';

Expand Down Expand Up @@ -74,7 +73,7 @@ export class GameFormat {
}

if (!format) {
throw new Error(`Unknown game format: ${isString(encodedFormat) ? encodedFormat : SafeJsonStringify(encodedFormat)}`);
throw new Error(`Unknown game format: ${isString(encodedFormat) ? encodedFormat : JSON.stringify(encodedFormat)}`);
}
return format;
}
Expand Down
8 changes: 4 additions & 4 deletions src/common/util/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ export function updateOrDeleteObjectAtHex(
state = triggerEvent(state, 'afterDestroyed', { object, condition: ((t: w.Trigger) => (t.cause === cause || t.cause === 'anyevent')) });
if (cause === 'combat' && object.mostRecentlyInCombatWith) {
state = triggerEvent(state, 'afterDestroysOtherObject', {
object: object.mostRecentlyInCombatWith,
object: getObjectById(state, object.mostRecentlyInCombatWith),
condition: ((t: w.Trigger) => matchesType(object.card, t.cardType!))
});
}
Expand Down Expand Up @@ -828,12 +828,12 @@ export function triggerEvent(
if (target.object) {
state = { ...state, it: target.object };
condition = ((t: w.Trigger) =>
(t.targets as w.Object[]).map((o: w.Object) => o.id).includes(target.object!.id) && defaultCondition(t)
(t.targets as w.ObjectsTargetRef).entries.includes(target.object!.id) && defaultCondition(t)
);
} else if (target.player) {
state = { ...state, itP: currentPlayer(state) };
condition = ((t: w.Trigger) =>
(t.targets as w.PlayerInGameState[]).map((p: w.PlayerInGameState) => p.color).includes(state.currentTurn) && defaultCondition(t)
(t.targets as w.PlayersTargetRef).entries.includes(state.currentTurn) && defaultCondition(t)
);
}
if (target.undergoer) {
Expand All @@ -847,7 +847,7 @@ export function triggerEvent(
(object.triggers || new Array<w.TriggeredAbility>())
.map((t: w.TriggeredAbility) => {
// Assign t.trigger.targets (used in testing the condition) and t.object (used in executing the action).
t.trigger.targets = (executeCmd(state, t.trigger.targetFunc, object) as w.Target).entries;
t.trigger.targets = getRefToTarget(executeCmd(state, t.trigger.targetFunc, object) as w.Target);
return { ...t, object };
})
.filter((t) => t.trigger.type === triggerType && condition(t.trigger))
Expand Down
25 changes: 12 additions & 13 deletions src/server/multiplayer/socket.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Server } from 'http';

import * as Flatted from 'flatted';
import { noop } from 'lodash';
import * as SafeJsonStringify from 'safe-json-stringify';
import * as WebSocket from 'ws';
Expand Down Expand Up @@ -62,7 +61,7 @@ export default function launchWebsocketServer(server: Server, path: string): voi
}

function onMessage(clientID: m.ClientID, data: string): void {
const { type, payload }: m.Action = Flatted.parse(data);
const { type, payload }: m.Action = JSON.parse(data);

if (type !== 'ws:KEEPALIVE') {
console.log(`< ${renderMessage(data)}`);
Expand All @@ -86,13 +85,13 @@ export default function launchWebsocketServer(server: Server, path: string): voi
setUserData(clientID, payload.userData);
} else if (type === 'ws:CHAT') {
const inGame = state.getAllOpponents(clientID);
const payloadWithSender = {...payload, sender: clientID};
const payloadWithSender = { ...payload, sender: clientID };
(inGame.length > 0 ? sendMessageInGame : sendMessageInLobby)(clientID, 'ws:CHAT', payloadWithSender);
} else if (type !== 'ws:KEEPALIVE' && state.lookupGameByClient(clientID)) {
// Broadcast in-game actions if the client is a player in a game.
revealVisibleCardsInGame(state.lookupGameByClient(clientID)!, [{type, payload}, clientID]);
revealVisibleCardsInGame(state.lookupGameByClient(clientID)!, [{ type, payload }, clientID]);
sendMessageInGame(clientID, type, payload);
const { gameEnded } = state.appendGameAction(clientID, {type, payload});
const { gameEnded } = state.appendGameAction(clientID, { type, payload });
revealVisibleCardsInGame(state.lookupGameByClient(clientID)!);

// If the preceding in-game action has caused the game to end, broadcast the new lobby state
Expand All @@ -110,7 +109,7 @@ export default function launchWebsocketServer(server: Server, path: string): voi
}

function sendMessage(type: string, payload: Record<string, any> = {}, recipientIDs: m.ClientID[] | null = null): void {
const message = Flatted.stringify({type, payload});
const message = JSON.stringify({ type, payload });
state.getClientSockets(recipientIDs).forEach((socket) => {
try {
socket.send(message);
Expand All @@ -135,11 +134,11 @@ export default function launchWebsocketServer(server: Server, path: string): voi
}

function sendChat(msg: string, recipientIDs: m.ClientID[] | null = null): void {
sendMessage('ws:CHAT', {msg, sender: '[Server]'}, recipientIDs);
sendMessage('ws:CHAT', { msg, sender: '[Server]' }, recipientIDs);
}

function sendChatToLobby(msg: string): void {
sendMessage('ws:CHAT', {msg, sender: '[Server]'}, state.getAllPlayersInLobby());
sendMessage('ws:CHAT', { msg, sender: '[Server]' }, state.getAllPlayersInLobby());
}

function broadcastInfo(): void {
Expand Down Expand Up @@ -174,8 +173,8 @@ export default function launchWebsocketServer(server: Server, path: string): voi
if (game) {
const { decks, format, name, startingSeed, usernames, options } = game;

sendMessage('ws:GAME_START', {player: 'blue', format, decks, usernames, options, seed: startingSeed }, [clientID]);
sendMessage('ws:GAME_START', {player: 'orange', format, decks, usernames, options, seed: startingSeed }, [opponentID]);
sendMessage('ws:GAME_START', { player: 'blue', format, decks, usernames, options, seed: startingSeed }, [clientID]);
sendMessage('ws:GAME_START', { player: 'orange', format, decks, usernames, options, seed: startingSeed }, [opponentID]);
revealVisibleCardsInGame(game);
sendChat(`Entering game ${name} ...`, [clientID, opponentID]);
broadcastInfo();
Expand Down Expand Up @@ -206,7 +205,7 @@ export default function launchWebsocketServer(server: Server, path: string): voi
};

sendMessage('ws:GAME_START', gameStartPayload, [clientID]);
sendMessage('ws:CURRENT_STATE', {actions}, [clientID]);
sendMessage('ws:CURRENT_STATE', { actions }, [clientID]);
sendChat(`Entering game ${name} as a spectator ...`, [clientID]);
sendChat(`${state.getClientUsername(clientID)} has joined as a spectator.`, state.getAllOpponents(clientID));

Expand All @@ -233,8 +232,8 @@ export default function launchWebsocketServer(server: Server, path: string): voi
newGames.forEach((game) => {
const { decks, name, startingSeed, usernames } = game;

sendMessage('ws:GAME_START', {player: 'blue', decks, usernames, seed: startingSeed }, [game.ids.blue]);
sendMessage('ws:GAME_START', {player: 'orange', decks, usernames, seed: startingSeed }, [game.ids.orange]);
sendMessage('ws:GAME_START', { player: 'blue', decks, usernames, seed: startingSeed }, [game.ids.blue]);
sendMessage('ws:GAME_START', { player: 'orange', decks, usernames, seed: startingSeed }, [game.ids.orange]);
sendChat(`Entering game ${name} ...`, game.players);

broadcastInfo();
Expand Down
7 changes: 2 additions & 5 deletions src/server/multiplayer/util.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
// Utility methods related to the multiplayer server go here.
import * as Flatted from 'flatted';
import { truncate, without } from 'lodash';
import * as SafeJsonStringify from 'safe-json-stringify';

import * as m from './multiplayer';

const MAX_DEBUG_MSG_LENGTH = 500;

/** Given a Flatted-encoded message, safely render it as JSON, truncating the length to MAX_DEBUG_MSG_LENGTH. */
/** Truncate a string to MAX_DEBUG_MSG_LENGTH. */
export function renderMessage(msg: string): string {
const jsonMsg: string = SafeJsonStringify(Flatted.parse(msg));
return truncate(jsonMsg, { length: MAX_DEBUG_MSG_LENGTH });
return truncate(msg, { length: MAX_DEBUG_MSG_LENGTH });
}

// Returns a copy of a game with the given client
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载