diff --git a/package.json b/package.json index 840fe5a73b..e3ce2f4d2d 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "compression": "1.7.2", "cookie": "0.3.1", "cors": "2.8.4", + "crc-32": "^1.2.0", "css-loader": "0.28.10", "debug": "3.1.0", "deep-equal": "1.0.1", diff --git a/src/api/models/othello-game.ts b/src/api/models/othello-game.ts index ab90cffa44..788fb5cba6 100644 --- a/src/api/models/othello-game.ts +++ b/src/api/models/othello-game.ts @@ -35,6 +35,9 @@ export interface IGame { }; form1: any; form2: any; + + // ログのposを文字列としてすべて連結したもののCRC32値 + crc32: string; } /** diff --git a/src/api/stream/othello-game.ts b/src/api/stream/othello-game.ts index 888c599338..e2ecce38ea 100644 --- a/src/api/stream/othello-game.ts +++ b/src/api/stream/othello-game.ts @@ -1,5 +1,6 @@ import * as websocket from 'websocket'; import * as redis from 'redis'; +import * as CRC32 from 'crc-32'; import Game, { pack } from '../models/othello-game'; import { publishOthelloGameStream } from '../event'; import Othello from '../../common/othello/core'; @@ -50,6 +51,11 @@ export default function(request: websocket.request, connection: websocket.connec if (msg.pos == null) return; set(msg.pos); break; + + case 'check': + if (msg.crc32 == null) return; + check(msg.crc32); + break; } }); @@ -231,11 +237,12 @@ export default function(request: websocket.request, connection: websocket.connec } //#endregion - publishOthelloGameStream(gameId, 'started', await pack(gameId)); + publishOthelloGameStream(gameId, 'started', await pack(gameId, user)); }, 3000); } } + // 石を打つ async function set(pos) { const game = await Game.findOne({ _id: gameId }); @@ -278,10 +285,13 @@ export default function(request: websocket.request, connection: websocket.connec pos }; + const crc32 = CRC32.str(game.logs.map(x => x.pos.toString()).join('') + pos.toString()); + await Game.update({ _id: gameId }, { $set: { + crc32, is_ended: o.isEnded, winner_id: winner }, @@ -300,4 +310,20 @@ export default function(request: websocket.request, connection: websocket.connec }); } } + + async function check(crc32) { + const game = await Game.findOne({ _id: gameId }); + + if (!game.is_started) return; + + // 互換性のため + if (game.crc32 == null) return; + + if (crc32 !== game.crc32) { + connection.send(JSON.stringify({ + type: 'rescue', + body: await pack(game, user) + })); + } + } } diff --git a/src/web/app/common/views/components/othello.game.vue b/src/web/app/common/views/components/othello.game.vue index 77be458879..01148f193e 100644 --- a/src/web/app/common/views/components/othello.game.vue +++ b/src/web/app/common/views/components/othello.game.vue @@ -37,17 +37,20 @@