Merge branch 'master' into production
This commit is contained in:
commit
ebb5dcc3a4
|
@ -33,7 +33,7 @@ do
|
||||||
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
|
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
|
||||||
echo "---BUILDING pixelplanet---"
|
echo "---BUILDING pixelplanet---"
|
||||||
cd "$BUILDDIR"
|
cd "$BUILDDIR"
|
||||||
cp "$PROXYFILE" ./
|
cp "$PROXYFILE" ./src/
|
||||||
yarn run build --release
|
yarn run build --release
|
||||||
echo "---RESTARTING CANVAS---"
|
echo "---RESTARTING CANVAS---"
|
||||||
cp -r build/* "${PFOLDER}/"
|
cp -r build/* "${PFOLDER}/"
|
||||||
|
@ -55,7 +55,7 @@ do
|
||||||
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
|
COMMITS=`echo "$COMMITS" | sed ':a;N;$!ba;s/\n/\\\n/g'`
|
||||||
echo "---BUILDING pixelplanet---"
|
echo "---BUILDING pixelplanet---"
|
||||||
cd "$BUILDDIR"
|
cd "$BUILDDIR"
|
||||||
cp "$PROXYFILE" ./
|
cp "$PROXYFILE" ./src/
|
||||||
nice -n 19 yarn run build --release
|
nice -n 19 yarn run build --release
|
||||||
echo "---RESTARTING CANVAS---"
|
echo "---RESTARTING CANVAS---"
|
||||||
cp -r build/* "${DEVFOLDER}/"
|
cp -r build/* "${DEVFOLDER}/"
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* react html for adminpage
|
* Html for adminpage
|
||||||
*
|
*
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/server';
|
||||||
|
|
||||||
|
import Html from './Html';
|
||||||
|
|
||||||
const Admin = () => (
|
const Admin = () => (
|
||||||
<form method="post" action="admintools" encType="multipart/form-data">
|
<form method="post" action="admintools" encType="multipart/form-data">
|
||||||
|
@ -36,4 +39,12 @@ const Admin = () => (
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Admin;
|
const data = {
|
||||||
|
title: 'PixelPlanet.fun AdminTools',
|
||||||
|
description: 'admin access on pixelplanet',
|
||||||
|
body: <Admin />,
|
||||||
|
};
|
||||||
|
const adminHtml =
|
||||||
|
`<!doctype html>${ReactDOM.renderToStaticMarkup(<Html {...data} />)}`;
|
||||||
|
|
||||||
|
export default adminHtml;
|
||||||
|
|
|
@ -7,12 +7,12 @@ import React from 'react';
|
||||||
import { IconContext } from 'react-icons';
|
import { IconContext } from 'react-icons';
|
||||||
|
|
||||||
import CoolDownBox from './CoolDownBox';
|
import CoolDownBox from './CoolDownBox';
|
||||||
import NotifyBox from './NotifyBox.js';
|
import NotifyBox from './NotifyBox';
|
||||||
import CoordinatesBox from './CoordinatesBox';
|
import CoordinatesBox from './CoordinatesBox';
|
||||||
import GlobeButton from './GlobeButton';
|
import GlobeButton from './GlobeButton';
|
||||||
import CanvasSwitchButton from './CanvasSwitchButton';
|
import CanvasSwitchButton from './CanvasSwitchButton';
|
||||||
import OnlineBox from './OnlineBox';
|
import OnlineBox from './OnlineBox';
|
||||||
import PalselButton from './PalselButton.js';
|
import PalselButton from './PalselButton';
|
||||||
import ChatButton from './ChatButton';
|
import ChatButton from './ChatButton';
|
||||||
import Palette from './Palette';
|
import Palette from './Palette';
|
||||||
import ChatBox from './ChatBox';
|
import ChatBox from './ChatBox';
|
||||||
|
@ -23,10 +23,6 @@ import ModalRoot from './ModalRoot';
|
||||||
|
|
||||||
import baseCss from './base.tcss';
|
import baseCss from './base.tcss';
|
||||||
|
|
||||||
const position = 'absolute';
|
|
||||||
const left = '1em';
|
|
||||||
const right = left;
|
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<div>
|
<div>
|
||||||
<style dangerouslySetInnerHTML={{ __html: baseCss }} />
|
<style dangerouslySetInnerHTML={{ __html: baseCss }} />
|
||||||
|
|
|
@ -12,14 +12,8 @@ import { switchCanvas } from '../actions';
|
||||||
import type { State } from '../reducers';
|
import type { State } from '../reducers';
|
||||||
|
|
||||||
|
|
||||||
function globe(canvasId, canvasIdent, canvasSize, view) {
|
const CanvasSwitchButton = ({ canvasId, changeCanvas }) => (
|
||||||
const [x, y] = view.map(Math.round);
|
<div id="canvasbutton" className="actionbuttons" onClick={() => changeCanvas(canvasId)}>
|
||||||
window.location.href = `globe/#${canvasIdent},${canvasId},${canvasSize},${x},${y}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const CanvasSwitchButton = ({ canvasId, switchCanvas }) => (
|
|
||||||
<div id="canvasbutton" className="actionbuttons" onClick={() => switchCanvas(canvasId)}>
|
|
||||||
{(canvasId == 0) ? <FaGlobe /> : <FaGlobeAfrica />}
|
{(canvasId == 0) ? <FaGlobe /> : <FaGlobeAfrica />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -31,7 +25,7 @@ function mapStateToProps(state: State) {
|
||||||
|
|
||||||
function mapDispatchToProps(dispatch) {
|
function mapDispatchToProps(dispatch) {
|
||||||
return {
|
return {
|
||||||
switchCanvas(canvasId) {
|
changeCanvas(canvasId) {
|
||||||
const newCanvasId = (canvasId == 0) ? 1 : 0;
|
const newCanvasId = (canvasId == 0) ? 1 : 0;
|
||||||
dispatch(switchCanvas(newCanvasId));
|
dispatch(switchCanvas(newCanvasId));
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { MdForum } from 'react-icons/md';
|
||||||
import { showChatModal } from '../actions';
|
import { showChatModal } from '../actions';
|
||||||
|
|
||||||
|
|
||||||
const ChatButton = ({ name, open }) => (
|
const ChatButton = ({ open }) => (
|
||||||
<div id="chatbutton" className="actionbuttons" onClick={open}>
|
<div id="chatbutton" className="actionbuttons" onClick={open}>
|
||||||
<MdForum />
|
<MdForum />
|
||||||
</div>: null
|
</div>: null
|
||||||
|
|
|
@ -25,7 +25,7 @@ const textStyle = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const ChatModal = ({ center }) => (
|
const ChatModal = () => (
|
||||||
<Modal title="Chat">
|
<Modal title="Chat">
|
||||||
<p style={{ textAlign: 'center' }}>
|
<p style={{ textAlign: 'center' }}>
|
||||||
<p style={textStyle}>Chat with other people here</p>
|
<p style={textStyle}>Chat with other people here</p>
|
||||||
|
@ -36,9 +36,3 @@ const ChatModal = ({ center }) => (
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
||||||
function mapStateToProps(state: State) {
|
|
||||||
const { center } = state.user;
|
|
||||||
return { center };
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(ChatModal);
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import globeCss from './globe.tcss';
|
||||||
const Globe = () => (
|
const Globe = () => (
|
||||||
<div>
|
<div>
|
||||||
<style dangerouslySetInnerHTML={{ __html: globeCss }} />
|
<style dangerouslySetInnerHTML={{ __html: globeCss }} />
|
||||||
<div id="webgl"></div>
|
<div id="webgl" />
|
||||||
<div id="coorbox">(0, 0)</div>
|
<div id="coorbox">(0, 0)</div>
|
||||||
<div id="info">Double click on globe to go back.</div>
|
<div id="info">Double click on globe to go back.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
46
src/components/Main.js
Normal file
46
src/components/Main.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Html for mainpage
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/server';
|
||||||
|
|
||||||
|
import Html from './Html';
|
||||||
|
import assets from './assets.json';
|
||||||
|
import { ASSET_SERVER } from '../core/config';
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
title: 'PixelPlanet.fun',
|
||||||
|
description: 'Place color pixels on an map styled canvas ' +
|
||||||
|
'with other players online',
|
||||||
|
// styles: [
|
||||||
|
// { id: 'css', cssText: baseCss },
|
||||||
|
// ],
|
||||||
|
scripts: [
|
||||||
|
ASSET_SERVER + assets.vendor.js,
|
||||||
|
ASSET_SERVER + assets.client.js,
|
||||||
|
],
|
||||||
|
useRecaptcha: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generates string with html of main page
|
||||||
|
* including setting global variables for countryCoords
|
||||||
|
* and assetserver
|
||||||
|
* @param countryCoords Cell with coordinates of client country
|
||||||
|
* @return html of mainpage
|
||||||
|
*/
|
||||||
|
function generateMainPage(countryCoords: Cell): string {
|
||||||
|
const [x, y] = countryCoords;
|
||||||
|
const code =
|
||||||
|
`window.coordx=${x};window.coordy=${y};window.assetserver="${ASSET_SERVER}";`;
|
||||||
|
const htmldata = { ...data, code };
|
||||||
|
const html = ReactDOM.renderToStaticMarkup(<Html {...htmldata} />);
|
||||||
|
|
||||||
|
return `<!doctype html>${html}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default generateMainPage;
|
|
@ -32,8 +32,8 @@ export const DISCORD_INVITE = process.env.DISCORD_INVITE || 'https://discordapp.
|
||||||
// Accounts
|
// Accounts
|
||||||
export const APISOCKET_KEY = process.env.APISOCKET_KEY || 'changethis';
|
export const APISOCKET_KEY = process.env.APISOCKET_KEY || 'changethis';
|
||||||
// Comma seperated list of user ids of Admins
|
// Comma seperated list of user ids of Admins
|
||||||
export const ADMIN_IDS = (process.env.ADMIN_IDS) ?
|
export const ADMIN_IDS = (process.env.ADMIN_IDS) ?
|
||||||
process.env.ADMIN_IDS.split(',').map((z) => parseInt(z, 10)) : [];
|
process.env.ADMIN_IDS.split(',').map(z => parseInt(z, 10)) : [];
|
||||||
|
|
||||||
export const analytics = {
|
export const analytics = {
|
||||||
// https://analytics.google.com/
|
// https://analytics.google.com/
|
||||||
|
|
|
@ -140,7 +140,7 @@ class CanvasUpdater {
|
||||||
setInterval(this.updateZoomlevelTiles, timeout, c);
|
setInterval(this.updateZoomlevelTiles, timeout, c);
|
||||||
}
|
}
|
||||||
if (this.maxTiledZoom === 0) {
|
if (this.maxTiledZoom === 0) {
|
||||||
//in the case of canvasSize == 256
|
// in the case of canvasSize == 256
|
||||||
this.TileLoadingQueues.push([]);
|
this.TileLoadingQueues.push([]);
|
||||||
setInterval(this.updateZoomlevelTiles, 5 * 60 * 1000, 0);
|
setInterval(this.updateZoomlevelTiles, 5 * 60 * 1000, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ export function getIdFromObject(obj: Object, ident: string): number {
|
||||||
for (let i = 0; i < ids.length; i += 1) {
|
for (let i = 0; i < ids.length; i += 1) {
|
||||||
const key = ids[i];
|
const key = ids[i];
|
||||||
if (obj[key].ident === ident) {
|
if (obj[key].ident === ident) {
|
||||||
return key;
|
return parseInt(key, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
import redis from '../redis';
|
import redis from '../redis';
|
||||||
import { randomDice } from '../../utils/random';
|
import { randomDice } from '../../utils/random';
|
||||||
import { verifyCaptcha } from '../../utils/recaptcha';
|
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
import Sequelize from 'sequelize';
|
import Sequelize from 'sequelize';
|
||||||
|
|
||||||
|
|
168
src/globe.js
168
src/globe.js
|
@ -7,8 +7,8 @@ import { TrackballControls } from 'three-trackballcontrols-ts';
|
||||||
function checkMaterial(object) {
|
function checkMaterial(object) {
|
||||||
if (object.material) {
|
if (object.material) {
|
||||||
const materialName = object.material.name;
|
const materialName = object.material.name;
|
||||||
if (materialName == "canvas") {
|
if (materialName === 'canvas') {
|
||||||
console.log("Found material for canvas texture");
|
console.log('Found material for canvas texture');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,19 @@ function parseHashCoords() {
|
||||||
try {
|
try {
|
||||||
const hash = window.location.hash;
|
const hash = window.location.hash;
|
||||||
const array = hash.substring(1).split(',');
|
const array = hash.substring(1).split(',');
|
||||||
const ident = array.shift();
|
const ident = array.shift();
|
||||||
const [id, size, x, y] = array.map((z) => parseInt(z));
|
const [id, size, x, y] = array.map(z => parseInt(z, 10));
|
||||||
if (!ident || isNaN(x) || isNaN(y) || isNaN(id) || isNaN(size)) {
|
if (!ident || isNaN(x) || isNaN(y) || isNaN(id) || isNaN(size)) {
|
||||||
throw "NaN";
|
throw 'NaN';
|
||||||
}
|
}
|
||||||
return [ident, id, size, x, y];
|
return [ident, id, size, x, y];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ['d', 0, 65536, 0, 0];
|
return ['d', 0, 65536, 0, 0];
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotateToCoords(canvasSize, object, coords) {
|
function rotateToCoords(canvasSize, object, coords) {
|
||||||
console.log("Rotate to", coords);
|
console.log('Rotate to', coords);
|
||||||
const [x, y] = coords;
|
const [x, y] = coords;
|
||||||
const rotOffsetX = 0;
|
const rotOffsetX = 0;
|
||||||
const rotOffsetY = 3 * Math.PI / 2;
|
const rotOffsetY = 3 * Math.PI / 2;
|
||||||
|
@ -43,40 +43,60 @@ function rotateToCoords(canvasSize, object, coords) {
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
var webglEl = document.getElementById('webgl');
|
const webglEl = document.getElementById('webgl');
|
||||||
|
|
||||||
const [canvasIdent, canvasId, canvasSize, x, y] = parseHashCoords();
|
const [canvasIdent, canvasId, canvasSize, x, y] = parseHashCoords();
|
||||||
|
|
||||||
var canvasTexture = new THREE.MeshPhongMaterial({
|
const canvasTexture = new THREE.MeshPhongMaterial({
|
||||||
map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.png`),
|
map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.png`),
|
||||||
bumpMap: new THREE.TextureLoader().load(`./assets3d/normal${canvasId}.jpg`),
|
bumpMap: new THREE.TextureLoader().load(`./assets3d/normal${canvasId}.jpg`),
|
||||||
bumpScale: 0.02,
|
bumpScale: 0.02,
|
||||||
specularMap: new THREE.TextureLoader().load(`./assets3d/specular${canvasId}.jpg`),
|
specularMap: new THREE.TextureLoader()
|
||||||
specular: new THREE.Color('grey')
|
.load(`./assets3d/specular${canvasId}.jpg`),
|
||||||
|
specular: new THREE.Color('grey'),
|
||||||
});
|
});
|
||||||
|
|
||||||
var width = window.innerWidth,
|
let width = window.innerWidth;
|
||||||
height = window.innerHeight;
|
let height = window.innerHeight;
|
||||||
|
|
||||||
var scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
|
|
||||||
var camera = new THREE.PerspectiveCamera(45, width / height, 0.01, 1000);
|
const camera = new THREE.PerspectiveCamera(45, width / height, 0.01, 1000);
|
||||||
camera.position.z = 4.0;
|
camera.position.z = 4.0;
|
||||||
|
|
||||||
var renderer = new THREE.WebGLRenderer();
|
const renderer = new THREE.WebGLRenderer();
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
|
|
||||||
scene.add(new THREE.AmbientLight(0x333333));
|
scene.add(new THREE.AmbientLight(0x333333));
|
||||||
|
|
||||||
var light = new THREE.DirectionalLight(0xffffff, 0.7);
|
let controls = null;
|
||||||
light.position.set(10,6,10);
|
let object = null;
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
controls.update();
|
||||||
|
if (object) object.rotation.y += 0.0005;
|
||||||
|
requestAnimationFrame(render);
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
const light = new THREE.DirectionalLight(0xffffff, 0.7);
|
||||||
|
light.position.set(10, 6, 10);
|
||||||
scene.add(light);
|
scene.add(light);
|
||||||
|
|
||||||
var controls = null;
|
function createControls() {
|
||||||
|
const contr = new TrackballControls(camera, renderer.domElement);
|
||||||
|
contr.rotateSpeed = 1.8;
|
||||||
|
contr.zoomSpeed = 1.0;
|
||||||
|
contr.panSpeed = 0.3;
|
||||||
|
contr.minDistance = 1.5;
|
||||||
|
contr.maxDistance = 70.00;
|
||||||
|
contr.keys = [65, 83, 68]; // ASD
|
||||||
|
contr.dynamicDampingFactor = 0.2;
|
||||||
|
return contr;
|
||||||
|
}
|
||||||
|
|
||||||
var object = null;
|
const loader = new GLTFLoader();
|
||||||
var loader = new GLTFLoader();
|
loader.load('./assets3d/globe.glb', (glb) => {
|
||||||
loader.load('./assets3d/globe.glb', function (glb) {
|
|
||||||
scene.add(glb.scene);
|
scene.add(glb.scene);
|
||||||
const children = glb.scene.children;
|
const children = glb.scene.children;
|
||||||
for (let cnt = 0; cnt < children.length; cnt++) {
|
for (let cnt = 0; cnt < children.length; cnt++) {
|
||||||
|
@ -89,96 +109,68 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
object = children[cnt];
|
object = children[cnt];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (object) {
|
if (object) {
|
||||||
object.material = canvasTexture;
|
object.material = canvasTexture;
|
||||||
}
|
}
|
||||||
rotateToCoords(canvasSize, object, [x, y]);
|
rotateToCoords(canvasSize, object, [x, y]);
|
||||||
controls = createControls(camera);
|
controls = createControls();
|
||||||
render();
|
render();
|
||||||
}, function (xhr) {
|
}, (xhr) => {
|
||||||
console.log(`${xhr.loaded} loaded`);
|
console.log(`${xhr.loaded} loaded`);
|
||||||
}, function (error) {
|
}, (error) => {
|
||||||
console.log('An error happened', error);
|
console.log('An error happened', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Earth params
|
|
||||||
var radius = 0.5,
|
|
||||||
segments = 32,
|
|
||||||
rotation = 6;
|
|
||||||
|
|
||||||
|
|
||||||
function createControls(camera) {
|
|
||||||
const controls = new TrackballControls(camera, renderer.domElement);
|
|
||||||
controls.rotateSpeed = 1.8;
|
|
||||||
controls.zoomSpeed = 1.0;
|
|
||||||
controls.panSpeed = 0.3;
|
|
||||||
controls.minDistance = 1.5;
|
|
||||||
controls.maxDistance = 70.00;
|
|
||||||
controls.keys = [65, 83, 68]; // ASD
|
|
||||||
controls.dynamicDampingFactor = 0.2;
|
|
||||||
return controls;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
webglEl.appendChild(renderer.domElement);
|
webglEl.appendChild(renderer.domElement);
|
||||||
|
|
||||||
|
const stars = new THREE.Mesh(
|
||||||
function render() {
|
new THREE.SphereGeometry(90, 64, 64),
|
||||||
controls.update();
|
|
||||||
if (object) object.rotation.y += 0.0005;
|
|
||||||
requestAnimationFrame(render);
|
|
||||||
renderer.render(scene, camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
var stars = new THREE.Mesh(
|
|
||||||
new THREE.SphereGeometry(90, 64, 64),
|
|
||||||
new THREE.MeshBasicMaterial({
|
new THREE.MeshBasicMaterial({
|
||||||
map: new THREE.TextureLoader().load('./assets3d/starfield.png'),
|
map: new THREE.TextureLoader().load('./assets3d/starfield.png'),
|
||||||
side: THREE.BackSide
|
side: THREE.BackSide,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
scene.add(stars);
|
scene.add(stars);
|
||||||
|
|
||||||
setInterval(onDocumentMouseMove, 1000);
|
|
||||||
|
|
||||||
|
|
||||||
function onWindowResize() {
|
function onWindowResize() {
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
width = window.innerWidth;
|
||||||
const aspect = window.innerWidth / window.innerHeight;
|
height = window.innerHeight;
|
||||||
|
renderer.setSize(width, height);
|
||||||
|
const aspect = width / height;
|
||||||
camera.aspect = aspect;
|
camera.aspect = aspect;
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
if (controls) controls.handleResize();
|
if (controls) controls.handleResize();
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', onWindowResize,false);
|
window.addEventListener('resize', onWindowResize, false);
|
||||||
|
|
||||||
|
|
||||||
var raycaster = new THREE.Raycaster();
|
const raycaster = new THREE.Raycaster();
|
||||||
var mouse = new THREE.Vector2();
|
const mouse = new THREE.Vector2();
|
||||||
var lastView = [0, 0];
|
const coorbox = document.getElementById('coorbox');
|
||||||
const coorbox = document.getElementById("coorbox");
|
|
||||||
function onDocumentMouseMove(event) {
|
function onDocumentMouseMove(event) {
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event) {
|
if (event) {
|
||||||
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
|
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||||
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
|
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||||
} else {
|
} else {
|
||||||
mouse.x = 0.0;
|
mouse.x = 0.0;
|
||||||
mouse.y = 0.0;
|
mouse.y = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
raycaster.setFromCamera( mouse, camera );
|
raycaster.setFromCamera(mouse, camera);
|
||||||
var intersects = raycaster.intersectObject( object );
|
const intersects = raycaster.intersectObject(object);
|
||||||
|
|
||||||
const elem = document.getElementsByTagName("BODY")[0];
|
const elem = document.getElementsByTagName('BODY')[0];
|
||||||
if(intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
const { x, y } = intersects[0].uv;
|
const { xi, yi } = intersects[0].uv;
|
||||||
const xabs = Math.floor((x - 0.5) * canvasSize);
|
const xabs = Math.floor((xi - 0.5) * canvasSize);
|
||||||
const yabs = Math.floor((0.5 - y) * canvasSize);
|
const yabs = Math.floor((0.5 - yi) * canvasSize);
|
||||||
//console.log(`On ${xabs} / ${yabs} cam: ${camera.position.z}`);
|
// console.log(`On ${xabs} / ${yabs} cam: ${camera.position.z}`);
|
||||||
coorbox.innerHTML = `(${xabs}, ${yabs})`;
|
coorbox.innerHTML = `(${xabs}, ${yabs})`;
|
||||||
elem.style.cursor = 'move';
|
elem.style.cursor = 'move';
|
||||||
} else {
|
} else {
|
||||||
|
@ -186,26 +178,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setInterval(onDocumentMouseMove, 1000);
|
||||||
|
|
||||||
function onDocumentDblClick(event) {
|
function onDocumentDblClick(event) {
|
||||||
if (!object) {
|
if (!object) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
|
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||||
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
|
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||||
|
|
||||||
raycaster.setFromCamera( mouse, camera );
|
raycaster.setFromCamera(mouse, camera);
|
||||||
var intersects = raycaster.intersectObject( object );
|
const intersects = raycaster.intersectObject(object);
|
||||||
|
|
||||||
if(intersects.length > 0) {
|
if (intersects.length > 0) {
|
||||||
const { x, y } = intersects[0].uv;
|
const { xi, yi } = intersects[0].uv;
|
||||||
const xabs = Math.round((x - 0.5) * canvasSize);
|
const xabs = Math.round((xi - 0.5) * canvasSize);
|
||||||
const yabs = Math.round((0.5 - y) * canvasSize);
|
const yabs = Math.round((0.5 - yi) * canvasSize);
|
||||||
window.location.href = `./#${canvasIdent},${xabs},${yabs},0`;
|
window.location.href = `./#${canvasIdent},${xabs},${yabs},0`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('mousemove', onDocumentMouseMove, false);
|
document.addEventListener('mousemove', onDocumentMouseMove, false);
|
||||||
document.addEventListener('dblclick', onDocumentDblClick, false);
|
document.addEventListener('dblclick', onDocumentDblClick, false);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,8 +11,6 @@ import type { Request, Response } from 'express';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom/server';
|
|
||||||
|
|
||||||
import { getIPFromRequest } from '../utils/ip';
|
import { getIPFromRequest } from '../utils/ip';
|
||||||
import { getIdFromObject } from '../core/utils';
|
import { getIdFromObject } from '../core/utils';
|
||||||
|
@ -26,23 +24,12 @@ import { MINUTE } from '../core/constants';
|
||||||
import canvases from '../canvases.json';
|
import canvases from '../canvases.json';
|
||||||
import { imageABGR2Canvas } from '../core/Image';
|
import { imageABGR2Canvas } from '../core/Image';
|
||||||
|
|
||||||
import Html from '../components/Html';
|
import adminHtml from '../components/Admin';
|
||||||
import Admin from '../components/Admin';
|
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const limiter = expressLimiter(router, redis);
|
const limiter = expressLimiter(router, redis);
|
||||||
|
|
||||||
/*
|
|
||||||
* build html of admin page with react
|
|
||||||
*/
|
|
||||||
const data = {
|
|
||||||
title: 'PixelPlanet.fun AdminTools',
|
|
||||||
description: 'admin access on pixelplanet',
|
|
||||||
body: <Admin />,
|
|
||||||
};
|
|
||||||
const index = `<!doctype html>${ReactDOM.renderToStaticMarkup(<Html {...data} />)}`;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* multer middleware for getting POST parameters
|
* multer middleware for getting POST parameters
|
||||||
|
@ -246,7 +233,7 @@ router.use(async (req: Request, res: Response) => {
|
||||||
res.set({
|
res.set({
|
||||||
'Content-Type': 'text/html',
|
'Content-Type': 'text/html',
|
||||||
});
|
});
|
||||||
res.status(200).send(index);
|
res.status(200).send(adminHtml);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import nodeIp from 'ip';
|
||||||
|
|
||||||
import draw from '../../core/draw';
|
import draw from '../../core/draw';
|
||||||
import { blacklistDetector, cheapDetector, strongDetector } from '../../core/isProxy';
|
import { blacklistDetector, cheapDetector, strongDetector } from '../../core/isProxy';
|
||||||
import { verifyCaptcha } from '../../utils/recaptcha';
|
import verifyCaptcha from '../../utils/recaptcha';
|
||||||
import logger from '../../core/logger';
|
import logger from '../../core/logger';
|
||||||
import { clamp } from '../../core/utils';
|
import { clamp } from '../../core/utils';
|
||||||
import redis from '../../data/redis';
|
import redis from '../../data/redis';
|
||||||
|
|
|
@ -248,7 +248,7 @@ class APISocketServer extends EventEmitter {
|
||||||
ret,
|
ret,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch (err) {
|
||||||
logger.error(`Got undecipherable api-ws message ${message}`);
|
logger.error(`Got undecipherable api-ws message ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* save the chat history
|
* save the chat history
|
||||||
* TODO:
|
* TODO:
|
||||||
* This should really be saved in redis
|
* This should really be saved in redis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
function loadImage(url) {
|
function loadImage(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.addEventListener('load', e => resolve(img));
|
img.addEventListener('load', () => resolve(img));
|
||||||
img.addEventListener('error', () => {
|
img.addEventListener('error', () => {
|
||||||
reject(new Error(`Failed to load image's URL: ${url}`));
|
reject(new Error(`Failed to load image's URL: ${url}`));
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,19 +19,19 @@ const ENDPOINT = `${BASE_ENDPOINT}?secret=${RECAPTCHA_SECRET}`;
|
||||||
* @param ip
|
* @param ip
|
||||||
* @returns {Promise.<boolean>}
|
* @returns {Promise.<boolean>}
|
||||||
*/
|
*/
|
||||||
export async function verifyCaptcha(
|
async function verifyCaptcha(
|
||||||
token: string,
|
token: string,
|
||||||
ip: string,
|
ip: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
if (!RECAPTCHA_SECRET) {
|
if (!RECAPTCHA_SECRET) {
|
||||||
logger.info(`Got captcha token but reCaptcha isn't configured?!`);
|
logger.info('Got captcha token but reCaptcha isn\'t configured?!');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const url = `${ENDPOINT}&response=${token}&remoteip=${ip}`;
|
const url = `${ENDPOINT}&response=${token}&remoteip=${ip}`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const { success, challenge_ts, hostname } = await response.json();
|
const { success } = await response.json();
|
||||||
if (success) {
|
if (success) {
|
||||||
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
|
logger.info(`CAPTCHA ${ip} successfully solved captcha`);
|
||||||
return true;
|
return true;
|
||||||
|
@ -46,3 +46,5 @@ export async function verifyCaptcha(
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default verifyCaptcha;
|
||||||
|
|
|
@ -3,15 +3,17 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const mail_tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
|
const mailTester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
|
||||||
|
|
||||||
export function validateEMail(email) {
|
export function validateEMail(email) {
|
||||||
if (!email) return "Email can't be empty.";
|
if (!email) return "Email can't be empty.";
|
||||||
if (email.length < 5) return 'Email should be at least 5 characters long.';
|
if (email.length < 5) return 'Email should be at least 5 characters long.';
|
||||||
if (email.length > 40) return "Email can't be longer than 40 characters.";
|
if (email.length > 40) return "Email can't be longer than 40 characters.";
|
||||||
if (email.indexOf('.') === -1) return 'Email should at least contain a dot';
|
if (email.indexOf('.') === -1) return 'Email should at least contain a dot';
|
||||||
if (email.split('').filter(x => x === '@').length !== 1) return 'Email should contain a @';
|
if (email.split('').filter(x => x === '@').length !== 1) {
|
||||||
if (!mail_tester.test(email)) return 'Your Email looks shady';
|
return 'Email should contain a @';
|
||||||
|
}
|
||||||
|
if (!mailTester.test(email)) return 'Your Email looks shady';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +26,9 @@ export function validateName(name) {
|
||||||
name.indexOf('\\') !== -1 ||
|
name.indexOf('\\') !== -1 ||
|
||||||
name.indexOf('>') !== -1 ||
|
name.indexOf('>') !== -1 ||
|
||||||
name.indexOf('<') !== -1 ||
|
name.indexOf('<') !== -1 ||
|
||||||
name.indexOf('#') !== -1) return 'Name contains invalid character like @, /, \\ or #';
|
name.indexOf('#') !== -1) {
|
||||||
|
return 'Name contains invalid character like @, /, \\ or #';
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +41,12 @@ export function sanitizeName(name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validatePassword(password) {
|
export function validatePassword(password) {
|
||||||
if (password.length < 6) return 'Password must be at least 6 characters long.';
|
if (password.length < 6) {
|
||||||
if (password.length > 60) return 'Password must be shorter than 60 characters.';
|
return 'Password must be at least 6 characters long.';
|
||||||
|
}
|
||||||
|
if (password.length > 60) {
|
||||||
|
return 'Password must be shorter than 60 characters.';
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/web.js
36
src/web.js
|
@ -5,14 +5,11 @@ import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import etag from 'etag';
|
import etag from 'etag';
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom/server';
|
|
||||||
import expressValidator from 'express-validator';
|
import expressValidator from 'express-validator';
|
||||||
|
|
||||||
|
|
||||||
// import baseCss from './components/base.tcss';
|
// import baseCss from './components/base.tcss';
|
||||||
import forceGC from './core/forceGC';
|
import forceGC from './core/forceGC';
|
||||||
import Html from './components/Html';
|
|
||||||
import assets from './assets.json'; // eslint-disable-line import/no-unresolved
|
import assets from './assets.json'; // eslint-disable-line import/no-unresolved
|
||||||
import logger from './core/logger';
|
import logger from './core/logger';
|
||||||
import models from './data/models';
|
import models from './data/models';
|
||||||
|
@ -25,9 +22,10 @@ import {
|
||||||
resetPassword,
|
resetPassword,
|
||||||
} from './routes';
|
} from './routes';
|
||||||
import globeHtml from './components/Globe';
|
import globeHtml from './components/Globe';
|
||||||
|
import generateMainPage from './components/Main';
|
||||||
|
|
||||||
import { SECOND, MONTH } from './core/constants';
|
import { SECOND, MONTH } from './core/constants';
|
||||||
import { PORT, ASSET_SERVER, DISCORD_INVITE } from './core/config';
|
import { PORT, DISCORD_INVITE } from './core/config';
|
||||||
|
|
||||||
import { ccToCoords } from './utils/location';
|
import { ccToCoords } from './utils/location';
|
||||||
import { wsupgrade } from './socket/websockets';
|
import { wsupgrade } from './socket/websockets';
|
||||||
|
@ -117,7 +115,7 @@ app.use('/reset_password', resetPassword);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 3D Globe
|
// 3D Globe (react generated)
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
const globeEtag = etag(
|
const globeEtag = etag(
|
||||||
`${assets.globe.js}`,
|
`${assets.globe.js}`,
|
||||||
|
@ -127,10 +125,10 @@ app.get('/globe', async (req, res) => {
|
||||||
res.set({
|
res.set({
|
||||||
'Cache-Control': `private, max-age=${15 * 60}`, // seconds
|
'Cache-Control': `private, max-age=${15 * 60}`, // seconds
|
||||||
'Content-Type': 'text/html; charset=utf-8',
|
'Content-Type': 'text/html; charset=utf-8',
|
||||||
ETag: indexEtag,
|
ETag: globeEtag,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.headers['if-none-match'] === indexEtag) {
|
if (req.headers['if-none-match'] === globeEtag) {
|
||||||
res.status(304).end();
|
res.status(304).end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,21 +138,8 @@ app.get('/globe', async (req, res) => {
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Register server-side rendering middleware
|
// Main Page (react generated)
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
const data = {
|
|
||||||
title: 'PixelPlanet.fun',
|
|
||||||
description: 'Place color pixels on an map styled canvas ' +
|
|
||||||
'with other players online',
|
|
||||||
// styles: [
|
|
||||||
// { id: 'css', cssText: baseCss },
|
|
||||||
// ],
|
|
||||||
scripts: [
|
|
||||||
ASSET_SERVER + assets.vendor.js,
|
|
||||||
ASSET_SERVER + assets.client.js,
|
|
||||||
],
|
|
||||||
useRecaptcha: true,
|
|
||||||
};
|
|
||||||
const indexEtag = etag(
|
const indexEtag = etag(
|
||||||
`${assets.vendor.js},${assets.client.js}`,
|
`${assets.vendor.js},${assets.client.js}`,
|
||||||
{ weak: true },
|
{ weak: true },
|
||||||
|
@ -174,14 +159,9 @@ app.get('/', async (req, res) => {
|
||||||
|
|
||||||
// get start coordinates based on cloudflare header country
|
// get start coordinates based on cloudflare header country
|
||||||
const country = req.headers['cf-ipcountry'];
|
const country = req.headers['cf-ipcountry'];
|
||||||
const [x, y] = (country) ? ccToCoords(country) : [0, 0];
|
const countryCoords = (country) ? ccToCoords(country) : [0, 0];
|
||||||
const code =
|
|
||||||
`window.coordx=${x};window.coordy=${y};window.assetserver="${ASSET_SERVER}";`;
|
|
||||||
const htmldata = { ...data, code };
|
|
||||||
const html = ReactDOM.renderToStaticMarkup(<Html {...htmldata} />);
|
|
||||||
const index = `<!doctype html>${html}`;
|
|
||||||
|
|
||||||
res.status(200).send(index);
|
res.status(200).send(generateMainPage(countryCoords));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user