278 lines
5.9 KiB
JavaScript
278 lines
5.9 KiB
JavaScript
/*
|
|
* Place pixel via Websocket
|
|
* Always just one pixelrequest, queue additional requests to send later
|
|
* Pixels get predicted on the client and reset if server refused
|
|
*
|
|
* TODO move stuff out of here and to actions / middleware
|
|
* clientPredictions could be in rendererHook
|
|
*
|
|
* */
|
|
import { t } from 'ttag';
|
|
import {
|
|
requestPlaceTimeout,
|
|
pAlert,
|
|
storeReceivePixelReturn,
|
|
requestPlacePixels,
|
|
} from '../store/actions';
|
|
import {
|
|
notify,
|
|
} from '../store/actions/thunks';
|
|
|
|
let pixelTimeout = null;
|
|
/*
|
|
* cache of pixels that still are to set
|
|
* [{i: i, j: j, pixels: [[offset, color],...]}, ...]
|
|
*/
|
|
let pixelQueue = [];
|
|
/*
|
|
* requests that got predicted on client and yet have to be
|
|
* received from the server
|
|
* [[i, j, offset, colorold, colornew], ...]
|
|
*/
|
|
let clientPredictions = [];
|
|
/*
|
|
* values of last request
|
|
* {i: i, j: j, pixels: [[offset, color], ...}
|
|
*/
|
|
let lastRequestValues = {};
|
|
|
|
/*
|
|
* request pixel placement from queue
|
|
*/
|
|
function requestFromQueue(store) {
|
|
if (!pixelQueue.length) {
|
|
pixelTimeout = null;
|
|
return;
|
|
}
|
|
|
|
/* timeout to warn user when Websocket is dysfunctional */
|
|
pixelTimeout = setTimeout(() => {
|
|
pixelQueue = [];
|
|
pixelTimeout = null;
|
|
store.dispatch(requestPlaceTimeout());
|
|
store.dispatch(pAlert(
|
|
t`Error :(`,
|
|
t`Didn't get an answer from pixelplanet. Maybe try to refresh?`,
|
|
'error',
|
|
));
|
|
}, 15000);
|
|
|
|
lastRequestValues = pixelQueue.shift();
|
|
const { i, j, pixels } = lastRequestValues;
|
|
store.dispatch(requestPlacePixels(i, j, pixels));
|
|
}
|
|
|
|
/*
|
|
* got pixel update from websocket
|
|
*/
|
|
export function receivePixelUpdate(
|
|
renderer,
|
|
i,
|
|
j,
|
|
offset,
|
|
color,
|
|
) {
|
|
for (let p = 0; p < clientPredictions.length; p += 1) {
|
|
const predPxl = clientPredictions[p];
|
|
if (predPxl[0] === i
|
|
&& predPxl[1] === j
|
|
&& predPxl[2] === offset
|
|
) {
|
|
if (predPxl[4] === color) {
|
|
clientPredictions.splice(p, 1);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
renderer.renderPixel(i, j, offset, color, true);
|
|
}
|
|
|
|
/*
|
|
* Revert predictions starting at given pixel
|
|
* @param i, j, offset data of the first pixel that got rejected
|
|
*/
|
|
function revertPredictionsAt(
|
|
renderer,
|
|
sI,
|
|
sJ,
|
|
sOffset,
|
|
) {
|
|
let p = 0;
|
|
while (p < clientPredictions.length) {
|
|
const predPxl = clientPredictions[p];
|
|
if (predPxl[0] === sI
|
|
&& predPxl[1] === sJ
|
|
&& predPxl[2] === sOffset
|
|
) {
|
|
break;
|
|
}
|
|
p += 1;
|
|
}
|
|
|
|
if (p >= clientPredictions.length) {
|
|
clientPredictions = [];
|
|
return;
|
|
}
|
|
|
|
while (p < clientPredictions.length) {
|
|
const [i, j, offset, color] = clientPredictions[p];
|
|
renderer.renderPixel(i, j, offset, color, false);
|
|
p += 1;
|
|
}
|
|
|
|
clientPredictions = [];
|
|
}
|
|
|
|
/*
|
|
* try to place a pixel
|
|
*/
|
|
export function tryPlacePixel(
|
|
renderer,
|
|
store,
|
|
i,
|
|
j,
|
|
offset,
|
|
color,
|
|
curColor,
|
|
) {
|
|
renderer.renderPixel(i, j, offset, color, false);
|
|
clientPredictions.push([i, j, offset, curColor, color]);
|
|
|
|
if (pixelQueue.length) {
|
|
const lastReq = pixelQueue[pixelQueue.length - 1];
|
|
const { i: lastI, j: lastJ } = lastReq;
|
|
if (i === lastI && j === lastJ) {
|
|
/* append to last request in queue if same chunk */
|
|
lastReq.pixels.push([offset, color]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
pixelQueue.push({
|
|
i,
|
|
j,
|
|
pixels: [[offset, color]],
|
|
});
|
|
|
|
if (!pixelTimeout) {
|
|
requestFromQueue(store);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* got return from pixel request
|
|
*/
|
|
export function receivePixelReturn(
|
|
store,
|
|
renderer,
|
|
args,
|
|
) {
|
|
clearTimeout(pixelTimeout);
|
|
|
|
store.dispatch(storeReceivePixelReturn(args));
|
|
|
|
const {
|
|
retCode,
|
|
coolDownSeconds,
|
|
pxlCnt,
|
|
} = args;
|
|
|
|
if (coolDownSeconds) {
|
|
store.dispatch(notify(coolDownSeconds));
|
|
}
|
|
|
|
if (retCode) {
|
|
/*
|
|
* one or more pixels didn't get set,
|
|
* revert predictions and clean queue
|
|
*/
|
|
const { i, j, pixels } = lastRequestValues;
|
|
const [offset] = pixels[pxlCnt];
|
|
revertPredictionsAt(renderer, i, j, offset);
|
|
pixelQueue = [];
|
|
}
|
|
|
|
let errorTitle = null;
|
|
let msg = null;
|
|
let type = 'error';
|
|
switch (retCode) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
errorTitle = t`Invalid Canvas`;
|
|
msg = t`This canvas doesn't exist`;
|
|
break;
|
|
case 2:
|
|
errorTitle = t`Invalid Coordinates`;
|
|
msg = t`x out of bounds`;
|
|
break;
|
|
case 3:
|
|
errorTitle = t`Invalid Coordinates`;
|
|
msg = t`y out of bounds`;
|
|
break;
|
|
case 4:
|
|
errorTitle = t`Invalid Coordinates`;
|
|
msg = t`z out of bounds`;
|
|
break;
|
|
case 5:
|
|
errorTitle = t`Wrong Color`;
|
|
msg = t`Invalid color selected`;
|
|
break;
|
|
case 6:
|
|
errorTitle = t`Just for registered Users`;
|
|
msg = t`You have to be logged in to place on this canvas`;
|
|
break;
|
|
case 7:
|
|
errorTitle = t`Place more :)`;
|
|
// eslint-disable-next-line max-len
|
|
msg = t`You can not access this canvas yet. You need to place more pixels`;
|
|
break;
|
|
case 8:
|
|
store.dispatch(notify(t`Pixel protected!`));
|
|
break;
|
|
case 9:
|
|
// pixestack used up
|
|
break;
|
|
case 10:
|
|
errorTitle = 'Captcha';
|
|
msg = t`Please prove that you are human`;
|
|
type = 'captcha';
|
|
break;
|
|
case 11:
|
|
errorTitle = t`No Proxies Allowed :(`;
|
|
msg = t`You are using a Proxy.`;
|
|
break;
|
|
case 12:
|
|
errorTitle = t`Not allowed`;
|
|
msg = t`Just the Top10 of yesterday can place here`;
|
|
break;
|
|
case 13:
|
|
errorTitle = t`You are weird`;
|
|
// eslint-disable-next-line max-len
|
|
msg = t`Server got confused by your pixels. Are you playing on multiple devices?`;
|
|
break;
|
|
case 14:
|
|
errorTitle = t`Banned`;
|
|
type = 'ban';
|
|
break;
|
|
case 15:
|
|
errorTitle = t`Range Banned`;
|
|
msg = t`Your Internet Provider is banned from playing this game`;
|
|
break;
|
|
default:
|
|
errorTitle = t`Weird`;
|
|
msg = t`Couldn't set Pixel`;
|
|
}
|
|
|
|
if (msg || errorTitle) {
|
|
store.dispatch(pAlert(
|
|
(errorTitle || t`Error ${retCode}`),
|
|
msg,
|
|
type,
|
|
));
|
|
}
|
|
|
|
requestFromQueue(store);
|
|
}
|
|
|