add an interface to monitor the states of the polling connections
This commit is contained in:
parent
2bfcc822a3
commit
3f173d70a2
|
|
@ -16,8 +16,12 @@ const meta = {
|
||||||
listeners: {},
|
listeners: {},
|
||||||
nextId: 1,
|
nextId: 1,
|
||||||
currentRequest: null,
|
currentRequest: null,
|
||||||
|
connectionState: 'initializing',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const stateListeners = {};
|
||||||
|
const stateNextId = 1;
|
||||||
|
|
||||||
/* One-time request, no caching or polling */
|
/* One-time request, no caching or polling */
|
||||||
function getGameState(gameId, retries) {
|
function getGameState(gameId, retries) {
|
||||||
if (arguments.length < 2) {
|
if (arguments.length < 2) {
|
||||||
|
|
@ -161,6 +165,70 @@ function sendUpdate(gameId, data, retries) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConnectionStateChanged(gameId, callback) {
|
||||||
|
if (arguments.length < 2) {
|
||||||
|
callback = gameId;
|
||||||
|
gameId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameId !== undefined) {
|
||||||
|
const original = callback;
|
||||||
|
callback = function(cbGameId, cbState) {
|
||||||
|
if (cbGameId === gameId) {
|
||||||
|
original(cbState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const cbId = stateNextId;
|
||||||
|
stateNextId += 1;
|
||||||
|
stateListeners[cbId] = callback;
|
||||||
|
|
||||||
|
return function clearConnectionStateChanged() {
|
||||||
|
delete stateListeners[cbId];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConnectionState(gameId) {
|
||||||
|
if (arguments.length < 1) {
|
||||||
|
return meta.connectionState;
|
||||||
|
} else {
|
||||||
|
return games[gameId] && games[gameId].connectionState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setConnectionState(game, state) {
|
||||||
|
let gameId;
|
||||||
|
|
||||||
|
if (arguments.length < 2) {
|
||||||
|
gameId = 'meta';
|
||||||
|
state = game;
|
||||||
|
game = meta;
|
||||||
|
} else if (typeof game === 'string') {
|
||||||
|
gameId = game;
|
||||||
|
game = gamePollState(game);
|
||||||
|
} else {
|
||||||
|
gameId = game.gameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state !== game.connectionState) {
|
||||||
|
if (state !== 'initializing' && state !== 'connecting' && state !== 'polling'
|
||||||
|
&& state !== 'failed' && state !== 'stopped') {
|
||||||
|
throw new TypeError(`invalid connection state: ${state}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
game.connectionState = state;
|
||||||
|
|
||||||
|
for (const cbId in stateListeners) {
|
||||||
|
try {
|
||||||
|
stateListeners[cbId](gameId, state);
|
||||||
|
} catch(err) {
|
||||||
|
console.error('uncaught exception in callback', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function gamePollState(gameId) {
|
function gamePollState(gameId) {
|
||||||
if (gameId in games === false) {
|
if (gameId in games === false) {
|
||||||
games[gameId] = {
|
games[gameId] = {
|
||||||
|
|
@ -168,6 +236,7 @@ function gamePollState(gameId) {
|
||||||
data: { gameId, modified: 0 },
|
data: { gameId, modified: 0 },
|
||||||
listeners: [],
|
listeners: [],
|
||||||
nextId: 1,
|
nextId: 1,
|
||||||
|
connectionState: 'initializing',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,15 +246,21 @@ function gamePollState(gameId) {
|
||||||
function processGame(gameId, data) {
|
function processGame(gameId, data) {
|
||||||
const game = gamePollState(gameId);
|
const game = gamePollState(gameId);
|
||||||
|
|
||||||
if (data && data.modified > game.data.modified) {
|
if (data && (game.data.modified === 0 || data.modified > game.data.modified)) {
|
||||||
|
data.gameId = data.gameId || gameId;
|
||||||
game.data = data;
|
game.data = data;
|
||||||
|
|
||||||
for (const cbId in game.listeners) {
|
for (const cbId in game.listeners) {
|
||||||
try {
|
try {
|
||||||
game.listeners[cbId](data, gameId);
|
game.listeners[cbId](JSON.parse(JSON.stringify(data)), gameId);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error('uncaught exception in callback', err);
|
console.error('uncaught exception in callback', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.modified !== 0) {
|
||||||
|
processMeta({ games: [data], modified: 0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,24 +272,31 @@ function startGamePoll(gameId, afterTime) {
|
||||||
afterTime = game.data.modified;
|
afterTime = game.data.modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (game.connectionState !== 'polling') {
|
||||||
|
if (game.connectionState !== 'failed') {
|
||||||
|
setConnectionState(game, 'connecting');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const thisRequest = game.currentRequest = $.ajax({
|
const thisRequest = game.currentRequest = $.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
url: `${API_BASE}/game/${gameId}/poll/${afterTime}`,
|
url: `${API_BASE}/game/${gameId}${!afterTime ? '' : `/poll/${afterTime}`}`,
|
||||||
cache: false,
|
cache: false,
|
||||||
timeout: LONG_TIMEOUT,
|
timeout: afterTime ? LONG_TIMEOUT : SHORT_TIMEOUT,
|
||||||
}).done((data, textStatus, jqXHR) => {
|
}).done((data, textStatus, jqXHR) => {
|
||||||
if (game.currentRequest === thisRequest) {
|
if (game.currentRequest === thisRequest) {
|
||||||
game.currentRequest = null;
|
game.currentRequest = null;
|
||||||
if (jqXHR.status == 204) {
|
if (jqXHR.status !== 204) {
|
||||||
startGamePoll(gameId, afterTime);
|
afterTime = data.modified;
|
||||||
} else {
|
|
||||||
processGame(gameId, data);
|
processGame(gameId, data);
|
||||||
startGamePoll(gameId, data.modified);
|
|
||||||
}
|
}
|
||||||
|
setConnectionState(game, 'polling');
|
||||||
|
startGamePoll(gameId, afterTime);
|
||||||
}
|
}
|
||||||
}).fail((jqXHR, textStatus, errorThrown) => {
|
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||||
if (game.currentRequest === thisRequest) {
|
if (game.currentRequest === thisRequest) {
|
||||||
|
setConnectionState(game, 'failed');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (game.currentRequest === thisRequest) {
|
if (game.currentRequest === thisRequest) {
|
||||||
game.currentRequest = null;
|
game.currentRequest = null;
|
||||||
|
|
@ -235,19 +317,28 @@ function stopGamePoll(gameId) {
|
||||||
const request = game.currentRequest;
|
const request = game.currentRequest;
|
||||||
if (request !== null) {
|
if (request !== null) {
|
||||||
game.currentRequest = null;
|
game.currentRequest = null;
|
||||||
|
setConnectionState(game, 'stopped');
|
||||||
request.abort();
|
request.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processMeta(data) {
|
function processMeta(data) {
|
||||||
meta.lastModified = data.modified;
|
if (data.modified > meta.lastModified) {
|
||||||
|
meta.lastModified = data.modified;
|
||||||
|
}
|
||||||
|
|
||||||
for (const game of data.games) {
|
for (const game of data.games) {
|
||||||
if (game.gameId in meta.games === false || game.modified > meta.games[game.gameId].modified) {
|
const gameId = game.gameId;
|
||||||
meta.games[game.gameId] = game;
|
if (gameId in meta.games === false || game.modified > meta.games[gameId].modified) {
|
||||||
|
meta.games[gameId] = game;
|
||||||
|
if (gameId in games === false || games[gameId].data.modified === 0) {
|
||||||
|
const copy = Object.assign({}, game);
|
||||||
|
copy.modified = 0;
|
||||||
|
processGame(gameId, copy);
|
||||||
|
}
|
||||||
for (const cbId in meta.listeners) {
|
for (const cbId in meta.listeners) {
|
||||||
try {
|
try {
|
||||||
meta.listeners[cbId](game, game.gameId);
|
meta.listeners[cbId](JSON.parse(JSON.stringify(game)), gameId);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error('uncaught exception in callback', err);
|
console.error('uncaught exception in callback', err);
|
||||||
}
|
}
|
||||||
|
|
@ -262,24 +353,31 @@ function startMetaPoll(afterTime) {
|
||||||
afterTime = meta.lastModified;
|
afterTime = meta.lastModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (meta.connectionState !== 'polling') {
|
||||||
|
if (meta.connectionState !== 'failed') {
|
||||||
|
setConnectionState('connecting');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const thisRequest = meta.currentRequest = $.ajax({
|
const thisRequest = meta.currentRequest = $.ajax({
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
url: `https://jessemcdonald.info/pacosako/api/games/poll/${afterTime}`,
|
url: `${API_BASE}/games${!afterTime ? '' : `/poll/${afterTime}`}`,
|
||||||
cache: false,
|
cache: false,
|
||||||
timeout: LONG_TIMEOUT,
|
timeout: afterTime ? LONG_TIMEOUT : SHORT_TIMEOUT,
|
||||||
}).done((data, textStatus, jqXHR) => {
|
}).done((data, textStatus, jqXHR) => {
|
||||||
if (meta.currentRequest === thisRequest) {
|
if (meta.currentRequest === thisRequest) {
|
||||||
meta.currentRequest = null;
|
meta.currentRequest = null;
|
||||||
if (jqXHR.status == 204) {
|
if (jqXHR.status !== 204) {
|
||||||
startMetaPoll(afterTime);
|
afterTime = data.modified;
|
||||||
} else {
|
|
||||||
processMeta(data);
|
processMeta(data);
|
||||||
startMetaPoll(data.modified);
|
|
||||||
}
|
}
|
||||||
|
setConnectionState('polling');
|
||||||
|
startMetaPoll(afterTime);
|
||||||
}
|
}
|
||||||
}).fail((jqXHR, textStatus, errorThrown) => {
|
}).fail((jqXHR, textStatus, errorThrown) => {
|
||||||
if (meta.currentRequest === thisRequest) {
|
if (meta.currentRequest === thisRequest) {
|
||||||
|
setConnectionState('failed');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (meta.currentRequest === thisRequest) {
|
if (meta.currentRequest === thisRequest) {
|
||||||
meta.currentRequest = null;
|
meta.currentRequest = null;
|
||||||
|
|
@ -295,6 +393,7 @@ function stopMetaPoll() {
|
||||||
const request = meta.currentRequest;
|
const request = meta.currentRequest;
|
||||||
if (request !== null) {
|
if (request !== null) {
|
||||||
meta.currentRequest = null;
|
meta.currentRequest = null;
|
||||||
|
setConnectionState('stopped');
|
||||||
request.abort();
|
request.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -305,6 +404,8 @@ module.exports = {
|
||||||
onGameUpdate,
|
onGameUpdate,
|
||||||
onMetaUpdate,
|
onMetaUpdate,
|
||||||
sendUpdate,
|
sendUpdate,
|
||||||
|
getConnectionState,
|
||||||
|
onConnectionStateChanged,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* vim:set expandtab sw=3 ts=8: */
|
/* vim:set expandtab sw=3 ts=8: */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue