refactor pixel update socket client

update translation templates
fix #16
This commit is contained in:
HF 2022-10-03 21:32:19 +02:00
parent e9952134a5
commit 88d0ba6496
18 changed files with 927 additions and 901 deletions

View File

@ -3,244 +3,116 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Plural-Forms: nplurals=2; plural=(n!=1);\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n"
#: src/core/ChatProvider.js:390 #: src/core/ChatProvider.js:434
msgid "You can not send chat messages with proxy" msgid "You can not send chat messages with proxy"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:392 #: src/core/ChatProvider.js:436
msgid "You are banned" msgid "Your country is temporary muted from this chat channel"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:394 #: src/core/ChatProvider.js:439
msgid "Your Internet Provider is banned"
msgstr ""
#: src/core/ChatProvider.js:396
msgid "You are not allowed to use chat"
msgstr ""
#: src/core/ChatProvider.js:411
#, javascript-format
msgid "You are sending messages too fast, you have to wait ${ waitTime }s :("
msgstr ""
#: src/core/ChatProvider.js:415
msgid "You don't have access to this channel"
msgstr ""
#: src/core/ChatProvider.js:433
msgid "Your mail has to be verified in order to chat"
msgstr ""
#: src/core/ChatProvider.js:438
msgid "You are permanently muted, join our guilded to apppeal the mute" msgid "You are permanently muted, join our guilded to apppeal the mute"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:441
msgid "You are banned"
msgstr ""
#: src/core/ChatProvider.js:443 #: src/core/ChatProvider.js:443
msgid "Your Internet Provider is banned"
msgstr ""
#: src/core/ChatProvider.js:448
#, javascript-format #, javascript-format
msgid "You are muted for another ${ timeMin } minutes" msgid "You are muted for another ${ timeMin } minutes"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:445 #: src/core/ChatProvider.js:450
msgid "You are muted for another ${ muted } seconds" msgid "You are muted for another ${ ttl } seconds"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:453 #: src/core/ChatProvider.js:467
msgid "Ow no! Spam protection decided to mute you" #, javascript-format
msgid "You are sending messages too fast, you have to wait ${ waitTime }s :("
msgstr "" msgstr ""
#: src/core/ChatProvider.js:464 #: src/core/ChatProvider.js:471
msgid "You don't have access to this channel"
msgstr ""
#: src/core/ChatProvider.js:488
msgid "Your mail has to be verified in order to chat"
msgstr ""
#: src/core/ChatProvider.js:498
msgid "You can't send a message this long :(" msgid "You can't send a message this long :("
msgstr "" msgstr ""
#: src/core/ChatProvider.js:468 #: src/core/ChatProvider.js:502
msgid "Please use int channel" msgid "Please use int channel"
msgstr "" msgstr ""
#: src/core/ChatProvider.js:475 #: src/core/ChatProvider.js:510
msgid "Your country is temporary muted from this chat channel"
msgstr ""
#: src/core/ChatProvider.js:483
msgid "Stop flooding." msgid "Stop flooding."
msgstr "" msgstr ""
#: src/routes/reset_password.js:37 #: src/routes/reset_password.js:39
msgid "You sent an empty password or invalid data :(" msgid "You sent an empty password or invalid data :("
msgstr "" msgstr ""
#: src/routes/reset_password.js:49 #: src/routes/reset_password.js:51
msgid "This password-reset link isn't valid anymore :(" msgid "This password-reset link isn't valid anymore :("
msgstr "" msgstr ""
#: src/routes/reset_password.js:60 #: src/routes/reset_password.js:62
msgid "Your passwords do not match :(" msgid "Your passwords do not match :("
msgstr "" msgstr ""
#: src/routes/reset_password.js:75 #: src/routes/reset_password.js:77
msgid "User doesn't exist in our database :(" msgid "User doesn't exist in our database :("
msgstr "" msgstr ""
#: src/routes/reset_password.js:87 #: src/routes/reset_password.js:89
msgid "Passowrd successfully changed." msgid "Passowrd successfully changed."
msgstr "" msgstr ""
#: src/routes/reset_password.js:106 #: src/routes/reset_password.js:108
msgid "Invalid url :( Please check your mail again." msgid "Invalid url :( Please check your mail again."
msgstr "" msgstr ""
#: src/routes/reset_password.js:119 #: src/ssr/PopUp.jsx:58
msgid "" msgid "ppfun"
"This passwort reset link is wrong or already expired, please request a new "
"one (Note: you can use those links just once)"
msgstr "" msgstr ""
#: src/ssr/Globe.jsx:33 #: src/ssr/PopUp.jsx:59
msgid "PixelPlanet.Fun 3DGlobe"
msgstr ""
#: src/ssr/Globe.jsx:34
msgid "A 3D globe of our whole map"
msgstr ""
#: src/ssr/Globe.jsx:47
msgid "Double click on globe to go back."
msgstr ""
#: src/ssr/Globe.jsx:48
msgid "Loading..."
msgstr ""
#: src/ssr/Win.jsx:55
#: src/ssr/Win.jsx:56
msgid "PixelPlanet.Fun PopUp" msgid "PixelPlanet.Fun PopUp"
msgstr "" msgstr ""
#: src/ssr/Main.jsx:54 #: src/ssr/Globe.jsx:32
msgid "PixelPlanet.Fun 3DGlobe"
msgstr ""
#: src/ssr/Globe.jsx:33
msgid "A 3D globe of our whole map"
msgstr ""
#: src/ssr/Globe.jsx:46
msgid "Double click on globe to go back."
msgstr ""
#: src/ssr/Globe.jsx:47
msgid "Loading..."
msgstr ""
#: src/ssr/Main.jsx:59
msgid "PixelPlanet.Fun" msgid "PixelPlanet.Fun"
msgstr "" msgstr ""
#: src/ssr/Main.jsx:55 #: src/ssr/Main.jsx:60
msgid "Place color pixels on an map styled canvas with other players online" msgid "Place color pixels on an map styled canvas with other players online"
msgstr "" msgstr ""
#: src/core/mail.js:71
#, javascript-format
msgid ""
"We already sent you a verification mail, you can request another one in ${ "
"minLeft } minutes."
msgstr ""
#: src/core/mail.js:78
msgid "Welcome ${ name } to PixelPlanet, plese verify your mail"
msgstr ""
#: src/core/mail.js:79
#, javascript-format
msgid "Hello ${ name }"
msgstr ""
#: src/core/mail.js:80
msgid ""
"welcome to our little community of pixelplacers, to use your account, you "
"have to verify your mail. You can do that here: "
msgstr ""
#: src/core/mail.js:80
msgid "Click to Verify"
msgstr ""
#: src/core/mail.js:80
#: src/core/mail.js:127
msgid "Or by copying following url:"
msgstr ""
#: src/core/mail.js:81
msgid "Have fun and don't hesitate to contact us if you encouter any problems :)"
msgstr ""
#: src/core/mail.js:82
#: src/core/mail.js:129
msgid "Thanks"
msgstr ""
#: src/core/mail.js:92
msgid "Mail is not configured on the server"
msgstr ""
#: src/core/mail.js:101
msgid ""
"We already sent you a mail with instructions. Please wait before requesting "
"another mail."
msgstr ""
#: src/core/mail.js:109
msgid "Couldn't find this mail in our database"
msgstr ""
#: src/core/mail.js:125
msgid "You forgot your password for PixelPlanet? Get a new one here"
msgstr ""
#: src/core/mail.js:126
msgid "Hello"
msgstr ""
#: src/core/mail.js:127
msgid ""
"You requested to get a new password. You can change your password within "
"the next 30min here: "
msgstr ""
#: src/core/mail.js:127
#: src/ssr/PasswordReset.jsx:28
#: src/ssr/PasswordReset.jsx:49
msgid "Reset Password"
msgstr ""
#: src/core/mail.js:128
#, javascript-format
msgid ""
"If you did not request this mail, please just ignore it (the ip that "
"requested this mail was ${ ip })."
msgstr ""
#: src/ssr/PasswordReset.jsx:20
#: src/ssr/PasswordReset.jsx:40
msgid "PixelPlanet.fun Password Reset"
msgstr ""
#: src/ssr/PasswordReset.jsx:21
#: src/ssr/PasswordReset.jsx:41
msgid "Reset your password here"
msgstr ""
#: src/ssr/PasswordReset.jsx:30
#: src/ssr/RedirectionPage.jsx:12
msgid "Click here"
msgstr ""
#: src/ssr/PasswordReset.jsx:30
msgid "to go back to pixelplanet"
msgstr ""
#: src/ssr/PasswordReset.jsx:50
#, javascript-format
msgid "Hello ${ name }, you can set your new password here:"
msgstr ""
#: src/ssr/PasswordReset.jsx:54
msgid "New Password"
msgstr ""
#: src/ssr/PasswordReset.jsx:60
msgid "Confirm New Password"
msgstr ""
#: src/ssr/PasswordReset.jsx:64
msgid "Submit"
msgstr ""
#: src/utils/validation.js:17 #: src/utils/validation.js:17
msgid "Email can't be empty." msgid "Email can't be empty."
msgstr "" msgstr ""
@ -289,30 +161,72 @@ msgstr ""
msgid "Password must be shorter than 60 characters." msgid "Password must be shorter than 60 characters."
msgstr "" msgstr ""
#: src/routes/api/captcha.js:20 #: src/ssr/PasswordReset.jsx:20
msgid "No captcha text given" #: src/ssr/PasswordReset.jsx:40
msgid "PixelPlanet.fun Password Reset"
msgstr "" msgstr ""
#: src/routes/api/captcha.js:25 #: src/ssr/PasswordReset.jsx:21
msgid "No captcha id given" #: src/ssr/PasswordReset.jsx:41
msgid "Reset your password here"
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:54 #: src/core/MailProvider.js:105
#: src/routes/api/captcha.js:44 #: src/ssr/PasswordReset.jsx:28
#: src/ssr/PasswordReset.jsx:49
msgid "Reset Password"
msgstr ""
#: src/ssr/PasswordReset.jsx:30
#: src/ssr/RedirectionPage.jsx:12
msgid "Click here"
msgstr ""
#: src/ssr/PasswordReset.jsx:30
msgid "to go back to pixelplanet"
msgstr ""
#: src/ssr/PasswordReset.jsx:50
#, javascript-format
msgid "Hello ${ name }, you can set your new password here:"
msgstr ""
#: src/ssr/PasswordReset.jsx:54
msgid "New Password"
msgstr ""
#: src/ssr/PasswordReset.jsx:60
msgid "Confirm New Password"
msgstr ""
#: src/ssr/PasswordReset.jsx:65
msgid "Submit"
msgstr ""
#: src/routes/api/auth/register.js:59
#: src/routes/api/captcha.js:34
msgid "You took too long, try again." msgid "You took too long, try again."
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:57 #: src/routes/api/auth/register.js:62
#: src/routes/api/captcha.js:50 #: src/routes/api/captcha.js:39
msgid "You failed your captcha" msgid "You failed your captcha"
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:60 #: src/routes/api/captcha.js:43
#: src/routes/api/captcha.js:56 msgid "No captcha text given"
msgstr ""
#: src/routes/api/captcha.js:47
msgid "No captcha id given"
msgstr ""
#: src/routes/api/auth/register.js:65
#: src/routes/api/captcha.js:51
msgid "Unknown Captcha Error" msgid "Unknown Captcha Error"
msgstr "" msgstr ""
#: src/routes/api/captcha.js:63 #: src/routes/api/captcha.js:57
msgid "Server error occured" msgid "Server error occured"
msgstr "" msgstr ""
@ -328,66 +242,71 @@ msgstr ""
msgid "Just admins can do that" msgid "Just admins can do that"
msgstr "" msgstr ""
#: src/routes/api/baninfo.js:24 #: src/routes/api/baninfo.js:32
msgid "You are not banned" msgid "You are not banned"
msgstr "" msgstr ""
#: src/routes/api/auth/verify.js:22
#: src/routes/api/auth/verify.js:29
msgid "Mail verification"
msgstr ""
#: src/routes/api/auth/verify.js:23
msgid "You are now verified :)"
msgstr ""
#: src/routes/api/auth/verify.js:29
msgid ""
"Your mail verification code is invalid or already expired :(, please "
"request a new one."
msgstr ""
#: src/routes/api/auth/logout.js:11 #: src/routes/api/auth/logout.js:11
msgid "You are not even logged in." msgid "You are not even logged in."
msgstr "" msgstr ""
#: src/routes/api/auth/delete_account.js:54 #: src/routes/api/auth/delete_account.js:55
#: src/routes/api/auth/logout.js:20 #: src/routes/api/auth/logout.js:20
msgid "Server error when logging out." msgid "Server error when logging out."
msgstr "" msgstr ""
#: src/routes/api/auth/change_mail.js:38 #: src/routes/api/auth/change_mail.js:43
#: src/routes/api/auth/change_passwd.js:34 #: src/routes/api/auth/change_passwd.js:34
#: src/routes/api/auth/delete_account.js:34 #: src/routes/api/auth/delete_account.js:35
msgid "You are not authenticated." msgid "You are not authenticated."
msgstr "" msgstr ""
#: src/routes/api/auth/change_mail.js:47 #: src/routes/api/auth/change_mail.js:52
#: src/routes/api/auth/change_passwd.js:43 #: src/routes/api/auth/change_passwd.js:43
#: src/routes/api/auth/delete_account.js:44 #: src/routes/api/auth/delete_account.js:45
msgid "Incorrect password!" msgid "Incorrect password!"
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:26 #: src/routes/api/auth/change_mail.js:21
msgid "No Captcha given" #: src/routes/api/auth/register.js:24
msgstr "" msgid "This email provider is not allowed"
#: src/routes/api/auth/register.js:29
msgid "E-Mail already in use."
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:31 #: src/routes/api/auth/register.js:31
msgid "No Captcha given"
msgstr ""
#: src/routes/api/auth/register.js:34
msgid "E-Mail already in use."
msgstr ""
#: src/routes/api/auth/register.js:36
msgid "Username already in use." msgid "Username already in use."
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:84 #: src/routes/api/auth/register.js:89
msgid "Failed to create new user :(" msgid "Failed to create new user :("
msgstr "" msgstr ""
#: src/routes/api/auth/register.js:100 #: src/routes/api/auth/register.js:105
msgid "Failed to establish session after register :(" msgid "Failed to establish session after register :("
msgstr "" msgstr ""
#: src/routes/api/auth/verify.js:26
#: src/routes/api/auth/verify.js:35
msgid "Mail verification"
msgstr ""
#: src/routes/api/auth/verify.js:27
msgid "You are now verified :)"
msgstr ""
#: src/routes/api/auth/verify.js:35
msgid ""
"Your mail verification code is invalid or already expired :(, please "
"request a new one."
msgstr ""
#: src/ssr/RedirectionPage.jsx:19 #: src/ssr/RedirectionPage.jsx:19
msgid "PixelPlanet.fun Accounts" msgid "PixelPlanet.fun Accounts"
msgstr "" msgstr ""
@ -467,4 +386,79 @@ msgstr ""
msgid "" msgid ""
"A canvas for the most active players from the the previous day. Daily " "A canvas for the most active players from the the previous day. Daily "
"ranking updates at 00:00 UTC." "ranking updates at 00:00 UTC."
msgstr ""
#: src/core/MailProvider.js:66
#, javascript-format
msgid "Welcome ${ name } to PixelPlanet, plese verify your mail"
msgstr ""
#: src/core/MailProvider.js:67
msgid "Hello ${ name }"
msgstr ""
#: src/core/MailProvider.js:68
msgid ""
"welcome to our little community of pixelplacers, to use your account, you "
"have to verify your mail. You can do that here: "
msgstr ""
#: src/core/MailProvider.js:68
msgid "Click to Verify"
msgstr ""
#: src/core/MailProvider.js:68
#: src/core/MailProvider.js:105
msgid "Or by copying following url:"
msgstr ""
#: src/core/MailProvider.js:69
msgid "Have fun and don't hesitate to contact us if you encouter any problems :)"
msgstr ""
#: src/core/MailProvider.js:70
#: src/core/MailProvider.js:107
msgid "Thanks"
msgstr ""
#: src/core/MailProvider.js:87
#, javascript-format
msgid ""
"We already sent you a verification mail, you can request another one in ${ "
"minLeft } minutes."
msgstr ""
#: src/core/MailProvider.js:103
msgid "You forgot your password for PixelPlanet? Get a new one here"
msgstr ""
#: src/core/MailProvider.js:104
msgid "Hello"
msgstr ""
#: src/core/MailProvider.js:105
msgid ""
"You requested to get a new password. You can change your password within "
"the next 30min here: "
msgstr ""
#: src/core/MailProvider.js:106
#, javascript-format
msgid ""
"If you did not request this mail, please just ignore it (the ip that "
"requested this mail was ${ ip })."
msgstr ""
#: src/core/MailProvider.js:114
msgid "Mail is not configured on the server"
msgstr ""
#: src/core/MailProvider.js:122
msgid ""
"We already sent you a mail with instructions. Please wait before requesting "
"another mail."
msgstr ""
#: src/core/MailProvider.js:130
msgid "Couldn't find this mail in our database"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -14,14 +14,11 @@ import {
import { import {
fetchMe, fetchMe,
} from './store/actions/thunks'; } from './store/actions/thunks';
import { import pixelTransferController from './ui/PixelTransferController';
receivePixelUpdate,
receivePixelReturn,
} from './ui/placePixel';
import store from './store/store'; import store from './store/store';
import renderApp from './components/App'; import renderApp from './components/App';
import { initRenderer, getRenderer } from './ui/renderer'; import { initRenderer, getRenderer } from './ui/renderer';
import SocketClient from './socket/SocketClient'; import socketClient from './socket/SocketClient';
persistStore(store, {}, () => { persistStore(store, {}, () => {
window.addEventListener('message', store.dispatch); window.addEventListener('message', store.dispatch);
@ -30,32 +27,19 @@ persistStore(store, {}, () => {
initRenderer(store, false); initRenderer(store, false);
SocketClient.on('pixelUpdate', ({ pixelTransferController.initialize(store, socketClient, getRenderer);
i, j, pixels,
}) => {
pixels.forEach((pxl) => {
const [offset, color] = pxl;
// remove protection
receivePixelUpdate(getRenderer(), i, j, offset, color & 0x7F);
});
});
SocketClient.on('pixelReturn', (args) => {
receivePixelReturn(store, getRenderer(), args);
});
window.addEventListener('hashchange', () => { window.addEventListener('hashchange', () => {
store.dispatch(urlChange()); store.dispatch(urlChange());
}); });
// check if on mobile // check if on mobile
//
function checkMobile() { function checkMobile() {
store.dispatch(setMobile(true)); store.dispatch(setMobile(true));
} }
document.addEventListener('touchstart', checkMobile, { once: true }); document.addEventListener('touchstart', checkMobile, { once: true });
// listen for resize // listen for resize
//
function onWindowResize() { function onWindowResize() {
store.dispatch(windowResize()); store.dispatch(windowResize());
} }
@ -65,7 +49,8 @@ persistStore(store, {}, () => {
store.dispatch(initTimer()); store.dispatch(initTimer());
store.dispatch(fetchMe()); store.dispatch(fetchMe());
SocketClient.initialize(store);
socketClient.initialize(store);
}); });
(function load() { (function load() {
@ -90,7 +75,7 @@ persistStore(store, {}, () => {
if (!renderer.isChunkInView(zc, xc, yc)) { if (!renderer.isChunkInView(zc, xc, yc)) {
cnt++; cnt++;
if (value.isBasechunk) { if (value.isBasechunk) {
SocketClient.deRegisterChunk([xc, yc]); socketClient.deRegisterChunk([xc, yc]);
} }
chunks.delete(key); chunks.delete(key);
value.destructor(); value.destructor();

View File

@ -42,7 +42,6 @@ const GlobalCaptcha = ({ close }) => {
setSubmitting(true); setSubmitting(true);
const retCode = await socketClient const retCode = await socketClient
.sendCaptchaSolution(text, captchaid); .sendCaptchaSolution(text, captchaid);
console.log('Captcha return:', retCode);
switch (retCode) { switch (retCode) {
case 0: case 0:
close(); close();

View File

@ -20,9 +20,7 @@ import {
moveEast, moveEast,
onViewFinishChange, onViewFinishChange,
} from '../store/actions'; } from '../store/actions';
import { import pixelTransferController from '../ui/PixelTransferController';
tryPlacePixel,
} from '../ui/placePixel';
import { import {
screenToWorld, screenToWorld,
getChunkOfPixel, getChunkOfPixel,
@ -245,9 +243,7 @@ class PixelPlainterControls {
} }
const [i, j] = getChunkOfPixel(canvasSize, x, y); const [i, j] = getChunkOfPixel(canvasSize, x, y);
const offset = getOffsetOfPixel(canvasSize, x, y); const offset = getOffsetOfPixel(canvasSize, x, y);
tryPlacePixel( pixelTransferController.tryPlacePixel(
renderer,
store,
i, j, offset, i, j, offset,
selectedColor, selectedColor,
curColor, curColor,

View File

@ -1,8 +1,6 @@
// allow the websocket to be noisy on the console // allow the websocket to be noisy on the console
/* eslint-disable no-console */ /* eslint-disable no-console */
import EventEmitter from 'events';
import { import {
hydratePixelUpdate, hydratePixelUpdate,
hydratePixelReturn, hydratePixelReturn,
@ -37,12 +35,12 @@ import {
fetchMe, fetchMe,
} from '../store/actions/thunks'; } from '../store/actions/thunks';
import { shardHost } from '../store/actions/fetch'; import { shardHost } from '../store/actions/fetch';
import pixelTransferController from '../ui/PixelTransferController';
const chunks = []; const chunks = [];
class SocketClient extends EventEmitter { class SocketClient {
constructor() { constructor() {
super();
console.log('Creating WebSocketClient'); console.log('Creating WebSocketClient');
this.store = null; this.store = null;
this.ws = null; this.ws = null;
@ -186,8 +184,8 @@ class SocketClient extends EventEmitter {
sendCaptchaSolution(solution, captchaid) { sendCaptchaSolution(solution, captchaid) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let id; let id;
const queueObj = ['cs', (args) => { const queueObj = ['cs', (arg) => {
resolve(args); resolve(arg);
clearTimeout(id); clearTimeout(id);
}]; }];
this.reqQueue.push(queueObj); this.reqQueue.push(queueObj);
@ -207,12 +205,21 @@ class SocketClient extends EventEmitter {
* @param i, j chunk coordinates * @param i, j chunk coordinates
* @param pixel Array of [[offset, color],...] pixels within chunk * @param pixel Array of [[offset, color],...] pixels within chunk
*/ */
requestPlacePixels( sendPixelUpdate(i, j, pixels) {
i, j, return new Promise((resolve, reject) => {
pixels, let id;
) { const queueObj = ['pu', (arg) => {
const buffer = dehydratePixelUpdate(i, j, pixels); resolve(arg);
this.sendWhenReady(buffer); clearTimeout(id);
}];
this.reqQueue.push(queueObj);
id = setTimeout(() => {
const pos = this.reqQueue.indexOf(queueObj);
if (~pos) this.reqQueue.splice(pos, 1);
reject(new Error('Timeout'));
}, 20000);
this.sendWhenReady(dehydratePixelUpdate(i, j, pixels));
});
} }
sendChatMessage(message, channelId) { sendChatMessage(message, channelId) {
@ -267,11 +274,15 @@ class SocketClient extends EventEmitter {
switch (opcode) { switch (opcode) {
case PIXEL_UPDATE_OP: case PIXEL_UPDATE_OP:
this.emit('pixelUpdate', hydratePixelUpdate(data)); pixelTransferController.receivePixelUpdate(hydratePixelUpdate(data));
break; break;
case PIXEL_RETURN_OP: case PIXEL_RETURN_OP: {
this.emit('pixelReturn', hydratePixelReturn(data)); const pos = this.reqQueue.findIndex((q) => q[0] === 'pu');
if (~pos) {
this.reqQueue.splice(pos, 1)[0][1](hydratePixelReturn(data));
}
break; break;
}
case ONLINE_COUNTER_OP: case ONLINE_COUNTER_OP:
this.store.dispatch(receiveOnline(hydrateOnlineCounter(data))); this.store.dispatch(receiveOnline(hydrateOnlineCounter(data)));
break; break;

View File

@ -106,12 +106,6 @@ export function toggleOpenMenu() {
}; };
} }
export function requestPlaceTimeout() {
return {
type: 'REQ_PLACE_TIMEOUT',
};
}
export function setHover(hover) { export function setHover(hover) {
return { return {
type: 'SET_HOVER', type: 'SET_HOVER',
@ -298,22 +292,6 @@ export function sendChatMessage(
}; };
} }
/*
* check socket/packets/PixelReturn.js for args
*/
export function storeReceivePixelReturn(args) {
args.type = 'REC_PXL_RETURN';
return args;
}
export function requestPlacePixels(i, j, pixels) {
return {
type: 'REQ_PLACE_PXLS',
i,
j,
pixels,
};
}
export function logoutUser( export function logoutUser(
) { ) {
return { return {

View File

@ -23,11 +23,17 @@ export function receiveChatMessage(
return (dispatch, getState) => { return (dispatch, getState) => {
channel = Number(channel); channel = Number(channel);
const state = getState(); const state = getState();
const isRead = state.windows.showWindows let isRead = false;
// eslint-disable-next-line max-len if (state.windows) {
&& state.windows.windows.find((win) => win.windowType === 'CHAT' && !win.hidden) isRead = state.windows.windows.some(
// eslint-disable-next-line max-len (win) => win.windowType === 'CHAT' && !win.hidden,
&& Object.values(state.windows.args).find((args) => args.chatChannel === channel); ) && Object.values(state.windows.args).some(
(args) => args.chatChannel === channel,
);
} else {
isRead = state.popup.windowType === 'CHAT'
|| state.popup.args.chatChannel === channel;
}
// TODO ping doesn't work since update // TODO ping doesn't work since update
// const { nameRegExp } = state.user; // const { nameRegExp } = state.user;
@ -40,7 +46,7 @@ export function receiveChatMessage(
channel, channel,
user, user,
isPing: false, isPing: false,
isRead: !!isRead, isRead,
}); });
}; };
} }
@ -72,3 +78,15 @@ export function removeChatChannel(cid) {
cid, cid,
}; };
} }
export function setPixelsFetching(fetching) {
return {
type: 'SET_PXLS_FETCHING',
fetching,
};
}
export function receivePlacePixels(args) {
args.type = 'REC_SET_PXLS';
return args;
}

View File

@ -119,7 +119,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'REC_PXL_RETURN': { case 'REC_SET_PXLS': {
switch (action.retCode) { switch (action.retCode) {
case 0: { case 0: {
// successfully placed pixel // successfully placed pixel

View File

@ -114,7 +114,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'REC_PXL_RETURN': { case 'REC_SET_PXLS': {
const renderer = getRenderer(); const renderer = getRenderer();
renderer.forceNextSubrender = true; renderer.forceNextSubrender = true;
const { coolDownSeconds } = action; const { coolDownSeconds } = action;

View File

@ -24,14 +24,6 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'REQ_PLACE_PXLS': {
const {
i, j, pixels,
} = action;
SocketClient.requestPlacePixels(i, j, pixels);
break;
}
case 's/REQ_CHAT_MESSAGE': { case 's/REQ_CHAT_MESSAGE': {
const { const {
text, text,

View File

@ -6,6 +6,7 @@
const initialState = { const initialState = {
fetchingChunks: 0, fetchingChunks: 0,
fetchingChat: false, fetchingChat: false,
fetchingPixel: false,
fetchinApi: false, fetchinApi: false,
}; };
@ -59,6 +60,14 @@ export default function fetching(
}; };
} }
case 'SET_PXLS_FETCHING': {
const { fetching: fetchingPixel } = action;
return {
...state,
fetchingPixel,
};
}
default: default:
return state; return state;
} }

View File

@ -31,7 +31,7 @@ export default function ranks(
action, action,
) { ) {
switch (action.type) { switch (action.type) {
case 'REC_PXL_RETURN': { case 'REC_SET_PXLS': {
const { const {
rankedPxlCnt, rankedPxlCnt,
} = action; } = action;

View File

@ -4,7 +4,6 @@ const initialState = {
wait: null, wait: null,
coolDown: null, // ms coolDown: null, // ms
lastCoolDownEnd: null, lastCoolDownEnd: null,
allowSettingPixel: true,
// messages are sent by api/me, like not_verified status // messages are sent by api/me, like not_verified status
messages: [], messages: [],
mailreg: false, mailreg: false,
@ -42,28 +41,13 @@ export default function user(
}; };
} }
case 'REQ_PLACE_TIMEOUT': { case 'REC_SET_PXLS': {
return {
...state,
allowSettingPixel: true,
};
}
case 'REQ_PLACE_PXLS': {
return {
...state,
allowSettingPixel: false,
};
}
case 'REC_PXL_RETURN': {
const { const {
wait: duration, wait: duration,
} = action; } = action;
return { return {
...state, ...state,
wait: (duration) ? Date.now() + duration : state.wait, wait: (duration) ? Date.now() + duration : state.wait,
allowSettingPixel: true,
}; };
} }

View File

@ -0,0 +1,269 @@
/*
* Control in- and outcomming pixels,
* do client prediction, send to draw on renderer
*/
import { t } from 'ttag';
import {
pAlert,
} from '../store/actions';
import {
setPixelsFetching,
receivePlacePixels,
} from '../store/actions/socket';
import {
notify,
} from '../store/actions/thunks';
class PixelTransferController {
constructor() {
this.requestFromQueue = this.requestFromQueue.bind(this);
/*
* cache of pixels that still are to set
* [{i: i, j: j, pixels: [[offset, color],...]}, ...]
*/
this.pixelQueue = [];
/*
* requests that got predicted on client and yet have to be
* received from the server
* [[i, j, offset, colorold, colornew], ...]
*/
this.clientPredictions = [];
}
initialize(store, socketClient, getRenderer) {
this.store = store;
this.socketClient = socketClient;
this.getRenderer = getRenderer;
}
/*
* request pixel placement from queue
*/
async requestFromQueue() {
const { store } = this;
if (!this.pixelQueue.length) {
store.dispatch(setPixelsFetching(false));
return;
}
store.dispatch(setPixelsFetching(true));
const { i, j, pixels } = this.pixelQueue.shift();
let ret;
try {
ret = await this.socketClient.sendPixelUpdate(i, j, pixels);
} catch {
// timeout
ret = {
retCode: 16,
coolDownSeconds: 0,
pxlCnt: 0,
};
store.dispatch(pAlert(
t`Error :(`,
t`Didn't get an answer from pixelplanet. Maybe try to refresh?`,
'error',
));
}
const {
retCode,
coolDownSeconds,
pxlCnt,
} = ret;
if (coolDownSeconds) {
store.dispatch(notify(coolDownSeconds));
}
if (retCode) {
/*
* one or more pixels didn't get set,
* revert predictions and clean queue
*/
const [offset] = pixels[pxlCnt];
this.revertPredictionsAt(i, j, offset);
this.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;
case 16:
errorTitle = t`Timeout`;
// eslint-disable-next-line max-len
msg = t`Didn't get an answer from pixelplanet. Maybe try to refresh if problem persists?`;
break;
default:
errorTitle = t`Weird`;
msg = t`Couldn't set Pixel`;
}
if (msg || errorTitle) {
store.dispatch(pAlert(
(errorTitle || t`Error ${retCode}`),
msg,
type,
));
}
store.dispatch(receivePlacePixels(ret));
setTimeout(this.requestFromQueue, 100);
}
/*
* Revert predictions starting at given pixel
* @param i, j, offset data of the first pixel that got rejected
*/
revertPredictionsAt(sI, sJ, sOffset) {
const renderer = this.getRenderer();
const { clientPredictions } = this;
let p = 0;
while (p < clientPredictions.length) {
const predPxl = clientPredictions[p];
if (predPxl[0] === sI
&& predPxl[1] === sJ
&& predPxl[2] === sOffset
) {
break;
}
p += 1;
}
while (p < clientPredictions.length) {
const [i, j, offset, color] = clientPredictions[p];
renderer.renderPixel(i, j, offset, color, false);
p += 1;
}
this.clientPredictions = [];
}
/*
* got pixel update from websocket
*/
receivePixelUpdate({
i,
j,
pixels,
}) {
pixels.forEach((pxl) => {
const [offset, color] = pxl;
const { clientPredictions } = this;
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;
}
}
this.getRenderer().renderPixel(i, j, offset, color, true);
});
}
/*
* try to place a pixel
*/
tryPlacePixel(
i,
j,
offset,
color,
curColor,
) {
this.getRenderer().renderPixel(i, j, offset, color, false);
this.clientPredictions.push([i, j, offset, curColor, color]);
const { pixelQueue } = this;
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 (!this.store.getState().fetching.fetchingPixel) {
this.requestFromQueue();
}
}
}
export default new PixelTransferController();

View File

@ -427,8 +427,8 @@ class Renderer {
isLightGrid, isLightGrid,
} = state.gui; } = state.gui;
const { const {
allowSettingPixel, fetchingPixel,
} = state.user; } = state.fetching;
const { const {
view, view,
viewscale, viewscale,
@ -448,13 +448,13 @@ class Renderer {
// if we have to render placeholder // if we have to render placeholder
const doRenderPlaceholder = ( const doRenderPlaceholder = (
viewscale >= 3 viewscale >= 3
&& allowSettingPixel && !fetchingPixel
&& (hover || this.hover) && (hover || this.hover)
&& !isPotato && !isPotato
); );
const doRenderPotatoPlaceholder = ( const doRenderPotatoPlaceholder = (
viewscale >= 3 viewscale >= 3
&& allowSettingPixel && !fetchingPixel
&& (hover !== this.hover && (hover !== this.hover
|| this.forceNextRender || this.forceNextRender
|| this.forceNextSubrender || this.forceNextSubrender

View File

@ -20,8 +20,7 @@ import {
setHover, setHover,
selectColor, selectColor,
} from '../store/actions'; } from '../store/actions';
import { tryPlacePixel } from './placePixel'; import pixelTransferController from './PixelTransferController';
const renderDistance = 150; const renderDistance = 150;
@ -398,8 +397,8 @@ class Renderer {
store, store,
} = this; } = this;
const { const {
allowSettingPixel, fetchingPixel,
} = store.getState().user; } = store.getState().fetching;
mouse.set( mouse.set(
(clientX / innerWidth) * 2 - 1, (clientX / innerWidth) * 2 - 1,
@ -414,7 +413,7 @@ class Renderer {
.add(intersect.face.normal.multiplyScalar(0.5)) .add(intersect.face.normal.multiplyScalar(0.5))
.floor() .floor()
.addScalar(0.5); .addScalar(0.5);
if (!allowSettingPixel if (fetchingPixel
|| target.clone().sub(camera.position).length() > 120) { || target.clone().sub(camera.position).length() > 120) {
rollOverMesh.position.y = -10; rollOverMesh.position.y = -10;
} else { } else {
@ -442,8 +441,8 @@ class Renderer {
store, store,
} = this; } = this;
const { const {
allowSettingPixel, fetchingPixel,
} = store.getState().user; } = store.getState().fetching;
mouse.set(0, 0); mouse.set(0, 0);
raycaster.setFromCamera(mouse, camera); raycaster.setFromCamera(mouse, camera);
@ -454,9 +453,9 @@ class Renderer {
.add(intersect.face.normal.multiplyScalar(0.5)) .add(intersect.face.normal.multiplyScalar(0.5))
.floor() .floor()
.addScalar(0.5); .addScalar(0.5);
// TODO make rollOverMesh in a different color while allowSettingPixel false // TODO make rollOverMesh in a different color while fetchingPixel
// instead of hiding it.... we can now queue Voxels // instead of hiding it.... we can now queue Voxels
if (!allowSettingPixel if (fetchingPixel
|| target.clone().sub(camera.position).length() > 50) { || target.clone().sub(camera.position).length() > 50) {
rollOverMesh.position.y = -10; rollOverMesh.position.y = -10;
} else { } else {
@ -481,9 +480,7 @@ class Renderer {
const curColor = (chClr === 0) ? this.chunkLoader.getVoxel(x, y, z) : 0; const curColor = (chClr === 0) ? this.chunkLoader.getVoxel(x, y, z) : 0;
const [i, j] = getChunkOfPixel(canvasSize, x, y, z); const [i, j] = getChunkOfPixel(canvasSize, x, y, z);
const offset = getOffsetOfPixel(canvasSize, x, y, z); const offset = getOffsetOfPixel(canvasSize, x, y, z);
tryPlacePixel( pixelTransferController.tryPlacePixel(
this,
store,
i, j, i, j,
offset, offset,
chClr, chClr,
@ -579,10 +576,12 @@ class Renderer {
const state = this.store.getState(); const state = this.store.getState();
const { const {
allowSettingPixel,
isOnMobile, isOnMobile,
} = state.user; } = state.user;
if (!allowSettingPixel || isOnMobile) { const {
fetchingPixel,
} = state.fetching;
if (fetchingPixel || isOnMobile) {
return; return;
} }

View File

@ -1,277 +0,0 @@
/*
* 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);
}