pixelplanet/src/ui/placePixel.js
2022-08-15 21:37:25 +02:00

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);
}