diff --git a/public/preview0.png b/public/preview0.png new file mode 100644 index 0000000..4936177 Binary files /dev/null and b/public/preview0.png differ diff --git a/public/preview1.png b/public/preview1.png new file mode 100644 index 0000000..a2c1e78 Binary files /dev/null and b/public/preview1.png differ diff --git a/public/preview2.png b/public/preview2.png new file mode 100644 index 0000000..40be716 Binary files /dev/null and b/public/preview2.png differ diff --git a/public/voxeltest.html b/public/voxeltest.html deleted file mode 100644 index e98022e..0000000 --- a/public/voxeltest.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - three.js webgl - interactive - voxel painter - - - - - - - - - diff --git a/src/actions/index.js b/src/actions/index.js index d053f11..227c2ed 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -8,12 +8,6 @@ import type { import type { Cell } from '../core/Cell'; import type { ColorIndex } from '../core/Palette'; -import { loadImage } from '../ui/loadImage'; -import { - getColorIndexOfPixel, -} from '../core/utils'; - - export function sweetAlert( title: string, text: string, @@ -228,7 +222,6 @@ export function requestPlacePixel( y, clr: color, token, - a: x + y + 8, }); dispatch(setPlaceAllowed(false)); @@ -295,9 +288,7 @@ export function tryPlacePixel( ? state.gui.selectedColor : color; - if (getColorIndexOfPixel(getState(), coordinates) !== selectedColor) { - dispatch(requestPlacePixel(canvasId, coordinates, selectedColor)); - } + dispatch(requestPlacePixel(canvasId, coordinates, selectedColor)); }; } @@ -376,37 +367,23 @@ export function zoomOut(zoompoint): ThunkAction { }; } -function requestBigChunk(center: Cell): Action { +export function requestBigChunk(center: Cell): Action { return { type: 'REQUEST_BIG_CHUNK', center, }; } -function receiveBigChunk( +export function receiveBigChunk( center: Cell, - arrayBuffer: ArrayBuffer, ): Action { return { type: 'RECEIVE_BIG_CHUNK', center, - arrayBuffer, }; } -function receiveImageTile( - center: Cell, - tile: Image, -): Action { - return { - type: 'RECEIVE_IMAGE_TILE', - center, - tile, - }; -} - - -function receiveBigChunkFailure(center: Cell, error: Error): Action { +export function receiveBigChunkFailure(center: Cell, error: Error): Action { return { type: 'RECEIVE_BIG_CHUNK_FAILURE', center, @@ -414,74 +391,6 @@ function receiveBigChunkFailure(center: Cell, error: Error): Action { }; } -export function fetchTile(canvasId, center: Cell): PromiseAction { - const [cz, cx, cy] = center; - - return async (dispatch) => { - dispatch(requestBigChunk(center)); - try { - const url = `/tiles/${canvasId}/${cz}/${cx}/${cy}.png`; - const img = await loadImage(url); - dispatch(receiveImageTile(center, img)); - } catch (error) { - dispatch(receiveBigChunkFailure(center, error)); - } - }; -} - -export function fetchHistoricalChunk( - canvasId: number, - center: Cell, - historicalDate: string, - historicalTime: string, -): PromiseAction { - const [cx, cy] = center; - - return async (dispatch) => { - let url = `${window.backupurl}/${historicalDate}/`; - let zkey; - if (historicalTime) { - // incremential tiles - zkey = `${historicalDate}${historicalTime}`; - url += `${canvasId}/${historicalTime}/${cx}/${cy}.png`; - } else { - // full tiles - zkey = historicalDate; - url += `${canvasId}/tiles/${cx}/${cy}.png`; - } - const keyValues = [zkey, cx, cy]; - dispatch(requestBigChunk(keyValues)); - try { - const img = await loadImage(url); - dispatch(receiveImageTile(keyValues, img)); - } catch (error) { - dispatch(receiveBigChunkFailure(keyValues, error)); - } - }; -} - -export function fetchChunk(canvasId, center: Cell): PromiseAction { - const [, cx, cy] = center; - - return async (dispatch) => { - dispatch(requestBigChunk(center)); - try { - const url = `/chunks/${canvasId}/${cx}/${cy}.bmp`; - const response = await fetch(url); - if (response.ok) { - const arrayBuffer = await response.arrayBuffer(); - dispatch(receiveBigChunk(center, arrayBuffer)); - } else { - const error = new Error('Network response was not ok.'); - dispatch(receiveBigChunkFailure(center, error)); - } - } catch (error) { - dispatch(receiveBigChunkFailure(center, error)); - } - }; -} - - export function receiveCoolDown( waitSeconds: number, ): Action { @@ -491,7 +400,6 @@ export function receiveCoolDown( }; } - export function receivePixelUpdate( i: number, j: number, @@ -678,6 +586,10 @@ export function showHelpModal(): Action { return showModal('HELP'); } +export function showCanvasSelectionModal(): Action { + return showModal('CANVAS_SELECTION'); +} + export function showChatModal(): Action { if (window.innerWidth > 604) { return toggleChatBox(); } return showModal('CHAT'); @@ -714,10 +626,3 @@ export function urlChange(): PromiseAction { dispatch(reloadUrl()); }; } - -export function switchCanvas(canvasId: number): PromiseAction { - return async (dispatch) => { - await dispatch(selectCanvas(canvasId)); - dispatch(onViewFinishChange()); - }; -} diff --git a/src/actions/types.js b/src/actions/types.js index 42341a1..f361427 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -41,8 +41,7 @@ export type Action = | { type: 'SET_VIEW_COORDINATES', view: Cell } | { type: 'SET_SCALE', scale: number, zoompoint: Cell } | { type: 'REQUEST_BIG_CHUNK', center: Cell } - | { type: 'RECEIVE_BIG_CHUNK', center: Cell, arrayBuffer: ArrayBuffer } - | { type: 'RECEIVE_IMAGE_TILE', center: Cell, tile: Image } + | { type: 'RECEIVE_BIG_CHUNK', center: Cell } | { type: 'RECEIVE_BIG_CHUNK_FAILURE', center: Cell, error: Error } | { type: 'RECEIVE_COOLDOWN', waitSeconds: number } | { type: 'RECEIVE_PIXEL_UPDATE', diff --git a/src/canvases.json b/src/canvases.json index 1ddf9b8..c6a3cf6 100644 --- a/src/canvases.json +++ b/src/canvases.json @@ -1,6 +1,7 @@ { "0": { "ident":"d", + "title": "Earth", "colors": [ [ 202, 227, 255 ], [ 255, 255, 255 ], @@ -41,10 +42,12 @@ "pcd" : 7000, "cds": 60000, "req": -1, - "sd": "2020-01-08" + "sd": "2020-01-08", + "desc": "Our main canvas, a huge map of the world. Place everywhere you like" }, "1": { "ident": "m", + "title": "Moon", "colors" : [ [ 49, 46, 47 ], [ 99, 92, 90 ], @@ -85,6 +88,54 @@ "pcd": 15000, "cds": 900000, "req": 8000, - "sd": "2020-01-08" + "sd": "2020-01-08", + "desc": "Moon canvas with a pastel tone palette and black background" + }, + "2": { + "ident":"v", + "title": "3D Canvas", + "colors": [ + [ 202, 227, 255 ], + [ 255, 255, 255 ], + [ 255, 255, 255 ], + [ 228, 228, 228 ], + [ 196, 196, 196 ], + [ 136, 136, 136 ], + [ 78, 78, 78 ], + [ 0, 0, 0 ], + [ 244, 179, 174 ], + [ 255, 167, 209 ], + [ 255, 84, 178 ], + [ 255, 101, 101 ], + [ 229, 0, 0 ], + [ 154, 0, 0 ], + [ 254, 164, 96 ], + [ 229, 149, 0 ], + [ 160, 106, 66 ], + [ 96, 64, 40 ], + [ 245, 223, 176 ], + [ 255, 248, 137 ], + [ 229, 217, 0 ], + [ 148, 224, 68 ], + [ 2, 190, 1 ], + [ 104, 131, 56 ], + [ 0, 101, 19 ], + [ 202, 227, 255 ], + [ 0, 211, 221 ], + [ 0, 131, 199 ], + [ 0, 0, 234 ], + [ 25, 25, 115 ], + [ 207, 110, 228 ], + [ 130, 0, 128 ] + ], + "alpha": 0, + "size": 1024, + "v": true, + "bcd": 2000, + "pcd" : 2000, + "cds": 60000, + "req": 0, + "sd": "2020-01-08", + "desc": "Test 3D canvas. Changes are not saved." } } diff --git a/src/client.js b/src/client.js index 4fd8bb0..483497b 100644 --- a/src/client.js +++ b/src/client.js @@ -4,208 +4,39 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import fetch from 'isomorphic-fetch'; // TODO put in the beggining with webpack! -import Hammer from 'hammerjs'; import './components/font.css'; +// import initAds, { requestAds } from './ui/ads'; +import onKeyPress from './controls/keypress'; import { - screenToWorld, - getColorIndexOfPixel, -} from './core/utils'; - -import type { State } from './reducers'; -import initAds, { requestAds } from './ui/ads'; -import { - tryPlacePixel, - setHover, - unsetHover, - setViewCoordinates, - setScale, - zoomIn, - zoomOut, receivePixelUpdate, receiveCoolDown, fetchMe, fetchStats, initTimer, urlChange, - onViewFinishChange, receiveOnline, receiveChatMessage, receiveChatHistory, - selectColor, } from './actions'; import store from './ui/store'; -import onKeyPress from './ui/keypress'; import App from './components/App'; -import renderer from './ui/Renderer'; +import { initRenderer, getRenderer } from './ui/renderer'; import ProtocolClient from './socket/ProtocolClient'; -window.addEventListener('keydown', onKeyPress, false); - - -function initViewport() { - const canvas = document.getElementById('gameWindow'); - - const viewport = canvas; - viewport.width = window.innerWidth; - viewport.height = window.innerHeight; - - // track hover - viewport.onmousemove = ({ clientX, clientY }: MouseEvent) => { - store.dispatch(setHover([clientX, clientY])); - }; - viewport.onmouseout = () => { - store.dispatch(unsetHover()); - }; - viewport.onwheel = ({ deltaY }: WheelEvent) => { - const state = store.getState(); - const { hover } = state.gui; - let zoompoint = null; - if (hover) { - zoompoint = screenToWorld(state, viewport, hover); - } - if (deltaY < 0) { - store.dispatch(zoomIn(zoompoint)); - } - if (deltaY > 0) { - store.dispatch(zoomOut(zoompoint)); - } - store.dispatch(onViewFinishChange()); - }; - viewport.onauxclick = ({ which, clientX, clientY }: MouseEvent) => { - // middle mouse button - if (which !== 2) { - return; - } - const state = store.getState(); - if (state.canvas.scale < 3) { - return; - } - const coords = screenToWorld(state, viewport, [clientX, clientY]); - const clrIndex = getColorIndexOfPixel(state, coords); - if (clrIndex === null) { - return; - } - store.dispatch(selectColor(clrIndex)); - }; - - // fingers controls on touch - const hammertime = new Hammer(viewport); - hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL }); - hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL }); - // Zoom-in Zoom-out in touch devices - hammertime.get('pinch').set({ enable: true }); - - hammertime.on('tap', ({ center }) => { - const state = store.getState(); - const { autoZoomIn } = state.gui; - const { placeAllowed } = state.user; - - const { - scale, - isHistoricalView, - } = state.canvas; - if (isHistoricalView) return; - - const { x, y } = center; - const cell = screenToWorld(state, viewport, [x, y]); - - if (autoZoomIn && scale < 8) { - store.dispatch(setViewCoordinates(cell)); - store.dispatch(setScale(12)); - return; - } - - // don't allow placing of pixel just on low zoomlevels - if (scale < 3) return; - - if (!placeAllowed) return; - - // dirty trick: to fetch only before multiple 3 AND on user action - // if (pixelsPlaced % 3 === 0) requestAds(); - - // TODO assert only one finger - store.dispatch(tryPlacePixel(cell)); - }); - - const initialState: State = store.getState(); - [window.lastPosX, window.lastPosY] = initialState.canvas.view; - let lastScale = initialState.canvas.scale; - hammertime.on( - 'panstart pinchstart pan pinch panend pinchend', - ({ - type, deltaX, deltaY, scale, - }) => { - viewport.style.cursor = 'move'; // like google maps - const { scale: viewportScale } = store.getState().canvas; - - // pinch start - if (type === 'pinchstart') { - store.dispatch(unsetHover()); - lastScale = viewportScale; - } - - // panstart - if (type === 'panstart') { - store.dispatch(unsetHover()); - const { view: initView } = store.getState().canvas; - [window.lastPosX, window.lastPosY] = initView; - } - - // pinch - if (type === 'pinch') { - store.dispatch(setScale(lastScale * scale)); - } - - // pan - store.dispatch(setViewCoordinates([ - window.lastPosX - (deltaX / viewportScale), - window.lastPosY - (deltaY / viewportScale), - ])); - - // pinch end - if (type === 'pinchend') { - lastScale = viewportScale; - } - - // panend - if (type === 'panend') { - store.dispatch(onViewFinishChange()); - const { view } = store.getState().canvas; - [window.lastPosX, window.lastPosY] = view; - viewport.style.cursor = 'auto'; - } - }, - ); - - return viewport; -} - - -document.addEventListener('DOMContentLoaded', () => { - ReactDOM.render( - - - , - document.getElementById('app'), - ); - - const viewport = initViewport(); - renderer.setViewport(viewport, store); +function init() { + initRenderer(store, false); ProtocolClient.on('pixelUpdate', ({ i, j, offset, color, }) => { store.dispatch(receivePixelUpdate(i, j, offset, color)); - // render updated pixel - renderer.renderPixel(i, j, offset, color); }); ProtocolClient.on('cooldownPacket', (waitSeconds) => { - console.log(`Received CoolDown ${waitSeconds}`); store.dispatch(receiveCoolDown(waitSeconds)); }); ProtocolClient.on('onlineCounter', ({ online }) => { @@ -221,55 +52,53 @@ document.addEventListener('DOMContentLoaded', () => { store.dispatch(fetchMe()); }); - window.addEventListener('resize', () => { - viewport.width = window.innerWidth; - viewport.height = window.innerHeight; - renderer.forceNextRender = true; - }); window.addEventListener('hashchange', () => { store.dispatch(urlChange()); }); - store.subscribe(() => { - // const state: State = store.getState(); - // this gets executed when store changes - }); - store.dispatch(initTimer()); - window.animationLoop = function animationLoop() { - renderer.render(viewport); - window.requestAnimationFrame(window.animationLoop); - } - window.animationLoop(); - window.store = store; - store.dispatch(fetchMe()); ProtocolClient.connect(); store.dispatch(fetchStats()); setInterval(() => { store.dispatch(fetchStats()); }, 300000); +} +init(); + +document.addEventListener('DOMContentLoaded', () => { + ReactDOM.render( + + + , + document.getElementById('app'), + ); + + document.addEventListener('keydown', onKeyPress, false); // garbage collection function runGC() { - const state: State = store.getState(); - const { chunks } = state.canvas; + const renderer = getRenderer(); - const curTime = Date.now(); - let cnt = 0; - chunks.forEach((value, key) => { - if (curTime > value.timestamp + 300000) { - cnt++; - const [z, i, j] = value.cell; - if (!renderer.isChunkInView(z, i, j)) { - if (value.isBasechunk) { - ProtocolClient.deRegisterChunk([i, j]); + const chunks = renderer.getAllChunks(); + if (chunks) { + const curTime = Date.now(); + let cnt = 0; + chunks.forEach((value, key) => { + if (curTime > value.timestamp + 300000) { + cnt++; + const [z, i, j] = value.cell; + if (!renderer.isChunkInView(z, i, j)) { + if (value.isBasechunk) { + ProtocolClient.deRegisterChunk([i, j]); + } + chunks.delete(key); } - chunks.delete(key); } - } - }); - console.log('Garbage collection cleaned', cnt, 'chunks'); + }); + // eslint-disable-next-line no-console + console.log('Garbage collection cleaned', cnt, 'chunks'); + } } setInterval(runGC, 300000); }); diff --git a/src/components/App.jsx b/src/components/App.jsx index c069e76..c391f15 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -4,32 +4,25 @@ */ import React from 'react'; -import { connect } from 'react-redux'; import { IconContext } from 'react-icons'; -import type { State } from '../reducers'; -import CoolDownBox from './CoolDownBox'; -import NotifyBox from './NotifyBox'; import CoordinatesBox from './CoordinatesBox'; -import GlobeButton from './GlobeButton'; import CanvasSwitchButton from './CanvasSwitchButton'; import OnlineBox from './OnlineBox'; -import PalselButton from './PalselButton'; import ChatButton from './ChatButton'; -import Palette from './Palette'; import ChatBox from './ChatBox'; import Menu from './Menu'; +import UI from './UI'; import ReCaptcha from './ReCaptcha'; import ExpandMenuButton from './ExpandMenuButton'; import ModalRoot from './ModalRoot'; -import HistorySelect from './HistorySelect'; import baseCss from './base.tcss'; -const App = ({ isHistoricalView }) => ( +const App = () => (
+ {/* eslint-disable-next-line react/no-danger */}