merge consecutive updates into a single request

This commit is contained in:
Jesse D. McDonald 2020-04-29 00:27:10 -05:00
parent bac6305cb7
commit bf301ad79a
1 changed files with 96 additions and 68 deletions

View File

@ -426,16 +426,6 @@ $(function (){
return name.slice(0, 20) + '…'; return name.slice(0, 20) + '…';
} }
const PacoSakoUUID = 'b425b812-6bdb-11ea-9414-6f946662bac3'; /* V2 & V3 releases */
function putState() {
const boardElem = $('#cb_board');
const gameId = boardElem.data('gameId');
const moves = { past: currentGame.moves, future: currentGame.redoMoves };
boardElem.data('last_state', currentGame.moves);
putMeta({ board: moves });
}
let noticeBox = null; let noticeBox = null;
function openNoticeBox(content) { function openNoticeBox(content) {
@ -465,16 +455,13 @@ $(function (){
const updateQueue = { const updateQueue = {
head: 0, /* next item to be sent */ head: 0, /* next item to be sent */
tail: 0, /* ID to assign to the next update added to the queue */ tail: 0, /* ID to assign to the next update added to the queue */
lastGameId: null,
lastModified: null,
newModified: 0,
sending: false, sending: false,
add(gameId, data, modified) { add(gameId, data, modified) {
const wasEmpty = this.head === this.tail; const wasEmpty = this.isEmpty();
data = Object.assign({}, data, { modified }); data = Object.assign({}, data, { modified });
this[this.tail] = { gameId, data, modified }; this[this.tail] = { gameId, data };
this.tail += 1; this.tail += 1;
if (!this.sending) { if (!this.sending) {
@ -484,37 +471,70 @@ $(function (){
} }
}, },
isEmpty() {
return this.head === this.tail;
},
peek() {
if (!this.isEmpty()) {
return this[this.head];
} else {
return undefined;
}
},
remove() {
if (!this.isEmpty()) {
const first = this[this.head];
delete this[this.head];
this.head += 1;
return first;
} else {
return undefined;
}
},
sendNext() { sendNext() {
const queue = this; const queue = this;
const update = queue.remove();
if (queue.head === queue.tail) { if (update === undefined) {
return; return;
} }
const update = queue[queue.head]; /* merge updates for the same game with the same baseline into a single request */
delete queue[queue.head]; let peek;
queue.head += 1; while ((peek = queue.peek()) !== undefined && peek.gameId === update.gameId
&& peek.data.modified === update.data.modified) {
if (update.gameId === queue.lastGameId && update.modified === queue.lastModified) { Object.assign(update.data, peek.data);
update.data.modified = queue.newModified; queue.remove();
} }
IO.sendUpdate(update.gameId, update.data).then((response) => { IO.sendUpdate(update.gameId, update.data).then((response) => {
queue.lastGameId = update.gameId;
if (response && response.data && 'modified' in response.data) { if (response && response.data && 'modified' in response.data) {
/* /*
* If we queued two or more updates with the same .modified (thus * If we queued two or more updates with the same .modified (thus
* based on the same server data), send the next update with the * based on the same server data), send the later update(s) with the
* new .modified assigned by the server as a result of this update. * new .modified assigned by the server as a result of this update.
*/ */
queue.lastModified = update.modified; /* original, not adjusted */ for (let i = queue.head; i !== queue.tail; i += 1) {
queue.newModified = response.data.modified; if (queue[i].gameId === update.gameId && queue[i].data.modified === update.data.modified) {
} else { queue[i].data.modified = response.data.modified;
queue.lastModified = null; }
queue.newModified = 0; }
/*
* Send future updates with the new modified time, and prevent loading
* older data from the server in case the connection is lagging.
*/
const cbBoard = $('#cb_board');
if (cbBoard.data('gameId') === update.gameId
&& cbBoard.data('modified') === update.data.modified) {
cbBoard.data('modified', response.data.modified);
}
} }
if (queue.head === queue.tail) { if (queue.isEmpty()) {
queue.sending = false; queue.sending = false;
/* close the Saving... notice*/ /* close the Saving... notice*/
noticeBox.close({ ignoreDelay: true }); noticeBox.close({ ignoreDelay: true });
@ -527,9 +547,8 @@ $(function (){
debug('update error', err); debug('update error', err);
/* additional updates are unlikely to succeed, so empty the queue */ /* additional updates are unlikely to succeed, so empty the queue */
while (queue.head < queue.tail) { while (!queue.isEmpty()) {
delete queue[queue.head]; queue.remove();
queue.head += 1;
} }
queue.sending = false; queue.sending = false;
@ -542,6 +561,14 @@ $(function (){
}, },
}; };
function putState() {
const boardElem = $('#cb_board');
const gameId = boardElem.data('gameId');
const moves = { past: currentGame.moves, future: currentGame.redoMoves };
boardElem.data('last_state', currentGame.moves);
putMeta({ board: moves });
}
function putMeta(extra) { function putMeta(extra) {
const gameId = $('#cb_board').data('gameId'); const gameId = $('#cb_board').data('gameId');
const lightName = $('#cb_light_name').val(); const lightName = $('#cb_light_name').val();
@ -606,53 +633,55 @@ $(function (){
$('#cb_dark_name').val(''); $('#cb_dark_name').val('');
cancelGameCallback = IO.onGameUpdate(newId, function(data, gameId) { cancelGameCallback = IO.onGameUpdate(newId, function(data, gameId) {
const board = data.board || { past: [], future: [] }; if (data.modified > $('#cb_board').data('modified')) {
const board = data.board || { past: [], future: [] };
try { try {
const moves = { past: [...board.past], future: [...board.future] }; const moves = { past: [...board.past], future: [...board.future] };
const oldState = { past: currentGame.moves, future: currentGame.redoMoves }; const oldState = { past: currentGame.moves, future: currentGame.redoMoves };
if (!deepEqual(moves, oldState)) { if (!deepEqual(moves, oldState)) {
debug('got board', moves); debug('got board', moves);
const newGame = new PS.Game(); const newGame = new PS.Game();
try {
for (const move of moves.past) {
newGame.replayMove(move);
}
let n = 0;
try { try {
for (const move of moves.future.slice().reverse()) { for (const move of moves.past) {
newGame.replayMove(move); newGame.replayMove(move);
n += 1; }
let n = 0;
try {
for (const move of moves.future.slice().reverse()) {
newGame.replayMove(move);
n += 1;
}
} catch (err) {
debug('Error replaying board redo state', err);
}
for (let i = 0; i < n; ++i) {
newGame.undo();
} }
} catch (err) { } catch (err) {
debug('Error replaying board redo state', err); debug('Error replaying board state', err);
} }
for (let i = 0; i < n; ++i) { setCurrentGame(newGame, newGame.moves.length > currentGame.moves.length);
newGame.undo();
}
} catch (err) {
debug('Error replaying board state', err);
} }
} catch (err) {
setCurrentGame(newGame, newGame.moves.length > currentGame.moves.length); debug('Error parsing board data', err);
} }
} catch (err) {
debug('Error parsing board data', err); const d = data || {};
$('#cb_board').data('lightName', shortenName(String(d.lightName || 'Light')));
$('#cb_board').data('darkName', shortenName(String(d.darkName || 'Dark')));
$('#cb_light_name').val(String(d.lightName || ''));
$('#cb_dark_name').val(String(d.darkName || ''));
$('#cb_board').data('modified', data.modified);
} }
const d = data || {};
$('#cb_board').data('lightName', shortenName(String(d.lightName || 'Light')));
$('#cb_board').data('darkName', shortenName(String(d.darkName || 'Dark')));
$('#cb_light_name').val(String(d.lightName || ''));
$('#cb_dark_name').val(String(d.darkName || ''));
$('#cb_board').data('modified', data.modified);
}); });
let selOpt = $('#cb_game_' + newId); let selOpt = $('#cb_game_' + newId);
@ -1104,7 +1133,6 @@ $(function (){
renderBoard, renderBoard,
putState, putState,
putMeta, putMeta,
PacoSakoUUID,
IO, IO,
}; };