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"
"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"
msgstr ""
#: src/core/ChatProvider.js:392
msgid "You are banned"
#: src/core/ChatProvider.js:436
msgid "Your country is temporary muted from this chat channel"
msgstr ""
#: src/core/ChatProvider.js:394
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
#: src/core/ChatProvider.js:439
msgid "You are permanently muted, join our guilded to apppeal the mute"
msgstr ""
#: src/core/ChatProvider.js:441
msgid "You are banned"
msgstr ""
#: src/core/ChatProvider.js:443
msgid "Your Internet Provider is banned"
msgstr ""
#: src/core/ChatProvider.js:448
#, javascript-format
msgid "You are muted for another ${ timeMin } minutes"
msgstr ""
#: src/core/ChatProvider.js:445
msgid "You are muted for another ${ muted } seconds"
#: src/core/ChatProvider.js:450
msgid "You are muted for another ${ ttl } seconds"
msgstr ""
#: src/core/ChatProvider.js:453
msgid "Ow no! Spam protection decided to mute you"
#: src/core/ChatProvider.js:467
#, javascript-format
msgid "You are sending messages too fast, you have to wait ${ waitTime }s :("
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 :("
msgstr ""
#: src/core/ChatProvider.js:468
#: src/core/ChatProvider.js:502
msgid "Please use int channel"
msgstr ""
#: src/core/ChatProvider.js:475
msgid "Your country is temporary muted from this chat channel"
msgstr ""
#: src/core/ChatProvider.js:483
#: src/core/ChatProvider.js:510
msgid "Stop flooding."
msgstr ""
#: src/routes/reset_password.js:37
#: src/routes/reset_password.js:39
msgid "You sent an empty password or invalid data :("
msgstr ""
#: src/routes/reset_password.js:49
#: src/routes/reset_password.js:51
msgid "This password-reset link isn't valid anymore :("
msgstr ""
#: src/routes/reset_password.js:60
#: src/routes/reset_password.js:62
msgid "Your passwords do not match :("
msgstr ""
#: src/routes/reset_password.js:75
#: src/routes/reset_password.js:77
msgid "User doesn't exist in our database :("
msgstr ""
#: src/routes/reset_password.js:87
#: src/routes/reset_password.js:89
msgid "Passowrd successfully changed."
msgstr ""
#: src/routes/reset_password.js:106
#: src/routes/reset_password.js:108
msgid "Invalid url :( Please check your mail again."
msgstr ""
#: src/routes/reset_password.js:119
msgid ""
"This passwort reset link is wrong or already expired, please request a new "
"one (Note: you can use those links just once)"
#: src/ssr/PopUp.jsx:58
msgid "ppfun"
msgstr ""
#: src/ssr/Globe.jsx:33
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
#: src/ssr/PopUp.jsx:59
msgid "PixelPlanet.Fun PopUp"
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"
msgstr ""
#: src/ssr/Main.jsx:55
#: src/ssr/Main.jsx:60
msgid "Place color pixels on an map styled canvas with other players online"
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
msgid "Email can't be empty."
msgstr ""
@ -289,30 +161,72 @@ msgstr ""
msgid "Password must be shorter than 60 characters."
msgstr ""
#: src/routes/api/captcha.js:20
msgid "No captcha text given"
#: src/ssr/PasswordReset.jsx:20
#: src/ssr/PasswordReset.jsx:40
msgid "PixelPlanet.fun Password Reset"
msgstr ""
#: src/routes/api/captcha.js:25
msgid "No captcha id given"
#: src/ssr/PasswordReset.jsx:21
#: src/ssr/PasswordReset.jsx:41
msgid "Reset your password here"
msgstr ""
#: src/routes/api/auth/register.js:54
#: src/routes/api/captcha.js:44
#: src/core/MailProvider.js:105
#: 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."
msgstr ""
#: src/routes/api/auth/register.js:57
#: src/routes/api/captcha.js:50
#: src/routes/api/auth/register.js:62
#: src/routes/api/captcha.js:39
msgid "You failed your captcha"
msgstr ""
#: src/routes/api/auth/register.js:60
#: src/routes/api/captcha.js:56
#: src/routes/api/captcha.js:43
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"
msgstr ""
#: src/routes/api/captcha.js:63
#: src/routes/api/captcha.js:57
msgid "Server error occured"
msgstr ""
@ -328,66 +242,71 @@ msgstr ""
msgid "Just admins can do that"
msgstr ""
#: src/routes/api/baninfo.js:24
#: src/routes/api/baninfo.js:32
msgid "You are not banned"
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
msgid "You are not even logged in."
msgstr ""
#: src/routes/api/auth/delete_account.js:54
#: src/routes/api/auth/delete_account.js:55
#: src/routes/api/auth/logout.js:20
msgid "Server error when logging out."
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/delete_account.js:34
#: src/routes/api/auth/delete_account.js:35
msgid "You are not authenticated."
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/delete_account.js:44
#: src/routes/api/auth/delete_account.js:45
msgid "Incorrect password!"
msgstr ""
#: src/routes/api/auth/register.js:26
msgid "No Captcha given"
msgstr ""
#: src/routes/api/auth/register.js:29
msgid "E-Mail already in use."
#: src/routes/api/auth/change_mail.js:21
#: src/routes/api/auth/register.js:24
msgid "This email provider is not allowed"
msgstr ""
#: 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."
msgstr ""
#: src/routes/api/auth/register.js:84
#: src/routes/api/auth/register.js:89
msgid "Failed to create new user :("
msgstr ""
#: src/routes/api/auth/register.js:100
#: src/routes/api/auth/register.js:105
msgid "Failed to establish session after register :("
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
msgid "PixelPlanet.fun Accounts"
msgstr ""
@ -467,4 +386,79 @@ msgstr ""
msgid ""
"A canvas for the most active players from the the previous day. Daily "
"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 ""

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -106,12 +106,6 @@ export function toggleOpenMenu() {
};
}
export function requestPlaceTimeout() {
return {
type: 'REQ_PLACE_TIMEOUT',
};
}
export function setHover(hover) {
return {
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(
) {
return {

View File

@ -23,11 +23,17 @@ export function receiveChatMessage(
return (dispatch, getState) => {
channel = Number(channel);
const state = getState();
const isRead = state.windows.showWindows
// eslint-disable-next-line max-len
&& state.windows.windows.find((win) => win.windowType === 'CHAT' && !win.hidden)
// eslint-disable-next-line max-len
&& Object.values(state.windows.args).find((args) => args.chatChannel === channel);
let isRead = false;
if (state.windows) {
isRead = state.windows.windows.some(
(win) => win.windowType === 'CHAT' && !win.hidden,
) && 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
// const { nameRegExp } = state.user;
@ -40,7 +46,7 @@ export function receiveChatMessage(
channel,
user,
isPing: false,
isRead: !!isRead,
isRead,
});
};
}
@ -72,3 +78,15 @@ export function removeChatChannel(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;
}
case 'REC_PXL_RETURN': {
case 'REC_SET_PXLS': {
switch (action.retCode) {
case 0: {
// successfully placed pixel

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,6 @@ const initialState = {
wait: null,
coolDown: null, // ms
lastCoolDownEnd: null,
allowSettingPixel: true,
// messages are sent by api/me, like not_verified status
messages: [],
mailreg: false,
@ -42,28 +41,13 @@ export default function user(
};
}
case 'REQ_PLACE_TIMEOUT': {
return {
...state,
allowSettingPixel: true,
};
}
case 'REQ_PLACE_PXLS': {
return {
...state,
allowSettingPixel: false,
};
}
case 'REC_PXL_RETURN': {
case 'REC_SET_PXLS': {
const {
wait: duration,
} = action;
return {
...state,
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,
} = state.gui;
const {
allowSettingPixel,
} = state.user;
fetchingPixel,
} = state.fetching;
const {
view,
viewscale,
@ -448,13 +448,13 @@ class Renderer {
// if we have to render placeholder
const doRenderPlaceholder = (
viewscale >= 3
&& allowSettingPixel
&& !fetchingPixel
&& (hover || this.hover)
&& !isPotato
);
const doRenderPotatoPlaceholder = (
viewscale >= 3
&& allowSettingPixel
&& !fetchingPixel
&& (hover !== this.hover
|| this.forceNextRender
|| this.forceNextSubrender

View File

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