make parent classes for renderer and chunks,

move GC into those parent classes
restrict the total amount of loaded Chunks
This commit is contained in:
HF 2023-01-16 21:49:57 +01:00
parent 060688337d
commit 90cd2016aa
21 changed files with 641 additions and 508 deletions

View File

@ -219,6 +219,30 @@ msgstr ""
msgid "You are not banned" msgid "You are not banned"
msgstr "" msgstr ""
#: src/routes/api/auth/logout.js:11
msgid "You are not even logged in."
msgstr ""
#: 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/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/routes/api/auth/change_mail.js:21 #: src/routes/api/auth/change_mail.js:21
#: src/routes/api/auth/register.js:24 #: src/routes/api/auth/register.js:24
msgid "This email provider is not allowed" msgid "This email provider is not allowed"
@ -256,15 +280,6 @@ msgstr ""
msgid "Failed to establish session after register :(" msgid "Failed to establish session after register :("
msgstr "" msgstr ""
#: src/routes/api/auth/logout.js:11
msgid "You are not even logged in."
msgstr ""
#: 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:43 #: 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:35 #: src/routes/api/auth/delete_account.js:35
@ -277,21 +292,6 @@ msgstr ""
msgid "Incorrect password!" msgid "Incorrect password!"
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 ""

View File

@ -46,136 +46,136 @@ msgstr ""
msgid "Hide Hidden Canvases" msgid "Hide Hidden Canvases"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:63 #: src/ui/PixelTransferController.js:70
msgid "Error :(" msgid "Error :("
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:64 #: src/ui/PixelTransferController.js:71
msgid "Didn't get an answer from pixelplanet. Maybe try to refresh?" msgid "Didn't get an answer from pixelplanet. Maybe try to refresh?"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:96 #: src/ui/PixelTransferController.js:103
msgid "Invalid Canvas" msgid "Invalid Canvas"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:97 #: src/ui/PixelTransferController.js:104
msgid "This canvas doesn't exist" msgid "This canvas doesn't exist"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:100 #: src/ui/PixelTransferController.js:107
#: src/ui/PixelTransferController.js:104 #: src/ui/PixelTransferController.js:111
#: src/ui/PixelTransferController.js:108 #: src/ui/PixelTransferController.js:115
msgid "Invalid Coordinates" msgid "Invalid Coordinates"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:101 #: src/ui/PixelTransferController.js:108
msgid "x out of bounds" msgid "x out of bounds"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:105 #: src/ui/PixelTransferController.js:112
msgid "y out of bounds" msgid "y out of bounds"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:109 #: src/ui/PixelTransferController.js:116
msgid "z out of bounds" msgid "z out of bounds"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:112 #: src/ui/PixelTransferController.js:119
msgid "Wrong Color" msgid "Wrong Color"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:113 #: src/ui/PixelTransferController.js:120
msgid "Invalid color selected" msgid "Invalid color selected"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:116 #: src/ui/PixelTransferController.js:123
msgid "Just for registered Users" msgid "Just for registered Users"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:117 #: src/ui/PixelTransferController.js:124
msgid "You have to be logged in to place on this canvas" msgid "You have to be logged in to place on this canvas"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:120 #: src/ui/PixelTransferController.js:127
msgid "Place more :)" msgid "Place more :)"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:122 #: src/ui/PixelTransferController.js:129
msgid "You can not access this canvas yet. You need to place more pixels" msgid "You can not access this canvas yet. You need to place more pixels"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:125 #: src/ui/PixelTransferController.js:132
msgid "Pixel protected!" msgid "Pixel protected!"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:132 #: src/ui/PixelTransferController.js:139
msgid "Please prove that you are human" msgid "Please prove that you are human"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:136 #: src/ui/PixelTransferController.js:143
msgid "No Proxies Allowed :(" msgid "No Proxies Allowed :("
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:137 #: src/ui/PixelTransferController.js:144
msgid "You are using a Proxy." msgid "You are using a Proxy."
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:140 #: src/ui/PixelTransferController.js:147
msgid "Not allowed" msgid "Not allowed"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:141 #: src/ui/PixelTransferController.js:148
msgid "Just the Top10 of yesterday can place here" msgid "Just the Top10 of yesterday can place here"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:144 #: src/ui/PixelTransferController.js:151
msgid "You are weird" msgid "You are weird"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:146 #: src/ui/PixelTransferController.js:153
msgid "Server got confused by your pixels. Are you playing on multiple devices?" msgid "Server got confused by your pixels. Are you playing on multiple devices?"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:149 #: src/ui/PixelTransferController.js:156
msgid "Banned" msgid "Banned"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:153 #: src/ui/PixelTransferController.js:160
msgid "Range Banned" msgid "Range Banned"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:154 #: src/ui/PixelTransferController.js:161
msgid "Your Internet Provider is banned from playing this game" msgid "Your Internet Provider is banned from playing this game"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:157 #: src/ui/PixelTransferController.js:164
msgid "Timeout" msgid "Timeout"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:159 #: src/ui/PixelTransferController.js:166
msgid "" msgid ""
"Didn't get an answer from pixelplanet. Maybe try to refresh if problem " "Didn't get an answer from pixelplanet. Maybe try to refresh if problem "
"persists?" "persists?"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:162 #: src/ui/PixelTransferController.js:169
msgid "Weird" msgid "Weird"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:163 #: src/ui/PixelTransferController.js:170
msgid "Couldn't set Pixel" msgid "Couldn't set Pixel"
msgstr "" msgstr ""
#: src/ui/PixelTransferController.js:168 #: src/ui/PixelTransferController.js:175
#, javascript-format #, javascript-format
msgid "Error ${ retCode }" msgid "Error ${ retCode }"
msgstr "" msgstr ""
#: src/ui/renderer.js:35 #: src/ui/rendererFactory.js:30
msgid "Canvas Error" msgid "Canvas Error"
msgstr "" msgstr ""
#: src/ui/renderer.js:36 #: src/ui/rendererFactory.js:31
msgid "Can't render 3D canvas, do you have WebGL2 disabled?" msgid "Can't render 3D canvas, do you have WebGL2 disabled?"
msgstr "" msgstr ""
@ -248,6 +248,11 @@ msgstr ""
msgid "Pixels placed" msgid "Pixels placed"
msgstr "" msgstr ""
#: src/components/buttons/CanvasSwitchButton.jsx:20
#: src/components/windows/index.js:19
msgid "Canvas Selection"
msgstr ""
#: src/components/buttons/ChatButton.jsx:88 #: src/components/buttons/ChatButton.jsx:88
msgid "Close Chat" msgid "Close Chat"
msgstr "" msgstr ""
@ -256,11 +261,6 @@ msgstr ""
msgid "Open Chat" msgid "Open Chat"
msgstr "" msgstr ""
#: src/components/buttons/CanvasSwitchButton.jsx:20
#: src/components/windows/index.js:19
msgid "Canvas Selection"
msgstr ""
#: src/components/buttons/ExpandMenuButton.jsx:22 #: src/components/buttons/ExpandMenuButton.jsx:22
msgid "Close Menu" msgid "Close Menu"
msgstr "" msgstr ""
@ -310,13 +310,8 @@ msgstr ""
msgid "Resize" msgid "Resize"
msgstr "" msgstr ""
#: src/components/buttons/DownloadButton.jsx:36 #: src/components/buttons/GlobeButton.jsx:34
msgid "Make Screenshot" msgid "Globe View"
msgstr ""
#: src/components/buttons/LogInButton.jsx:20
#: src/components/windows/index.js:15
msgid "User Area"
msgstr "" msgstr ""
#: src/components/buttons/PalselButton.jsx:30 #: src/components/buttons/PalselButton.jsx:30
@ -327,8 +322,9 @@ msgstr ""
msgid "Open Palette" msgid "Open Palette"
msgstr "" msgstr ""
#: src/components/buttons/GlobeButton.jsx:34 #: src/components/buttons/SettingsButton.jsx:21
msgid "Globe View" #: src/components/windows/index.js:14
msgid "Settings"
msgstr "" msgstr ""
#: src/components/BanInfo.jsx:75 #: src/components/BanInfo.jsx:75
@ -337,9 +333,13 @@ msgstr ""
msgid "Help" msgid "Help"
msgstr "" msgstr ""
#: src/components/buttons/SettingsButton.jsx:21 #: src/components/buttons/LogInButton.jsx:20
#: src/components/windows/index.js:14 #: src/components/windows/index.js:15
msgid "Settings" msgid "User Area"
msgstr ""
#: src/components/buttons/DownloadButton.jsx:36
msgid "Make Screenshot"
msgstr "" msgstr ""
#: src/components/windows/index.js:16 #: src/components/windows/index.js:16
@ -447,6 +447,99 @@ msgstr ""
msgid "Why?" msgid "Why?"
msgstr "" msgstr ""
#: src/components/windows/Settings.jsx:86
msgid "Show Grid"
msgstr ""
#: src/components/windows/Settings.jsx:91
msgid "Turn on grid to highlight pixel borders."
msgstr ""
#: src/components/windows/Settings.jsx:94
msgid "Show Pixel Activity"
msgstr ""
#: src/components/windows/Settings.jsx:99
msgid "Show circles where pixels are placed."
msgstr ""
#: src/components/windows/Settings.jsx:102
msgid "Disable Game Sounds"
msgstr ""
#: src/components/windows/Settings.jsx:108
msgid "All sound effects will be disabled."
msgstr ""
#: src/components/windows/Settings.jsx:112
msgid ""
"Your Browser doesn't allow us to use AudioContext to play sounds. Do you "
"have some privacy feature blocking us?"
msgstr ""
#: src/components/windows/Settings.jsx:118
msgid "Enable chat notifications"
msgstr ""
#: src/components/windows/Settings.jsx:122
msgid "Play a sound when new chat messages arrive"
msgstr ""
#: src/components/windows/Settings.jsx:125
msgid "Auto Zoom In"
msgstr ""
#: src/components/windows/Settings.jsx:130
msgid ""
"Zoom in instead of placing a pixel when you tap the canvas and your zoom is "
"small."
msgstr ""
#: src/components/windows/Settings.jsx:133
msgid "Compact Palette"
msgstr ""
#: src/components/windows/Settings.jsx:138
msgid "Display Palette in a compact form that takes less screen space."
msgstr ""
#: src/components/windows/Settings.jsx:141
msgid "Potato Mode"
msgstr ""
#: src/components/windows/Settings.jsx:145
msgid "For when you are playing on a potato."
msgstr ""
#: src/components/Converter.jsx:376
#: src/components/windows/Settings.jsx:148
msgid "Light Grid"
msgstr ""
#: src/components/windows/Settings.jsx:152
msgid "Show Grid in white instead of black."
msgstr ""
#: src/components/windows/Settings.jsx:156
msgid "Historical View"
msgstr ""
#: src/components/windows/Settings.jsx:161
msgid "Check out past versions of the canvas."
msgstr ""
#: src/components/windows/Settings.jsx:166
msgid "Themes"
msgstr ""
#: src/components/windows/Settings.jsx:171
msgid "How pixelplanet should look like."
msgstr ""
#: src/components/windows/Settings.jsx:178
msgid "Select Language"
msgstr ""
#: src/components/windows/Help.jsx:42 #: src/components/windows/Help.jsx:42
msgid "Place color pixels on a large canvas with other players online!" msgid "Place color pixels on a large canvas with other players online!"
msgstr "" msgstr ""
@ -689,99 +782,6 @@ msgstr ""
msgid "Submit" msgid "Submit"
msgstr "" msgstr ""
#: src/components/windows/Settings.jsx:86
msgid "Show Grid"
msgstr ""
#: src/components/windows/Settings.jsx:91
msgid "Turn on grid to highlight pixel borders."
msgstr ""
#: src/components/windows/Settings.jsx:94
msgid "Show Pixel Activity"
msgstr ""
#: src/components/windows/Settings.jsx:99
msgid "Show circles where pixels are placed."
msgstr ""
#: src/components/windows/Settings.jsx:102
msgid "Disable Game Sounds"
msgstr ""
#: src/components/windows/Settings.jsx:108
msgid "All sound effects will be disabled."
msgstr ""
#: src/components/windows/Settings.jsx:112
msgid ""
"Your Browser doesn't allow us to use AudioContext to play sounds. Do you "
"have some privacy feature blocking us?"
msgstr ""
#: src/components/windows/Settings.jsx:118
msgid "Enable chat notifications"
msgstr ""
#: src/components/windows/Settings.jsx:122
msgid "Play a sound when new chat messages arrive"
msgstr ""
#: src/components/windows/Settings.jsx:125
msgid "Auto Zoom In"
msgstr ""
#: src/components/windows/Settings.jsx:130
msgid ""
"Zoom in instead of placing a pixel when you tap the canvas and your zoom is "
"small."
msgstr ""
#: src/components/windows/Settings.jsx:133
msgid "Compact Palette"
msgstr ""
#: src/components/windows/Settings.jsx:138
msgid "Display Palette in a compact form that takes less screen space."
msgstr ""
#: src/components/windows/Settings.jsx:141
msgid "Potato Mode"
msgstr ""
#: src/components/windows/Settings.jsx:145
msgid "For when you are playing on a potato."
msgstr ""
#: src/components/Converter.jsx:376
#: src/components/windows/Settings.jsx:148
msgid "Light Grid"
msgstr ""
#: src/components/windows/Settings.jsx:152
msgid "Show Grid in white instead of black."
msgstr ""
#: src/components/windows/Settings.jsx:156
msgid "Historical View"
msgstr ""
#: src/components/windows/Settings.jsx:161
msgid "Check out past versions of the canvas."
msgstr ""
#: src/components/windows/Settings.jsx:166
msgid "Themes"
msgstr ""
#: src/components/windows/Settings.jsx:171
msgid "How pixelplanet should look like."
msgstr ""
#: src/components/windows/Settings.jsx:178
msgid "Select Language"
msgstr ""
#: src/components/windows/CanvasSelect.jsx:29 #: src/components/windows/CanvasSelect.jsx:29
msgid "" msgid ""
"Select the canvas you want to use. Every canvas is unique and has " "Select the canvas you want to use. Every canvas is unique and has "
@ -826,6 +826,14 @@ msgid ""
"how the canvas was at that time." "how the canvas was at that time."
msgstr "" msgstr ""
#: src/components/windows/ForgotPassword.jsx:58
msgid "Sent you a mail with instructions to reset your password."
msgstr ""
#: src/components/windows/ForgotPassword.jsx:69
msgid "Enter your mail address and we will send you a new password:"
msgstr ""
#: src/components/windows/Chat.jsx:180 #: src/components/windows/Chat.jsx:180
msgid "Start chatting here" msgid "Start chatting here"
msgstr "" msgstr ""
@ -842,14 +850,6 @@ msgstr ""
msgid "Channel settings" msgid "Channel settings"
msgstr "" msgstr ""
#: src/components/windows/ForgotPassword.jsx:58
msgid "Sent you a mail with instructions to reset your password."
msgstr ""
#: src/components/windows/ForgotPassword.jsx:69
msgid "Enter your mail address and we will send you a new password:"
msgstr ""
#: src/components/Captcha.jsx:51 #: src/components/Captcha.jsx:51
#: src/components/Captcha.jsx:105 #: src/components/Captcha.jsx:105
msgid "Could not load captcha" msgid "Could not load captcha"
@ -931,6 +931,13 @@ msgstr ""
msgid "Password must be shorter than 60 characters." msgid "Password must be shorter than 60 characters."
msgstr "" msgstr ""
#: src/components/ChangeMail.jsx:91
#: src/components/ChangeName.jsx:68
#: src/components/ChangePassword.jsx:109
#: src/components/LanguageSelect.jsx:80
msgid "Save"
msgstr ""
#: src/components/GetIID.jsx:44 #: src/components/GetIID.jsx:44
msgid "Get IID" msgid "Get IID"
msgstr "" msgstr ""
@ -1121,13 +1128,6 @@ msgstr ""
msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC." msgid "Ranking updates every 5 min. Daily rankings get reset at midnight UTC."
msgstr "" msgstr ""
#: src/components/ChangeMail.jsx:91
#: src/components/ChangeName.jsx:68
#: src/components/ChangePassword.jsx:109
#: src/components/LanguageSelect.jsx:80
msgid "Save"
msgstr ""
#: src/components/CanvasItem.jsx:29 #: src/components/CanvasItem.jsx:29
msgid "Online Users" msgid "Online Users"
msgstr "" msgstr ""
@ -1201,6 +1201,24 @@ msgstr ""
msgid "LogIn" msgid "LogIn"
msgstr "" msgstr ""
#: src/components/UserMessages.jsx:28
msgid ""
"Please verify your mail address or your account could get deleted after a "
"few days."
msgstr ""
#: src/components/UserMessages.jsx:49
msgid "A new verification mail is getting sent to you."
msgstr ""
#: src/components/UserMessages.jsx:53
msgid "Click here to request a new verification mail."
msgstr ""
#: src/components/ChangeName.jsx:64
msgid "New Username"
msgstr ""
#: src/components/ChangePassword.jsx:21 #: src/components/ChangePassword.jsx:21
msgid "Passwords do not match." msgid "Passwords do not match."
msgstr "" msgstr ""
@ -1221,20 +1239,6 @@ msgstr ""
msgid "Confirm New Password" msgid "Confirm New Password"
msgstr "" msgstr ""
#: src/components/UserMessages.jsx:28
msgid ""
"Please verify your mail address or your account could get deleted after a "
"few days."
msgstr ""
#: src/components/UserMessages.jsx:49
msgid "A new verification mail is getting sent to you."
msgstr ""
#: src/components/UserMessages.jsx:53
msgid "Click here to request a new verification mail."
msgstr ""
#: src/components/ChangeMail.jsx:59 #: src/components/ChangeMail.jsx:59
msgid "" msgid ""
"Changed Mail successfully. We sent you a verification mail, " "Changed Mail successfully. We sent you a verification mail, "
@ -1245,14 +1249,34 @@ msgstr ""
msgid "New Mail" msgid "New Mail"
msgstr "" msgstr ""
#: src/components/ChangeName.jsx:64
msgid "New Username"
msgstr ""
#: src/components/DeleteAccount.jsx:66 #: src/components/DeleteAccount.jsx:66
msgid "Yes, Delete My Account!" msgid "Yes, Delete My Account!"
msgstr "" msgstr ""
#: src/components/SocialSettings.jsx:35
msgid "Block DMs"
msgstr ""
#: src/components/SocialSettings.jsx:42
msgid "Block all Private Messages"
msgstr ""
#: src/components/SocialSettings.jsx:44
msgid "Private"
msgstr ""
#: src/components/SocialSettings.jsx:51
msgid "Don't show me in global stats"
msgstr ""
#: src/components/SocialSettings.jsx:57
msgid "Unblock Users"
msgstr ""
#: src/components/SocialSettings.jsx:82
msgid "You have no users blocked"
msgstr ""
#: src/components/ModCanvastools.jsx:168 #: src/components/ModCanvastools.jsx:168
msgid "Build image on canvas." msgid "Build image on canvas."
msgstr "" msgstr ""
@ -1346,6 +1370,54 @@ msgstr ""
msgid "Stop Cleaner" msgid "Stop Cleaner"
msgstr "" msgstr ""
#: src/components/ModWatchtools.jsx:48
msgid "Interval is invalid"
msgstr ""
#: src/components/ModWatchtools.jsx:122
msgid "Check who placed in an area"
msgstr ""
#: src/components/ModWatchtools.jsx:123
msgid "Canvas"
msgstr ""
#: src/components/ModWatchtools.jsx:142
msgid "Interval"
msgstr ""
#: src/components/ModWatchtools.jsx:157
msgid "IID (optional)"
msgstr ""
#: src/components/ModWatchtools.jsx:236
msgid "Get Pixels"
msgstr ""
#: src/components/ModWatchtools.jsx:267
msgid "Get Users"
msgstr ""
#: src/components/ModIIDtools.jsx:20
msgid "You must enter a duration"
msgstr ""
#: src/components/ModIIDtools.jsx:24
msgid "You must enter an IID"
msgstr ""
#: src/components/ModIIDtools.jsx:53
msgid "IID Actions"
msgstr ""
#: src/components/ModIIDtools.jsx:80
msgid "Enter Reason"
msgstr ""
#: src/components/ModIIDtools.jsx:97
msgid "(0 = infinite)"
msgstr ""
#: src/components/Admintools.jsx:109 #: src/components/Admintools.jsx:109
msgid "IP Actions" msgid "IP Actions"
msgstr "" msgstr ""
@ -1378,76 +1450,8 @@ msgstr ""
msgid "User Name" msgid "User Name"
msgstr "" msgstr ""
#: src/components/SocialSettings.jsx:35 #: src/components/contextmenus/ChannelContextMenu.jsx:46
msgid "Block DMs" msgid "Mute"
msgstr ""
#: src/components/SocialSettings.jsx:42
msgid "Block all Private Messages"
msgstr ""
#: src/components/SocialSettings.jsx:44
msgid "Private"
msgstr ""
#: src/components/SocialSettings.jsx:51
msgid "Don't show me in global stats"
msgstr ""
#: src/components/SocialSettings.jsx:57
msgid "Unblock Users"
msgstr ""
#: src/components/SocialSettings.jsx:82
msgid "You have no users blocked"
msgstr ""
#: src/components/ModIIDtools.jsx:20
msgid "You must enter a duration"
msgstr ""
#: src/components/ModIIDtools.jsx:24
msgid "You must enter an IID"
msgstr ""
#: src/components/ModIIDtools.jsx:53
msgid "IID Actions"
msgstr ""
#: src/components/ModIIDtools.jsx:80
msgid "Enter Reason"
msgstr ""
#: src/components/ModIIDtools.jsx:97
msgid "(0 = infinite)"
msgstr ""
#: src/components/ModWatchtools.jsx:48
msgid "Interval is invalid"
msgstr ""
#: src/components/ModWatchtools.jsx:122
msgid "Check who placed in an area"
msgstr ""
#: src/components/ModWatchtools.jsx:123
msgid "Canvas"
msgstr ""
#: src/components/ModWatchtools.jsx:142
msgid "Interval"
msgstr ""
#: src/components/ModWatchtools.jsx:157
msgid "IID (optional)"
msgstr ""
#: src/components/ModWatchtools.jsx:236
msgid "Get Pixels"
msgstr ""
#: src/components/ModWatchtools.jsx:267
msgid "Get Users"
msgstr "" msgstr ""
#: src/components/contextmenus/UserContextMenu.jsx:49 #: src/components/contextmenus/UserContextMenu.jsx:49
@ -1462,10 +1466,6 @@ msgstr ""
msgid "Block" msgid "Block"
msgstr "" msgstr ""
#: src/components/contextmenus/ChannelContextMenu.jsx:46
msgid "Mute"
msgstr ""
#: src/components/windows/Help.jsx:15 #: src/components/windows/Help.jsx:15
#: src/components/windows/Settings.jsx:87 #: src/components/windows/Settings.jsx:87
msgctxt "keybinds" msgctxt "keybinds"
@ -1478,6 +1478,11 @@ msgctxt "keybinds"
msgid "X" msgid "X"
msgstr "" msgstr ""
#: src/components/windows/Settings.jsx:103
msgctxt "keybinds"
msgid "M"
msgstr ""
#: src/components/windows/Help.jsx:17 #: src/components/windows/Help.jsx:17
#: src/components/windows/Settings.jsx:158 #: src/components/windows/Settings.jsx:158
msgctxt "keybinds" msgctxt "keybinds"
@ -1527,9 +1532,4 @@ msgstr ""
#: src/components/windows/Help.jsx:32 #: src/components/windows/Help.jsx:32
msgctxt "keybinds" msgctxt "keybinds"
msgid "C" msgid "C"
msgstr ""
#: src/components/windows/Settings.jsx:103
msgctxt "keybinds"
msgid "M"
msgstr "" msgstr ""

View File

@ -17,8 +17,9 @@ import {
import pixelTransferController from './ui/PixelTransferController'; import pixelTransferController from './ui/PixelTransferController';
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/rendererFactory';
import socketClient from './socket/SocketClient'; import socketClient from './socket/SocketClient';
import { GC_INTERVAL } from './core/constants';
persistStore(store, {}, () => { persistStore(store, {}, () => {
window.addEventListener('message', store.dispatch); window.addEventListener('message', store.dispatch);
@ -50,7 +51,7 @@ persistStore(store, {}, () => {
store.dispatch(fetchMe()); store.dispatch(fetchMe());
socketClient.initialize(store); socketClient.initialize(store, pixelTransferController, getRenderer);
}); });
(function load() { (function load() {
@ -64,27 +65,8 @@ persistStore(store, {}, () => {
// garbage collection // garbage collection
setInterval(() => { setInterval(() => {
const renderer = getRenderer(); const renderer = getRenderer();
const chunks = renderer.getAllChunks(); renderer.gc();
if (chunks) { }, GC_INTERVAL);
const curTime = Date.now();
let cnt = 0;
chunks.forEach((value, key) => {
if (curTime > value.timestamp + 300000) {
const [zc, xc, yc] = value.cell;
if (!renderer.isChunkInView(zc, xc, yc)) {
cnt++;
if (value.isBasechunk) {
socketClient.deRegisterChunk([xc, yc]);
}
chunks.delete(key);
value.destructor();
}
}
});
// eslint-disable-next-line no-console
console.log('Garbage collection cleaned', cnt, 'chunks');
}
}, 300000);
document.removeEventListener('DOMContentLoaded', onLoad); document.removeEventListener('DOMContentLoaded', onLoad);
}; };

View File

@ -5,7 +5,7 @@
import React from 'react'; import React from 'react';
import { getRenderer } from '../ui/renderer'; import { getRenderer } from '../ui/rendererFactory';
const btnStyle = { const btnStyle = {
fontSize: 34, fontSize: 34,

View File

@ -8,7 +8,7 @@ import { MdFileDownload } from 'react-icons/md';
import fileDownload from 'js-file-download'; import fileDownload from 'js-file-download';
import { t } from 'ttag'; import { t } from 'ttag';
import { getRenderer } from '../../ui/renderer'; import { getRenderer } from '../../ui/rendererFactory';
/** /**
* https://jsfiddle.net/AbdiasSoftware/7PRNN/ * https://jsfiddle.net/AbdiasSoftware/7PRNN/

View File

@ -96,3 +96,8 @@ export const MAX_CHAT_MESSAGES = 100;
export const EVENT_USER_NAME = 'event'; export const EVENT_USER_NAME = 'event';
export const INFO_USER_NAME = 'info'; export const INFO_USER_NAME = 'info';
export const APISOCKET_USER_NAME = 'apisocket'; export const APISOCKET_USER_NAME = 'apisocket';
// maximum chunks to subscribe to
export const MAX_LOADED_CHUNKS = 2000;
export const MAX_CHUNK_AGE = 300000;
export const GC_INTERVAL = 300000;

View File

@ -10,7 +10,7 @@ import {
dehydrateRegCanvas, dehydrateRegCanvas,
dehydrateRegChunk, dehydrateRegChunk,
dehydrateRegMChunks, dehydrateRegMChunks,
dehydrateDeRegChunk, dehydrateDeRegMChunks,
dehydratePixelUpdate, dehydratePixelUpdate,
dehydratePing, dehydratePing,
} from './packets/client'; } from './packets/client';
@ -35,15 +35,15 @@ 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 = [];
class SocketClient { class SocketClient {
store = null;
pixelTransferController = null;
ws = null;
getRenderer;
constructor() { constructor() {
console.log('Creating WebSocketClient'); console.log('Creating WebSocketClient');
this.store = null;
this.ws = null;
this.channelId = 0; this.channelId = 0;
/* /*
* properties set in connect and open: * properties set in connect and open:
@ -59,12 +59,18 @@ class SocketClient {
setInterval(this.checkHealth, 2000); setInterval(this.checkHealth, 2000);
} }
initialize(store) { initialize(store, pixelTransferController, getRenderer) {
this.store = store; this.store = store;
if (pixelTransferController) {
this.pixelTransferController = pixelTransferController;
}
if (getRenderer) {
this.getRenderer = getRenderer;
}
return this.connect(); return this.connect();
} }
async connect() { connect() {
this.readyState = WebSocket.CONNECTING; this.readyState = WebSocket.CONNECTING;
if (this.ws) { if (this.ws) {
console.log('WebSocket already open, not starting'); console.log('WebSocket already open, not starting');
@ -139,8 +145,13 @@ class SocketClient {
this.send(dehydrateRegCanvas( this.send(dehydrateRegCanvas(
this.store.getState().canvas.canvasId, this.store.getState().canvas.canvasId,
)); ));
console.log(`Register ${chunks.length} chunks`); // register chunks
this.send(dehydrateRegMChunks(chunks)); const chunkids = this.getRenderer().recChunkIds;
if (chunkids.length) {
console.log(`Register ${chunkids.length} chunks`);
this.send(dehydrateRegMChunks(chunkids));
}
// flush queue
this.processMsgQueue(); this.processMsgQueue();
} }
@ -151,29 +162,21 @@ class SocketClient {
console.log( console.log(
`Notify websocket server that we changed canvas to ${canvasId}`, `Notify websocket server that we changed canvas to ${canvasId}`,
); );
chunks.length = 0;
this.send(dehydrateRegCanvas(canvasId)); this.send(dehydrateRegCanvas(canvasId));
} }
registerChunk(cell) { registerChunk(chunkid) {
const [i, j] = cell;
const chunkid = (i << 8) | j;
chunks.push(chunkid);
const buffer = dehydrateRegChunk(chunkid); const buffer = dehydrateRegChunk(chunkid);
if (this.readyState === WebSocket.OPEN) { if (this.readyState === WebSocket.OPEN) {
this.send(buffer); this.send(buffer);
} }
} }
deRegisterChunk(cell) { deRegisterChunks(chunkids) {
const [i, j] = cell; const buffer = dehydrateDeRegMChunks(chunkids);
const chunkid = (i << 8) | j;
const buffer = dehydrateDeRegChunk(chunkid);
if (this.readyState === WebSocket.OPEN) { if (this.readyState === WebSocket.OPEN) {
this.send(buffer); this.send(buffer);
} }
const pos = chunks.indexOf(chunkid);
if (~pos) chunks.splice(pos, 1);
} }
/* /*
@ -274,7 +277,11 @@ class SocketClient {
switch (opcode) { switch (opcode) {
case PIXEL_UPDATE_OP: case PIXEL_UPDATE_OP:
pixelTransferController.receivePixelUpdate(hydratePixelUpdate(data)); if (this.pixelTransferController) {
this.pixelTransferController.receivePixelUpdate(
hydratePixelUpdate(data),
);
}
break; break;
case PIXEL_RETURN_OP: { case PIXEL_RETURN_OP: {
const pos = this.reqQueue.findIndex((q) => q[0] === 'pu'); const pos = this.reqQueue.findIndex((q) => q[0] === 'pu');

View File

@ -218,30 +218,31 @@ export function requestBigChunk(center) {
}; };
} }
export function preLoadedBigChunk( export function removeChunks(chunks) {
center, return {
) { type: 'REMOVE_CHUNKS',
chunks,
};
}
export function preLoadedBigChunk(center) {
return { return {
type: 'PRE_LOADED_BIG_CHUNK', type: 'PRE_LOADED_BIG_CHUNK',
center, center,
}; };
} }
export function receiveBigChunk( export function receiveBigChunk(chunk) {
center,
chunk,
) {
return { return {
type: 'REC_BIG_CHUNK', type: 'REC_BIG_CHUNK',
center,
chunk, chunk,
}; };
} }
export function receiveBigChunkFailure(center, error) { export function receiveBigChunkFailure(chunk, error) {
return { return {
type: 'REC_BIG_CHUNK_FAILURE', type: 'REC_BIG_CHUNK_FAILURE',
center, chunk,
error, error,
}; };
} }

View File

@ -9,7 +9,7 @@
import { import {
getRenderer, getRenderer,
initRenderer, initRenderer,
} from '../../ui/renderer'; } from '../../ui/rendererFactory';
export default (store) => (next) => (action) => { export default (store) => (next) => (action) => {
const { type } = action; const { type } = action;

View File

@ -9,11 +9,21 @@ export default (store) => (next) => (action) => {
switch (action.type) { switch (action.type) {
case 'REC_BIG_CHUNK': case 'REC_BIG_CHUNK':
case 'REC_BIG_CHUNK_FAILURE': { case 'REC_BIG_CHUNK_FAILURE': {
if (!action.center) { const { chunk } = action;
break; if (chunk.recUpdates) {
SocketClient.registerChunk(action.chunk.id);
}
break;
}
case 'REMOVE_CHUNKS': {
const { chunks } = action;
const ids = chunks
.filter((chunk) => chunk.recUpdates)
.map((chunk) => chunk.id);
if (ids.length) {
SocketClient.deRegisterChunks(ids);
} }
const [, cx, cy] = action.center;
SocketClient.registerChunk([cx, cy]);
break; break;
} }

View File

@ -42,15 +42,7 @@ export default function fetching(
}; };
} }
case 'REC_BIG_CHUNK': { case 'REC_BIG_CHUNK':
const { fetchingChunks } = state;
return {
...state,
fetchingChunks: fetchingChunks - 1,
};
}
case 'REC_BIG_CHUNK_FAILURE': { case 'REC_BIG_CHUNK_FAILURE': {
const { fetchingChunks } = state; const { fetchingChunks } = state;

37
src/ui/Chunk.js Normal file
View File

@ -0,0 +1,37 @@
/*
* parent class for Chunk
*/
/* eslint-disable class-methods-use-this */
class Chunk {
// if chunk receives updates via websocket
recUpdates = false;
// timestamp of last touch,
// mustbe regularly updated for GC,
// either by touch() or by setting directly
timestamp;
// coordiantes
z;
i;
j;
constructor(z, i, j) {
this.timestamp = Date.now();
this.z = z;
this.i = i;
this.j = j;
}
get id() {
return (this.i << 8) | this.j;
}
touch() {
this.timestamp = Date.now();
}
destructor() {}
}
export default Chunk;

View File

@ -1,47 +1,30 @@
import { TILE_SIZE } from '../core/constants'; import { TILE_SIZE } from '../core/constants';
import Chunk from './Chunk';
class ChunkRGB { class Chunk2D extends Chunk {
// array of coords [zoom, cx, cy]
// just an identifier, could be removed
cell;
// HTMLCanvasElement of chunk // HTMLCanvasElement of chunk
image; image;
// boolean if chunk loeaded (request done) // boolean if chunk loeaded (request done)
ready; ready;
// boolean if chunk is empty // boolean if chunk is empty
isEmpty; isEmpty;
// number timestamp of last load for garbage collection
timestamp;
// palette of canvas // palette of canvas
palette; palette;
// boolean if it is basechunk, rather than zoomtile
isBasechunk;
constructor(palette, zoom = 0, cx = 0, cy = 0) { constructor(palette, zoom = 0, cx = 0, cy = 0) {
// isBasechunk gets set to true by REC_BIG_CHUNK super(zoom, cx, cy);
// if true => chunk got requested from api/chunk and
// receives websocket pixel updates
// if false => chunk is an zoomed png tile
this.isBasechunk = false;
this.palette = palette; this.palette = palette;
this.image = document.createElement('canvas'); this.image = document.createElement('canvas');
this.image.width = TILE_SIZE; this.image.width = TILE_SIZE;
this.image.height = TILE_SIZE; this.image.height = TILE_SIZE;
this.cell = [zoom, cx, cy];
this.ready = false; this.ready = false;
this.isEmpty = false; this.isEmpty = false;
this.timestamp = Date.now();
}
// eslint-disable-next-line class-methods-use-this
destructor() {
return null;
} }
// from Uint8Array // from Uint8Array
fromBuffer(chunkBuffer) { fromBuffer(chunkBuffer) {
this.isBasechunk = true; this.recUpdates = true;
const imageData = new ImageData(TILE_SIZE, TILE_SIZE); const imageData = new ImageData(TILE_SIZE, TILE_SIZE);
const imageView = new Uint32Array(imageData.data.buffer); const imageView = new Uint32Array(imageData.data.buffer);
const { abgr } = this.palette; const { abgr } = this.palette;
@ -110,7 +93,7 @@ class ChunkRGB {
} }
hasColorIn(cell, color) { hasColorIn(cell, color) {
const index = ChunkRGB.getIndexFromCell(cell); const index = Chunk2D.getIndexFromCell(cell);
const ctx = this.image.getContext('2d'); const ctx = this.image.getContext('2d');
const imageData = ctx.getImageData(0, 0, TILE_SIZE, TILE_SIZE); const imageData = ctx.getImageData(0, 0, TILE_SIZE, TILE_SIZE);
@ -128,4 +111,4 @@ class ChunkRGB {
} }
} }
export default ChunkRGB; export default Chunk2D;

View File

@ -8,6 +8,7 @@
import * as THREE from 'three'; import * as THREE from 'three';
import Chunk from './Chunk';
import { import {
THREE_TILE_SIZE, THREE_TILE_SIZE,
THREE_CANVAS_HEIGHT, THREE_CANVAS_HEIGHT,
@ -73,8 +74,7 @@ const material = new THREE.MeshLambertMaterial({
}); });
class Chunk { class Chunk3D extends Chunk {
cell; // Array
key; // string key; // string
ready = false; ready = false;
palette; // Object palette; // Object
@ -83,13 +83,12 @@ class Chunk {
faceCnt; // number faceCnt; // number
lastPixel; // number lastPixel; // number
heightMap; // Array heightMap; // Array
timestamp; // number
constructor(palette, key, xc, zc) { constructor(palette, key, xc, zc) {
this.cell = [0, xc, zc]; super(0, xc, zc);
this.recUpdates = true;
this.key = key; this.key = key;
this.palette = palette; this.palette = palette;
this.timestamp = Date.now();
} }
destructor() { destructor() {
@ -106,7 +105,7 @@ class Chunk {
if (y < 0) return 1; if (y < 0) return 1;
// z and y are swapped in api/pixel for compatibility // z and y are swapped in api/pixel for compatibility
// with 2D canvas // with 2D canvas
const offset = Chunk.getOffsetOfVoxel(x, y, z); const offset = Chunk3D.getOffsetOfVoxel(x, y, z);
return this.buffer[offset]; return this.buffer[offset];
} }
@ -139,7 +138,7 @@ class Chunk {
} }
} }
console.log(`Created buffer with ${cnt} voxels`); console.log(`Created buffer with ${cnt} voxels`);
const [faceCnt, lastPixel, heightMap] = Chunk.calculateMetaData(this.buffer); const [faceCnt, lastPixel, heightMap] = Chunk3D.calculateMetaData(this.buffer);
this.faceCnt = faceCnt; this.faceCnt = faceCnt;
this.lastPixel = lastPixel; this.lastPixel = lastPixel;
this.heightMap = heightMap; this.heightMap = heightMap;
@ -196,7 +195,7 @@ class Chunk {
if (heighestPixel > totalHeight) { if (heighestPixel > totalHeight) {
// last total pixel // last total pixel
totalHeight = heighestPixel; totalHeight = heighestPixel;
lastPixel = Chunk.getOffsetOfVoxel(x, heighestPixel, z); lastPixel = Chunk3D.getOffsetOfVoxel(x, heighestPixel, z);
} }
} }
} }
@ -233,7 +232,7 @@ class Chunk {
} }
setVoxel(x, y, z, clr) { setVoxel(x, y, z, clr) {
const offset = Chunk.getOffsetOfVoxel(x, y, z); const offset = Chunk3D.getOffsetOfVoxel(x, y, z);
this.setVoxelByOffset(offset, clr); this.setVoxelByOffset(offset, clr);
} }
@ -247,7 +246,7 @@ class Chunk {
chunkBuffer.set(chunkBufferInpt); chunkBuffer.set(chunkBufferInpt);
} }
this.buffer = chunkBuffer; this.buffer = chunkBuffer;
const [faceCnt, lastPixel, heightMap] = Chunk.calculateMetaData( const [faceCnt, lastPixel, heightMap] = Chunk3D.calculateMetaData(
chunkBuffer, chunkBuffer,
); );
this.faceCnt = faceCnt; this.faceCnt = faceCnt;
@ -383,4 +382,4 @@ class Chunk {
} }
} }
export default Chunk; export default Chunk3D;

131
src/ui/ChunkLoader.js Normal file
View File

@ -0,0 +1,131 @@
/*
* parent class for storing chunks
*/
import {
requestBigChunk,
receiveBigChunk,
receiveBigChunkFailure,
removeChunks,
// fetching of preLoad chunk triggers rerender already
// lets keep this out for now, until needed
// preLoadedBigChunk,
} from '../store/actions';
import {
MAX_LOADED_CHUNKS,
MAX_CHUNK_AGE,
} from '../core/constants';
/* eslint-disable class-methods-use-this */
class ChunkLoader {
#store;
// Map of chunkId: chunkRGB
#chunks;
#canvasId;
constructor(store, canvasId) {
this.#store = store;
this.#chunks = new Map();
this.#canvasId = canvasId;
}
get canvasId() {
return this.#canvasId;
}
get recChunkIds() {
const ids = [];
this.#chunks.forEach((chunk) => {
if (chunk.recUpdates) {
ids.push(chunk.id);
}
});
return ids;
}
destructor() {
this.#chunks.forEach((chunk) => {
chunk.destructor();
});
this.#chunks = new Map();
}
cget(key) {
return this.#chunks.get(key);
}
cset(key, chunk) {
/*
* chunks are not neccessarily fully loaded here,
* but they are in bcRecChunk
*/
this.#chunks.set(key, chunk);
}
bcReqChunk(chunk) {
const { z, i, j } = chunk;
this.#store.dispatch(requestBigChunk([z, i, j]));
}
bcRecChunk(chunk) {
this.#store.dispatch(receiveBigChunk(chunk));
const { size } = this.#chunks;
if (size > MAX_LOADED_CHUNKS) {
// hysteresis of 10%
const amountToRem = size - Math.floor(MAX_LOADED_CHUNKS * 0.9);
const chunksByTs = Array.from(this.#chunks.entries())
.sort((a, b) => a[1].timestamp - b[1].timestamp)
.slice(0, amountToRem);
chunksByTs.forEach(([key]) => {
this.#chunks.delete(key);
});
const remChunks = chunksByTs.map((c) => c[1]);
this.bcRemoveChunks(remChunks);
// eslint-disable-next-line max-len, no-console
console.log(`Cleared ${remChunks.length} to cut amount of chunks to ${this.#chunks.size}`);
}
}
/*
* 404 (aka empty chunks) also trigger this
*/
bcReqChunkFail(chunk, error) {
this.#store.dispatch(receiveBigChunkFailure(chunk, error.message));
}
/*
* @param chunks chunks[]
*/
bcRemoveChunks(chunks) {
this.#store.dispatch(removeChunks(chunks));
}
/*
* delete chunks that didn't get seen for
* more than 5min
*/
gc(renderer) {
const threshold = Date.now() - MAX_CHUNK_AGE;
const chunks = this.#chunks;
const remChunks = [];
chunks.forEach((chunk, key) => {
if (threshold > chunk.timestamp) {
const {
z,
i,
j,
} = chunk;
if (!renderer.isChunkInView(z, i, j)) {
remChunks.push(chunk);
chunks.delete(key);
chunk.destructor();
}
}
});
this.bcRemoveChunks(remChunks);
// eslint-disable-next-line no-console
console.log(`GC cleaned ${remChunks.length} chunks.`);
}
}
export default ChunkLoader;

View File

@ -2,19 +2,14 @@
* Fetching and storing of 2D chunks * Fetching and storing of 2D chunks
*/ */
import ChunkRGB from './ChunkRGB'; import ChunkLoader from './ChunkLoader';
import Chunk from './Chunk2D';
import { TILE_SIZE, TILE_ZOOM_LEVEL } from '../core/constants'; import { TILE_SIZE, TILE_ZOOM_LEVEL } from '../core/constants';
import { shardOrigin } from '../store/actions/fetch'; import { shardOrigin } from '../store/actions/fetch';
import { import {
loadingTiles, loadingTiles,
loadImage, loadImage,
} from './loadImage'; } from './loadImage';
import {
requestBigChunk,
receiveBigChunk,
receiveBigChunkFailure,
// preLoadedBigChunk,
} from '../store/actions';
import { import {
getMaxTiledZoom, getMaxTiledZoom,
getCellInsideChunk, getCellInsideChunk,
@ -22,18 +17,14 @@ import {
getHistoricalCanvasSize, getHistoricalCanvasSize,
} from '../core/utils'; } from '../core/utils';
class ChunkLoader { class ChunkLoader2D extends ChunkLoader {
store = null;
canvasId;
canvasMaxTiledZoom; canvasMaxTiledZoom;
historicalMaxTiledZooms; historicalMaxTiledZooms;
palette; palette;
canvasSize; canvasSize;
chunks;
constructor(store, canvasId, palette, canvasSize, historicalSizes) { constructor(store, canvasId, palette, canvasSize, historicalSizes) {
this.store = store; super(store, canvasId);
this.canvasId = canvasId;
this.palette = palette; this.palette = palette;
this.canvasSize = canvasSize; this.canvasSize = canvasSize;
this.canvasMaxTiledZoom = getMaxTiledZoom(canvasSize); this.canvasMaxTiledZoom = getMaxTiledZoom(canvasSize);
@ -46,12 +37,6 @@ class ChunkLoader {
} else { } else {
this.historicalMaxTiledZooms = []; this.historicalMaxTiledZooms = [];
} }
this.chunks = new Map();
}
getAllChunks() {
return this.chunks;
} }
getPixelUpdate( getPixelUpdate(
@ -60,7 +45,7 @@ class ChunkLoader {
offset, offset,
color, color,
) { ) {
const chunk = this.chunks.get(`${this.canvasMaxTiledZoom}:${cx}:${cy}`); const chunk = this.cget(`${this.canvasMaxTiledZoom}:${cx}:${cy}`);
if (chunk) { if (chunk) {
const ix = offset % TILE_SIZE; const ix = offset % TILE_SIZE;
const iy = Math.floor(offset / TILE_SIZE); const iy = Math.floor(offset / TILE_SIZE);
@ -75,7 +60,7 @@ class ChunkLoader {
const { canvasSize } = this; const { canvasSize } = this;
const [cx, cy] = getChunkOfPixel(canvasSize, x, y); const [cx, cy] = getChunkOfPixel(canvasSize, x, y);
const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`; const key = `${this.canvasMaxTiledZoom}:${cx}:${cy}`;
const chunk = this.chunks.get(key); const chunk = this.cget(key);
if (!chunk) { if (!chunk) {
return 0; return 0;
} }
@ -107,7 +92,7 @@ class ChunkLoader {
if (historicalTime && historicalTime !== '0000') { if (historicalTime && historicalTime !== '0000') {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const incrementialChunkKey = `${historicalDate}${historicalTime}:${cx}:${cy}`; const incrementialChunkKey = `${historicalDate}${historicalTime}:${cx}:${cy}`;
const incrementialChunk = this.chunks.get(incrementialChunkKey); const incrementialChunk = this.cget(incrementialChunkKey);
if (incrementialChunk) { if (incrementialChunk) {
const incrementialColor = incrementialChunk.getColorIndex(px, false); const incrementialColor = incrementialChunk.getColorIndex(px, false);
incrementialChunk.timestamp = curTime; incrementialChunk.timestamp = curTime;
@ -118,7 +103,7 @@ class ChunkLoader {
} }
const chunkKey = `${historicalDate}:${cx}:${cy}`; const chunkKey = `${historicalDate}:${cx}:${cy}`;
const chunk = this.chunks.get(chunkKey); const chunk = this.cget(chunkKey);
if (!chunk) { if (!chunk) {
return null; return null;
} }
@ -157,9 +142,6 @@ class ChunkLoader {
const pcX = (cx % zoomDiffAbs) * TILE_SIZE / zoomDiffAbs; const pcX = (cx % zoomDiffAbs) * TILE_SIZE / zoomDiffAbs;
const pcY = (cy % zoomDiffAbs) * TILE_SIZE / zoomDiffAbs; const pcY = (cy % zoomDiffAbs) * TILE_SIZE / zoomDiffAbs;
chunkRGB.preLoad(plChunk, zoomDiffAbs, pcX, pcY); chunkRGB.preLoad(plChunk, zoomDiffAbs, pcX, pcY);
// fetching of preLoad chunk triggers rerender already
// lets keep this commented out for now
// this.store.dispatch(preLoadedBigChunk([zoom, cx, cy]));
return chunkRGB.image; return chunkRGB.image;
} }
} catch (error) { } catch (error) {
@ -181,17 +163,17 @@ class ChunkLoader {
chunkPreLoading = true, chunkPreLoading = true,
) { ) {
const chunkKey = `${zoom}:${cx}:${cy}`; const chunkKey = `${zoom}:${cx}:${cy}`;
let chunkRGB = this.chunks.get(chunkKey); let chunkRGB = this.cget(chunkKey);
if (chunkRGB) { if (chunkRGB) {
if (chunkRGB.ready) { if (chunkRGB.ready) {
return chunkRGB.image; return chunkRGB.image;
} }
} else if (fetch) { } else if (fetch) {
chunkRGB = new ChunkRGB(this.palette, zoom, cx, cy); chunkRGB = new Chunk(this.palette, zoom, cx, cy);
this.chunks.set(chunkKey, chunkRGB); this.cset(chunkKey, chunkRGB);
// fetch chunk // fetch chunk
if (this.canvasMaxTiledZoom === zoom) { if (this.canvasMaxTiledZoom === zoom) {
this.fetchBaseChunk(zoom, cx, cy, chunkRGB); this.fetchBaseChunk(cx, cy, chunkRGB);
} else { } else {
this.fetchTile(zoom, cx, cy, chunkRGB); this.fetchTile(zoom, cx, cy, chunkRGB);
} }
@ -208,7 +190,7 @@ class ChunkLoader {
? `${historicalDate}${historicalTime}` ? `${historicalDate}${historicalTime}`
: historicalDate; : historicalDate;
chunkKey += `:${cx}:${cy}`; chunkKey += `:${cx}:${cy}`;
const chunk = this.chunks.get(chunkKey); const chunk = this.cget(chunkKey);
const { canvasId } = this; const { canvasId } = this;
if (chunk) { if (chunk) {
if (chunk.ready) { if (chunk.ready) {
@ -222,19 +204,18 @@ class ChunkLoader {
this.historicalMaxTiledZooms, this.historicalMaxTiledZooms,
); );
// fetch tile // fetch tile
const chunkRGB = new ChunkRGB( const chunkRGB = new Chunk(
this.palette, this.palette,
historicalCanvasMaxTiledZoom, historicalCanvasMaxTiledZoom,
cx, cx,
cy, cy,
); );
this.chunks.set(chunkKey, chunkRGB); this.cset(chunkKey, chunkRGB);
this.fetchHistoricalChunk( this.fetchHistoricalChunk(
cx, cx,
cy, cy,
historicalDate, historicalDate,
historicalTime, historicalTime,
historicalCanvasMaxTiledZoom,
chunkRGB, chunkRGB,
); );
} }
@ -246,12 +227,10 @@ class ChunkLoader {
cy, cy,
historicalDate, historicalDate,
historicalTime, historicalTime,
historicalCanvasMaxTiledZoom,
chunkRGB, chunkRGB,
) { ) {
const { canvasId } = this; const { canvasId } = this;
const center = [historicalCanvasMaxTiledZoom, cx, cy];
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
let url = `${window.ssv.backupurl}/${historicalDate.slice(0, 4)}/${historicalDate.slice(4, 6)}/${historicalDate.slice(6)}/`; let url = `${window.ssv.backupurl}/${historicalDate.slice(0, 4)}/${historicalDate.slice(4, 6)}/${historicalDate.slice(6)}/`;
if (historicalTime) { if (historicalTime) {
@ -261,13 +240,13 @@ class ChunkLoader {
// full tiles // full tiles
url += `${canvasId}/tiles/${cx}/${cy}.png`; url += `${canvasId}/tiles/${cx}/${cy}.png`;
} }
this.store.dispatch(requestBigChunk(center)); this.bcReqChunk(chunkRGB);
try { try {
const img = await loadImage(url); const img = await loadImage(url);
chunkRGB.fromImage(img); chunkRGB.fromImage(img);
this.store.dispatch(receiveBigChunk(center, chunkRGB)); this.bcRecChunk(chunkRGB);
} catch (error) { } catch (error) {
this.store.dispatch(receiveBigChunkFailure(center, error.message)); this.bcReqChunkFail(chunkRGB, error);
if (historicalTime) { if (historicalTime) {
chunkRGB.empty(true); chunkRGB.empty(true);
} else { } else {
@ -276,9 +255,8 @@ class ChunkLoader {
} }
} }
async fetchBaseChunk(zoom, cx, cy, chunkRGB) { async fetchBaseChunk(cx, cy, chunkRGB) {
const center = [zoom, cx, cy]; this.bcReqChunk(chunkRGB);
this.store.dispatch(requestBigChunk(center));
try { try {
const url = `${shardOrigin}/chunks/${this.canvasId}/${cx}/${cy}.bmp`; const url = `${shardOrigin}/chunks/${this.canvasId}/${cx}/${cy}.bmp`;
const response = await fetch(url); const response = await fetch(url);
@ -290,30 +268,30 @@ class ChunkLoader {
} else { } else {
throw new Error('Chunk response was invalid'); throw new Error('Chunk response was invalid');
} }
this.store.dispatch(receiveBigChunk(center, chunkRGB)); this.bcRecChunk(chunkRGB);
} else { } else {
throw new Error('Network response was not ok.'); throw new Error('Network response was not ok.');
} }
} catch (error) { } catch (error) {
chunkRGB.empty(); chunkRGB.empty();
this.store.dispatch(receiveBigChunkFailure(center, error.message)); chunkRGB.recUpdates = true;
this.bcReqChunkFail(chunkRGB, error);
} }
} }
async fetchTile(zoom, cx, cy, chunkRGB) { async fetchTile(zoom, cx, cy, chunkRGB) {
const center = [zoom, cx, cy]; this.bcReqChunk(chunkRGB);
this.store.dispatch(requestBigChunk(center));
try { try {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const url = `/tiles/${this.canvasId}/${zoom}/${cx}/${cy}.webp`; const url = `/tiles/${this.canvasId}/${zoom}/${cx}/${cy}.webp`;
const img = await loadImage(url); const img = await loadImage(url);
chunkRGB.fromImage(img); chunkRGB.fromImage(img);
this.store.dispatch(receiveBigChunk(center, chunkRGB)); this.bcRecChunk(chunkRGB);
} catch (error) { } catch (error) {
this.store.dispatch(receiveBigChunkFailure(center, error.message));
chunkRGB.empty(); chunkRGB.empty();
this.bcReqChunkFail(chunkRGB, error);
} }
} }
} }
export default ChunkLoader; export default ChunkLoader2D;

View File

@ -3,37 +3,21 @@
* *
*/ */
import Chunk from './ChunkRGB3D'; import ChunkLoader from './ChunkLoader';
import { import Chunk from './Chunk3D';
requestBigChunk,
receiveBigChunk,
receiveBigChunkFailure,
} from '../store/actions';
import { import {
getChunkOfPixel, getChunkOfPixel,
getOffsetOfPixel, getOffsetOfPixel,
} from '../core/utils'; } from '../core/utils';
import { shardOrigin } from '../store/actions/fetch'; import { shardOrigin } from '../store/actions/fetch';
class ChunkLoader { class ChunkLoader3D extends ChunkLoader {
store = null;
canvasId;
palette; palette;
chunks; // Map<string, Chunk>
constructor(store, canvasId, palette, canvasSize) { constructor(store, canvasId, palette, canvasSize) {
this.store = store; super(store, canvasId);
this.canvasId = canvasId;
this.palette = palette; this.palette = palette;
this.canvasSize = canvasSize; this.canvasSize = canvasSize;
this.chunks = new Map();
}
destructor() {
this.chunks.forEach((chunk) => {
chunk.destructor();
});
this.chunks = new Map();
} }
getVoxel(x, y, z) { getVoxel(x, y, z) {
@ -41,7 +25,7 @@ class ChunkLoader {
const [xc, zc] = getChunkOfPixel(canvasSize, x, y, z); const [xc, zc] = getChunkOfPixel(canvasSize, x, y, z);
const offset = getOffsetOfPixel(canvasSize, x, y, z); const offset = getOffsetOfPixel(canvasSize, x, y, z);
const key = `${xc}:${zc}`; const key = `${xc}:${zc}`;
const chunk = this.chunks.get(key); const chunk = this.cget(key);
if (chunk) { if (chunk) {
const clr = chunk.getVoxelByOffset(offset); const clr = chunk.getVoxelByOffset(offset);
return clr; return clr;
@ -49,10 +33,6 @@ class ChunkLoader {
return 0; return 0;
} }
getAllChunks() {
return this.chunks;
}
getVoxelUpdate( getVoxelUpdate(
xc, xc,
zc, zc,
@ -60,7 +40,7 @@ class ChunkLoader {
color, color,
) { ) {
const key = `${xc}:${zc}`; const key = `${xc}:${zc}`;
const chunk = this.chunks.get(key); const chunk = this.cget(key);
if (chunk) { if (chunk) {
chunk.setVoxelByOffset(offset, color); chunk.setVoxelByOffset(offset, color);
} }
@ -69,10 +49,10 @@ class ChunkLoader {
getChunk(xc, zc, fetch) { getChunk(xc, zc, fetch) {
const chunkKey = `${xc}:${zc}`; const chunkKey = `${xc}:${zc}`;
// console.log(`Get chunk ${chunkKey}`); // console.log(`Get chunk ${chunkKey}`);
let chunk = this.chunks.get(chunkKey); let chunk = this.cget(chunkKey);
if (chunk) { if (chunk) {
if (chunk.ready) { if (chunk.ready) {
chunk.timestamp = Date.now(); chunk.touch();
return chunk.mesh; return chunk.mesh;
} }
return null; return null;
@ -80,15 +60,14 @@ class ChunkLoader {
if (fetch) { if (fetch) {
// fetch chunk // fetch chunk
chunk = new Chunk(this.palette, chunkKey, xc, zc); chunk = new Chunk(this.palette, chunkKey, xc, zc);
this.chunks.set(chunkKey, chunk); this.cset(chunkKey, chunk);
this.fetchChunk(xc, zc, chunk); this.fetchChunk(xc, zc, chunk);
} }
return null; return null;
} }
async fetchChunk(cx, cz, chunk) { async fetchChunk(cx, cz, chunk) {
const center = [0, cx, cz]; this.bcReqChunk(chunk);
this.store.dispatch(requestBigChunk(center));
try { try {
const url = `${shardOrigin}/chunks/${this.canvasId}/${cx}/${cz}.bmp`; const url = `${shardOrigin}/chunks/${this.canvasId}/${cx}/${cz}.bmp`;
const response = await fetch(url); const response = await fetch(url);
@ -100,15 +79,15 @@ class ChunkLoader {
} else { } else {
throw new Error('Chunk response was invalid'); throw new Error('Chunk response was invalid');
} }
this.store.dispatch(receiveBigChunk(center, chunk)); this.bcRecChunk(chunk);
} else { } else {
throw new Error('Network response was not ok.'); throw new Error('Network response was not ok.');
} }
} catch (error) { } catch (error) {
chunk.empty(); chunk.empty();
this.store.dispatch(receiveBigChunkFailure(center, error)); this.bcReqChunkFail(chunk, error);
} }
} }
} }
export default ChunkLoader; export default ChunkLoader3D;

53
src/ui/Renderer.js Normal file
View File

@ -0,0 +1,53 @@
/*
* parent class for Renderer
*/
/* eslint-disable class-methods-use-this */
class Renderer {
store;
// needs to be known for lazy loading THREE
is3D = null;
// chunk loader must be set by subclass
chunkLoader = null;
constructor(store) {
this.store = store;
}
get chunks() {
return this.chunkLoader.chunks;
}
get recChunkIds() {
if (!this.chunkLoader) {
return [];
}
return this.chunkLoader.recChunkIds;
}
destructor() {
if (this.chunkLoader) {
this.chunkLoader.destructor();
}
}
render() {}
renderPixel() {}
updateCanvasData() {}
isChunkInView() {
return true;
}
gc() {
if (!this.chunkLoader) {
return;
}
this.chunkLoader.gc(this);
}
}
export default Renderer;

View File

@ -18,14 +18,14 @@ import {
} from './render2Delements'; } from './render2Delements';
import PixelPainterControls from '../controls/PixelPainterControls'; import PixelPainterControls from '../controls/PixelPainterControls';
import Renderer from './Renderer';
import ChunkLoader from './ChunkLoader2D'; import ChunkLoader from './ChunkLoader2D';
import pixelNotify from './PixelNotify'; import pixelNotify from './PixelNotify';
class Renderer { class Renderer2D extends Renderer {
is3D = false; is3D = false;
// //
canvasId = null; canvasId = null;
chunkLoader = null;
//-- //--
centerChunk; centerChunk;
tiledScale; tiledScale;
@ -33,7 +33,6 @@ class Renderer {
hover; hover;
//-- //--
viewport = null; viewport = null;
store;
//-- //--
forceNextRender; forceNextRender;
forceNextSubrender; forceNextSubrender;
@ -43,6 +42,7 @@ class Renderer {
oldHistoricalTime; oldHistoricalTime;
constructor(store) { constructor(store) {
super(store);
this.centerChunk = [null, null]; this.centerChunk = [null, null];
this.tiledScale = 0; this.tiledScale = 0;
this.tiledZoom = 4; this.tiledZoom = 4;
@ -71,23 +71,23 @@ class Renderer {
context.fillStyle = '#000000'; context.fillStyle = '#000000';
context.fillRect(0, 0, this.canvas.width, this.canvas.height); context.fillRect(0, 0, this.canvas.width, this.canvas.height);
//-- //--
this.setStore(store); const state = store.getState();
this.updateCanvasData(state);
this.updateScale(state);
this.controls = new PixelPainterControls(this, this.viewport, store);
} }
destructor() { destructor() {
this.controls.dispose(); this.controls.dispose();
window.removeEventListener('resize', this.onWindowResize); window.removeEventListener('resize', this.onWindowResize);
this.viewport.remove(); this.viewport.remove();
super.destructor();
} }
getViewport() { getViewport() {
return this.viewport; return this.viewport;
} }
getAllChunks() {
return this.chunkLoader.getAllChunks();
}
onWindowResize() { onWindowResize() {
this.viewport.width = window.innerWidth; this.viewport.width = window.innerWidth;
this.viewport.height = window.innerHeight; this.viewport.height = window.innerHeight;
@ -111,15 +111,6 @@ class Renderer {
this.forceNextRender = true; this.forceNextRender = true;
} }
// HAS to be set before any rendering can happen
setStore(store) {
this.store = store;
const state = store.getState();
this.updateCanvasData(state);
this.updateScale(state);
this.controls = new PixelPainterControls(this, this.viewport, store);
}
updateCanvasData(state) { updateCanvasData(state) {
const { const {
canvasId, canvasId,
@ -732,4 +723,4 @@ class Renderer {
} }
export default Renderer; export default Renderer2D;

View File

@ -8,6 +8,7 @@ import Sky from './Sky';
import InfiniteGridHelper from './InfiniteGridHelper'; import InfiniteGridHelper from './InfiniteGridHelper';
import VoxelPainterControls from '../controls/VoxelPainterControls'; import VoxelPainterControls from '../controls/VoxelPainterControls';
import Renderer from './Renderer';
import ChunkLoader from './ChunkLoader3D'; import ChunkLoader from './ChunkLoader3D';
import { import {
getChunkOfPixel, getChunkOfPixel,
@ -24,11 +25,9 @@ import pixelTransferController from './PixelTransferController';
const renderDistance = 150; const renderDistance = 150;
class Renderer { class Renderer3D extends Renderer {
is3D = true; is3D = true;
//-- //--
store;
//--
scene; scene;
camera; camera;
rollOverMesh; rollOverMesh;
@ -48,14 +47,12 @@ class Renderer {
pressCdTime; pressCdTime;
multitap; multitap;
//-- //--
chunkLoader = null;
forceNextRender = false; forceNextRender = false;
constructor(store) { constructor(store) {
this.store = store; super(store);
const state = store.getState(); const state = store.getState();
this.objects = []; this.objects = [];
this.chunkLoader = null;
// camera // camera
const camera = new THREE.PerspectiveCamera( const camera = new THREE.PerspectiveCamera(
@ -212,9 +209,7 @@ class Renderer {
const { domElement } = this.threeRenderer; const { domElement } = this.threeRenderer;
this.threeRenderer = null; this.threeRenderer = null;
domElement.remove(); domElement.remove();
if (this.chunkLoader) { super.destructor();
this.chunkLoader.destructor();
}
} }
updateView() { updateView() {
@ -266,11 +261,6 @@ class Renderer {
return null; return null;
} }
// TODO check if GC even works on 3D canvas
getAllChunks() {
return this.chunkLoader.getAllChunks();
}
renderPixel( renderPixel(
i, i,
j, j,
@ -669,4 +659,4 @@ class Renderer {
} }
} }
export default Renderer; export default Renderer3D;

View File

@ -7,17 +7,12 @@
import { t } from 'ttag'; import { t } from 'ttag';
import Renderer from './Renderer';
import Renderer2D from './Renderer2D'; import Renderer2D from './Renderer2D';
import { pAlert } from '../store/actions'; import { pAlert } from '../store/actions';
import { isWebGL2Available } from '../core/utils'; import { isWebGL2Available } from '../core/utils';
const dummyRenderer = { const dummyRenderer = new Renderer();
is3D: null,
render: () => null,
destructor: () => null,
renderPixel: () => null,
updateCanvasData: () => null,
};
let renderer = dummyRenderer; let renderer = dummyRenderer;