mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 07:24:52 +09:00
Merge pull request #218 from Hornwitser/remove-remanent-remotemap
Remove remanent remoteMap code
This commit is contained in:
10 files changed
+4
-850
No files matched your search
@@ -47,7 +47,6 @@ saves
|
||||
sharedPlugins/*
|
||||
!sharedPlugins/inventoryImports/
|
||||
!sharedPlugins/UPSdisplay/
|
||||
!sharedPlugins/remoteMap/
|
||||
!sharedPlugins/remoteCommands/
|
||||
!sharedPlugins/researchSync/
|
||||
!sharedPlugins/globalChat/
|
||||
|
||||
@@ -712,83 +712,11 @@ app.get("/api/getFactorioLocale", function(req,res){
|
||||
});
|
||||
});
|
||||
|
||||
/* Websockets for remoteMap */
|
||||
/* Websockets for plugins */
|
||||
var io = require("socket.io")({});
|
||||
const ioMetrics = require("socket.io-prometheus");
|
||||
ioMetrics(io);
|
||||
|
||||
var slaveMappers = {};
|
||||
class slaveMapper {
|
||||
constructor(instanceID, socket) {
|
||||
this.instanceID = instanceID;
|
||||
this.socket = socket;
|
||||
this.lastBeat = Date.now();
|
||||
|
||||
this.socket.on("heartbeat", () => {
|
||||
prometheusWsUsageCounter.labels('heartbeat', this.instanceID).inc();
|
||||
// we aren't ready to die yet apparently
|
||||
this.lastBeat = Date.now();
|
||||
});
|
||||
this.socket.on("sendChunk", function(data){
|
||||
prometheusWsUsageCounter.labels('sendChunk', this.instanceID).inc();
|
||||
mapRequesters[data.requesterID].socket.emit("displayChunk", data);
|
||||
});
|
||||
// slaveMapper sent us an entity update, process
|
||||
this.socket.on("sendEntity", entity => {
|
||||
prometheusWsUsageCounter.labels('sendEntity', this.instanceID).inc();
|
||||
Object.keys(mapRequesters).forEach(requesterName => {
|
||||
let requester = mapRequesters[requesterName];
|
||||
|
||||
if(requester.instanceID == this.instanceID){
|
||||
// this mapRequester is listening to this slaveMapper, so we send it updates
|
||||
requester.socket.emit("displayEntity", entity);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var mapRequesters = {};
|
||||
class mapRequester {
|
||||
constructor(requesterID, socket, instanceID){
|
||||
this.requesterID = requesterID;
|
||||
this.socket = socket;
|
||||
this.instanceID = instanceID;
|
||||
this.lastBeat = Date.now();
|
||||
|
||||
this.socket.on("heartbeat", () => {
|
||||
prometheusWsUsageCounter.labels('heartbeat', "other").inc();
|
||||
// we aren't ready to die yet apparently
|
||||
this.lastBeat = Date.now();
|
||||
});
|
||||
this.socket.on("requestChunk", loc => {
|
||||
prometheusWsUsageCounter.labels('requestChunk', "other").inc();
|
||||
loc.requesterID = this.requesterID;
|
||||
let instanceID = loc.instanceID || this.instanceID;
|
||||
if(slaveMappers[instanceID] && typeof loc.x == "number" && typeof loc.y == "number"){
|
||||
slaveMappers[instanceID].socket.emit("getChunk", loc);
|
||||
}
|
||||
});
|
||||
this.socket.on("requestEntity", req => {
|
||||
prometheusWsUsageCounter.labels('requestEntity', "other").inc();
|
||||
req.requesterID = this.requesterID;
|
||||
let instanceID = req.instanceID || this.instanceID;
|
||||
if(slaveMappers[instanceID] && typeof req.x == "number" && typeof req.y == "number"){
|
||||
slaveMappers[instanceID].socket.emit("getEntity", req);
|
||||
}
|
||||
});
|
||||
this.socket.on("placeEntity", req => {
|
||||
prometheusWsUsageCounter.labels('placeEntity', "other").inc();
|
||||
req.requesterID = this.requesterID;
|
||||
let instanceID = req.instanceID || this.instanceID;
|
||||
if(slaveMappers[instanceID]){
|
||||
slaveMappers[instanceID].socket.emit("placeEntity", req);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Websockets for send and recieve combinators */
|
||||
var wsSlaves = {};
|
||||
@@ -852,8 +780,8 @@ class wsSlave {
|
||||
io.on('connection', function (socket) {
|
||||
// cleanup dead sockets from disconnected people
|
||||
let terminatedConnections = 0;
|
||||
let currentConnections = Object.keys(mapRequesters).length + Object.keys(slaveMappers).length + Object.keys(wsSlaves).length;
|
||||
[mapRequesters, slaveMappers, wsSlaves].forEach(list => {
|
||||
let currentConnections = Object.keys(wsSlaves).length;
|
||||
[wsSlaves].forEach(list => {
|
||||
Object.keys(list).forEach(connectionID => {
|
||||
let connection = list[connectionID];
|
||||
if(connection.lastBeat < (Date.now() - 30000)){
|
||||
@@ -867,21 +795,6 @@ io.on('connection', function (socket) {
|
||||
// tell our friend that we are listening
|
||||
setTimeout(()=>socket.emit('hello', { hello: 'world' }), 5000);
|
||||
|
||||
/* initial processing for remoteMap */
|
||||
socket.on('registerSlaveMapper', function (data) {
|
||||
prometheusWsUsageCounter.labels('registerSlaveMapper', "other").inc();
|
||||
slaveMappers[data.instanceID] = new slaveMapper(data.instanceID, socket);
|
||||
console.log("remoteMap | SOCKET registered map provider for "+data.instanceID);
|
||||
});
|
||||
socket.on('registerMapRequester', function(data){
|
||||
// data {instanceID:""}
|
||||
prometheusWsUsageCounter.labels('registerMapRequester', "other").inc();
|
||||
let requesterID = Math.random().toString();
|
||||
mapRequesters[requesterID] = new mapRequester(requesterID, socket, data.instanceID);
|
||||
socket.emit("mapRequesterReady", true);
|
||||
console.log("remoteMap | SOCKET registered map requester for "+data.instanceID);
|
||||
});
|
||||
|
||||
/* Websockets for send and recieve combinators */
|
||||
socket.on("registerSlave", function(data) {
|
||||
prometheusWsUsageCounter.labels('registerSlave', "other").inc();
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ describe('Master server endpoint testing', function() {
|
||||
parallel("#GET static website data", function() {
|
||||
this.timeout(6000);
|
||||
|
||||
let paths = ["/", "/nodes", "/settings", "/nodeDetails", "/remoteMap"];
|
||||
let paths = ["/", "/nodes", "/settings", "/nodeDetails"];
|
||||
for (let path of paths) {
|
||||
it(`sends some HTML when accessing ${path}`, () => getValidate(path));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,4 @@ module.exports = function(app) {
|
||||
app.get("/nodeDetails", function(req, res) {
|
||||
res.render("nodeDetails");
|
||||
});
|
||||
app.get("/remoteMap", function(req, res) {
|
||||
res.render("remoteMap");
|
||||
});
|
||||
};
|
||||
@@ -131,18 +131,6 @@ function populateSlaveInfo(){
|
||||
HTML += '<li class="breadcrumb-item">Last seen: <span id="lastSeenDate">'+moment(Number(slave.time)).fromNow()+"</span></li>";
|
||||
HTML += '<li class="breadcrumb-item">Online players: '+slave.playerCount+"</li></ol></nav>";
|
||||
|
||||
// detect if remoteMap mod is installed, if it is we want to show the link for it
|
||||
let hasRemoteMap = false;
|
||||
slave.mods.forEach(mod => {
|
||||
console.log(mod.modName);
|
||||
if(mod.modName.includes("remoteMap")){
|
||||
hasRemoteMap = true; // we are still doing the logic on the outside, in case there are multiple instances of remoteMap installed....
|
||||
}
|
||||
});
|
||||
if(hasRemoteMap){
|
||||
HTML += "<a href='/remoteMap?instanceID="+getParameterByName("instanceID")+"'>Remote map</a>";
|
||||
}
|
||||
|
||||
HTML += "</div>"; // end of displayBody
|
||||
|
||||
// list mods and other metadata
|
||||
|
||||
@@ -14,13 +14,6 @@
|
||||
</div>
|
||||
<div id="display">
|
||||
<%
|
||||
function findInArray(key, value, array){
|
||||
let indexes = [];
|
||||
for(let i in array){
|
||||
if(array[i][key] && array[i][key].includes(value)) indexes.push(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
let html = ""
|
||||
for(let id in slaves){
|
||||
let slave = slaves[id];
|
||||
@@ -38,7 +31,6 @@
|
||||
<li class="list-group-item">IP: ${slave.publicIP}:${slave.serverPort}</li>
|
||||
<li class="list-group-item">Host: ${slave.mac}</li>
|
||||
</ul>
|
||||
${findInArray("modName", "remoteMap", slave.mods).length ? "<a href='/remoteMap?instanceID="+id+"'>Remote map</a>" : ""}
|
||||
`
|
||||
for(let o in locals.masterPlugins){
|
||||
let plug = locals.masterPlugins[o];
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<!doctype HTML>
|
||||
<head>
|
||||
<%- include("head.html") %>
|
||||
<script src="external/socket.io.js"></script>
|
||||
<script type="module" src="remoteMap.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<%- include("nav.html") %>
|
||||
<div id="body">
|
||||
<h2>remoteMap</h2>
|
||||
<div id="remoteMapContainer" style="float:left">
|
||||
<canvas class="remoteMap" id="remoteMapBackground" width="512" height="512" style="border:1px solid #000000;position:absolute;"></canvas>
|
||||
<canvas class="remoteMap" id="remoteMap" width="512" height="512" style="border:1px solid #000000;position:absolute;"></canvas>
|
||||
<canvas class="remoteMap" id="remoteMapSelection" width="512" height="512" style="border:1px solid #000000;position:absolute;"></canvas>
|
||||
</div>
|
||||
<div id="remoteMapItemSelector" style="float:left">
|
||||
<!-- Generated by script, don't edit -->
|
||||
</div>
|
||||
<div id="remoteMapSettings" style="float:left">
|
||||
<p>Map size: <input type="number" id="mapSize" style="color:black;"> (default 32)</p>
|
||||
<p>Tile size: <input type="number" id="tileSize" style="color:black;"> (default 8)</p>
|
||||
<p>FPS: <input type="number" id="fpsCounter" style="color:black;"></p>
|
||||
<p>Pause when window is deselected (saves CPU) <input type="checkbox" id="pauseOnBlur"></p>
|
||||
<p>Movement speed: <input type="number" id="movementSpeed" style="color:black;"> (default 0.2)</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
@@ -1,608 +0,0 @@
|
||||
/**
|
||||
Javascript to download and render a map of a factorio world from an instance using websockets and canvas
|
||||
|
||||
It is its responsibility to handle:
|
||||
* Display settings
|
||||
* Ordering of canvases
|
||||
* Querying slaves through master for map info
|
||||
* Drawing of sprites defined in external files to canvas
|
||||
* Handling input for placing/removing items
|
||||
* Transmitting item placements/removals through master
|
||||
|
||||
That is way too much for one file, so to anyone reading this:
|
||||
This file is horribly written and documented.
|
||||
I am sorry. Drawing code is at the bottom, good luck.
|
||||
|
||||
If you have questions and wish to contact me, my name
|
||||
is Danielv123 on Github, Discord and esperNet #factorio
|
||||
*/
|
||||
console.log(":)")
|
||||
/// ES6 imports
|
||||
// rules for how entities are drawn (sizes, offset etc)
|
||||
import {entityDrawRules} from "lib/entityDrawRules.js";
|
||||
import {getParameterByName, drawImageWithRotation, isImageOk} from "lib/utility.js";
|
||||
import spritesheetJson from "./pictures/spritesheet.js";
|
||||
import {itemSelector} from "./remoteMap/itemSelection.js";
|
||||
var global = {};
|
||||
|
||||
var itemSelectorGUI = new itemSelector("#remoteMapItemSelector", [
|
||||
{name:"stone-furnace"},
|
||||
{name:"accumulator"},
|
||||
{name:"electric-furnace"},
|
||||
{name:"inserter"},
|
||||
{name:"express-transport-belt"},
|
||||
]);
|
||||
|
||||
if(!localStorage.remoteMapConfig || localStorage.remoteMapConfig == "[object Object]"){
|
||||
localStorage.remoteMapConfig = JSON.stringify({
|
||||
mapSize: 32,
|
||||
tileSize: 8,
|
||||
movementSpeed: 0.2,
|
||||
});
|
||||
}
|
||||
|
||||
var remoteMapConfig = JSON.parse(localStorage.remoteMapConfig);
|
||||
|
||||
// Handle config changes through UI buttons
|
||||
(function(){
|
||||
let selectMapSize = document.querySelector("#mapSize");
|
||||
let selectTileSize = document.querySelector("#tileSize");
|
||||
if(selectMapSize && selectTileSize){
|
||||
selectMapSize.value = Number(remoteMapConfig.mapSize) || 32;
|
||||
selectTileSize.value = Number(remoteMapConfig.tileSize) || 8;
|
||||
|
||||
selectMapSize.onchange = function(){
|
||||
let config = JSON.parse(localStorage.remoteMapConfig);
|
||||
config.mapSize = Number(selectMapSize.value);
|
||||
localStorage.remoteMapConfig = JSON.stringify(config);
|
||||
console.log("Config updated: "+localStorage.remoteMapConfig);
|
||||
}
|
||||
selectTileSize.onchange = function(){
|
||||
let config = JSON.parse(localStorage.remoteMapConfig);
|
||||
config.tileSize = Number(selectTileSize.value);
|
||||
localStorage.remoteMapConfig = JSON.stringify(config);
|
||||
console.log("Config updated: "+localStorage.remoteMapConfig);
|
||||
}
|
||||
}
|
||||
let pauseOnBlur = document.querySelector("#pauseOnBlur");
|
||||
if(pauseOnBlur){
|
||||
pauseOnBlur.checked = localStorage.remoteMapPauseOnBlur || true;
|
||||
pauseOnBlur.onchange = function(){
|
||||
localStorage.remoteMapPauseOnBlur = pauseOnBlur.checked;
|
||||
}
|
||||
}
|
||||
let movementSpeed = document.querySelector("#movementSpeed");
|
||||
if(movementSpeed){
|
||||
movementSpeed.value = Number(remoteMapConfig.movementSpeed) || 0.2;
|
||||
movementSpeed.onchange = function(){
|
||||
let config = JSON.parse(localStorage.remoteMapConfig);
|
||||
config.movementSpeed = Number(movementSpeed.value);
|
||||
localStorage.remoteMapConfig = JSON.stringify(config);
|
||||
console.log("Config updated: "+localStorage.remoteMapConfig);
|
||||
|
||||
// also update config live
|
||||
remoteMapConfig.movementSpeed = config.movementSpeed;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
var socket = io.connect(document.location.origin);
|
||||
socket.on('hello', function (data) {
|
||||
console.log(data);
|
||||
socket.emit("registerMapRequester", {instanceID: getParameterByName("instanceID")});
|
||||
socket.on("mapRequesterReady", function(){
|
||||
setInterval(()=>{
|
||||
socket.emit("heartbeat"); // send our heartbeat to prevent being assumed dead
|
||||
},10000);
|
||||
// load important images then proceed with rendering (and loading more images)
|
||||
global.spritesheet = new Image();
|
||||
global.spritesheet.onload = function(){
|
||||
global.grassBackground = new Image();
|
||||
global.grassBackground.onload = function(){
|
||||
requestMapDraw();
|
||||
}
|
||||
global.grassBackground.src = "/pictures/grass-1.png";
|
||||
}
|
||||
global.spritesheet.src = "/pictures/spritesheet.png";
|
||||
});
|
||||
|
||||
socket.on("displayChunk", function(chunk){
|
||||
console.log("displayChunk triggered but I can't draw chunks");
|
||||
});
|
||||
socket.on("displayEntity", function(entity){
|
||||
// console.log("Displaying entity "+JSON.stringify(entity));
|
||||
drawEntity(entity);
|
||||
});
|
||||
});
|
||||
|
||||
function requestChunk(x,y){
|
||||
socket.emit('requestChunk', {x:x, y:y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
(function(){
|
||||
let remoteMapLayers = document.querySelectorAll(".remoteMap");
|
||||
remoteMapLayers.forEach(layer => {
|
||||
layer.addEventListener('contextmenu', event => event.preventDefault());
|
||||
layer.width = remoteMapConfig.tileSize * remoteMapConfig.mapSize;
|
||||
layer.height = remoteMapConfig.tileSize * remoteMapConfig.mapSize;
|
||||
});
|
||||
["width", "height"].forEach(style => document.querySelector("#remoteMapContainer").style[style] = remoteMapConfig.tileSize * remoteMapConfig.mapSize + "px");
|
||||
})();
|
||||
const canvas = document.querySelector("#remoteMap");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.font = "30px Arial";
|
||||
ctx.fillText("Use WASD to navigate.",10,50);
|
||||
|
||||
const selectionCanvas = document.querySelector("#remoteMapSelection");
|
||||
const selectionCtx = selectionCanvas.getContext("2d");
|
||||
selectionCtx.font = "30px Arial";
|
||||
selectionCtx.fillText("Selection layer",10,50);
|
||||
|
||||
const backgroundCanvas = document.querySelector("#remoteMapBackground");
|
||||
const backgroundCtx = backgroundCanvas.getContext("2d");
|
||||
// map view position, top left corner (or another corner?)
|
||||
window.playerPosition = {
|
||||
x:0,
|
||||
y:0,
|
||||
}
|
||||
window.cachePosition = {
|
||||
x:0,
|
||||
y:0,
|
||||
};
|
||||
function requestMapDraw(){
|
||||
let xLow = Math.floor(cachePosition.x % remoteMapConfig.tileSize);
|
||||
let yLow = Math.floor(cachePosition.y % remoteMapConfig.tileSize);
|
||||
|
||||
let xHigh = xLow+remoteMapConfig.mapSize;
|
||||
let yHigh = yLow+remoteMapConfig.mapSize;
|
||||
for(let x = xLow; x < xHigh; x++){
|
||||
for(let y = yLow; y < yHigh; y++){
|
||||
socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', keyDownHandler, false);
|
||||
document.addEventListener('keyup', keyUpHandler, false);
|
||||
var rightPressed = false;
|
||||
var leftPressed = false;
|
||||
var upPressed = false;
|
||||
var downPressed = false;
|
||||
function keyDownHandler(event) {
|
||||
if(event.keyCode == 39 || event.keyCode == 68) {
|
||||
rightPressed = true;
|
||||
}
|
||||
else if(event.keyCode == 37 || event.keyCode == 65) {
|
||||
leftPressed = true;
|
||||
}
|
||||
if(event.keyCode == 40 || event.keyCode == 83) {
|
||||
downPressed = true;
|
||||
}
|
||||
else if(event.keyCode == 38 || event.keyCode == 87) {
|
||||
upPressed = true;
|
||||
}
|
||||
}
|
||||
function keyUpHandler(event) {
|
||||
if(event.keyCode == 39 || event.keyCode == 68) {
|
||||
rightPressed = false;
|
||||
}
|
||||
else if(event.keyCode == 37 || event.keyCode == 65) {
|
||||
leftPressed = false;
|
||||
}
|
||||
if(event.keyCode == 40 || event.keyCode == 83) {
|
||||
downPressed = false;
|
||||
}
|
||||
else if(event.keyCode == 38 || event.keyCode == 87) {
|
||||
upPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
var entityCache = new Array(remoteMapConfig.mapSize);
|
||||
// populate cache with arrays of arrays
|
||||
for(let i = 0; i < remoteMapConfig.mapSize; i++){
|
||||
entityCache[i] = new Array(remoteMapConfig.mapSize);
|
||||
for(let o = 0; o < remoteMapConfig.mapSize; o++){
|
||||
entityCache[i][o] = " ";
|
||||
}
|
||||
}
|
||||
// cache navigation functions, for panning (walking, if you will)
|
||||
const cache = {
|
||||
walkDown: function walkUp(){
|
||||
cachePosition.y += remoteMapConfig.tileSize;
|
||||
for(let i = 0; i < remoteMapConfig.mapSize; i++){
|
||||
entityCache[i].shift(); // remove leftmost entry
|
||||
entityCache[i].push(" "); // add new entry on the right
|
||||
// get data from slaveMapper
|
||||
let x = cachePosition.x/remoteMapConfig.tileSize + i;
|
||||
let y = cachePosition.y/remoteMapConfig.tileSize+remoteMapConfig.mapSize-1;
|
||||
socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
},
|
||||
walkUp: function walkDown(){
|
||||
cachePosition.y -= remoteMapConfig.tileSize;
|
||||
for(let i = 0; i < remoteMapConfig.mapSize; i++){
|
||||
entityCache[i].pop(); // remove rightmost entry
|
||||
entityCache[i].unshift(" "); // add new entry on the left
|
||||
// get data from slaveMapper
|
||||
let x = cachePosition.x/remoteMapConfig.tileSize + i;
|
||||
let y = cachePosition.y/remoteMapConfig.tileSize;
|
||||
socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
},
|
||||
walkLeft: function walkLeft(){
|
||||
cachePosition.x += remoteMapConfig.tileSize;
|
||||
entityCache.shift();
|
||||
entityCache.push(new Array(remoteMapConfig.mapSize));
|
||||
// get data from slaveMapper
|
||||
// fill in a row on the right side of the screen, that is the bottom of the 1st level array
|
||||
for(let i = 0; i < remoteMapConfig.mapSize; i++){
|
||||
let x = cachePosition.x/remoteMapConfig.tileSize + remoteMapConfig.mapSize - 1;
|
||||
let y = cachePosition.y/remoteMapConfig.tileSize + i;
|
||||
socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
},
|
||||
walkRight: function walkRight(){
|
||||
cachePosition.x -= remoteMapConfig.tileSize;
|
||||
entityCache.pop();
|
||||
entityCache.unshift(new Array(remoteMapConfig.mapSize));
|
||||
// get data from slaveMapper
|
||||
// fill a row on the left side of the screen, that is the top column
|
||||
for(let i = 0; i < remoteMapConfig.mapSize; i++){
|
||||
let x = cachePosition.x/remoteMapConfig.tileSize;
|
||||
let y = cachePosition.y/remoteMapConfig.tileSize + i;
|
||||
socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")});
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawFromCache(){
|
||||
clear();
|
||||
let startTime = Date.now()
|
||||
entityCache.forEach(row => {
|
||||
row.forEach(doc => {
|
||||
if(doc && typeof doc == "object"){
|
||||
drawEntity(doc, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
window.requestAnimationFrame(renderLoop);
|
||||
var fpsTimings = {
|
||||
lastFrame: Date.now(),
|
||||
averageLength: 60,
|
||||
sum:0,
|
||||
counter: document.querySelector("#fpsCounter"),
|
||||
}
|
||||
var isPaused = false;
|
||||
window.onblur = function() {
|
||||
if(localStorage.remoteMapPauseOnBlur == "true"){
|
||||
isPaused = true;
|
||||
console.log("paused");
|
||||
}
|
||||
}
|
||||
window.onfocus = function() {
|
||||
if(isPaused){
|
||||
isPaused = false;
|
||||
console.log("unpaused");
|
||||
window.requestAnimationFrame(renderLoop);
|
||||
}
|
||||
}
|
||||
function getMousePos(canvas, evt) {
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
return {
|
||||
x: evt.clientX - rect.left,
|
||||
y: evt.clientY - rect.top
|
||||
};
|
||||
}
|
||||
window.mousePos = {};
|
||||
selectionCanvas.addEventListener('mousemove', function(evt) {
|
||||
var mousePos = getMousePos(selectionCanvas, evt);
|
||||
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
|
||||
window.mousePos.x = mousePos.x;
|
||||
window.mousePos.y = mousePos.y;
|
||||
}, false);
|
||||
selectionCanvas.addEventListener("mousedown", function(evt) {
|
||||
if(evt.which === 1) window.mousePos.clicked = true;
|
||||
if(evt.which === 3) window.mousePos.rightClicked = true;
|
||||
|
||||
// place item in world by sending it with websockets and shit
|
||||
let tileSize = remoteMapConfig.tileSize;
|
||||
let entity = {
|
||||
name: itemSelectorGUI.getItem().name,
|
||||
position:{
|
||||
x: Math.floor((playerPosition.x + window.mousePos.x) / tileSize),
|
||||
y: Math.floor((playerPosition.y + window.mousePos.y) / tileSize),
|
||||
},
|
||||
direction: itemSelectorGUI.getItem().direction.toString(),
|
||||
};
|
||||
if(evt.which === 1){ // left click to place
|
||||
console.log(JSON.stringify(entity));
|
||||
socket.emit("placeEntity", entity);
|
||||
} else if(evt.which === 3){ // right click to delete
|
||||
entity.name = "deleted";
|
||||
socket.emit("placeEntity", entity)
|
||||
}
|
||||
// console.log(entity);
|
||||
});
|
||||
selectionCanvas.addEventListener("mouseup", function(evt) {
|
||||
if(evt.which === 1) window.mousePos.clicked = false;
|
||||
if(evt.which === 3) window.mousePos.rightClicked = false;
|
||||
});
|
||||
function renderLoop(){
|
||||
// render game canvas
|
||||
let newTimestamp = Date.now();
|
||||
fpsTimings.sum = fpsTimings.sum / fpsTimings.averageLength * (fpsTimings.averageLength - 1);
|
||||
let timeSinceLastFrame;
|
||||
fpsTimings.sum += timeSinceLastFrame = newTimestamp - fpsTimings.lastFrame;
|
||||
fpsTimings.counter.value = (1000 / (fpsTimings.sum / fpsTimings.averageLength)).toPrecision(4);
|
||||
fpsTimings.lastFrame = newTimestamp;
|
||||
if(!isPaused){
|
||||
let moveSpeed = Number(remoteMapConfig.movementSpeed);
|
||||
let movement = moveSpeed * timeSinceLastFrame;
|
||||
// handle movement and requesting new entities
|
||||
if(upPressed){
|
||||
console.log("w");
|
||||
playerPosition.y -= movement;
|
||||
if(Math.abs((playerPosition.y-cachePosition.y)/remoteMapConfig.tileSize) > 1){
|
||||
for(let i = 0; i < Math.floor(Math.abs((playerPosition.y-cachePosition.y)/remoteMapConfig.tileSize)); i++){
|
||||
cache.walkUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(leftPressed){
|
||||
console.log("a");
|
||||
playerPosition.x -= movement;
|
||||
if(Math.abs((playerPosition.x-cachePosition.x)/remoteMapConfig.tileSize) > 1){
|
||||
for(let i = 0; i < Math.floor(Math.abs((playerPosition.x-cachePosition.x)/remoteMapConfig.tileSize)); i++){
|
||||
cache.walkRight();
|
||||
}}
|
||||
}
|
||||
if(downPressed){
|
||||
console.log("s");
|
||||
playerPosition.y += movement;
|
||||
if(Math.abs((playerPosition.y-cachePosition.y)/remoteMapConfig.tileSize) > 1){
|
||||
for(let i = 0; i < Math.floor(Math.abs((playerPosition.y-cachePosition.y)/remoteMapConfig.tileSize)); i++){
|
||||
cache.walkDown();
|
||||
}}
|
||||
}
|
||||
if(rightPressed){
|
||||
console.log("d");
|
||||
let oldPos = playerPosition.x;
|
||||
playerPosition.x += movement;
|
||||
if(Math.abs((playerPosition.x-cachePosition.x)/remoteMapConfig.tileSize) > 1){
|
||||
for(let i = 0; i < Math.floor(Math.abs((playerPosition.x-cachePosition.x)/remoteMapConfig.tileSize)); i++){
|
||||
cache.walkLeft();
|
||||
}}
|
||||
}
|
||||
// draw map
|
||||
drawFromCache();
|
||||
// queue next tick
|
||||
window.requestAnimationFrame(renderLoop);
|
||||
}
|
||||
|
||||
// render selection canvas
|
||||
let mousePosition = window.mousePos;
|
||||
if(mousePosition && mousePosition.x){
|
||||
// make a box around the mouse cursor
|
||||
selectionCtx.beginPath();
|
||||
selectionCtx.lineWidth = remoteMapConfig.tileSize / 16;
|
||||
selectionCtx.strokeStyle="yellow";
|
||||
if(mousePosition.clicked) selectionCtx.strokeStyle = "red";
|
||||
if(mousePosition.rightClicked) selectionCtx.strokeStyle = "blue";
|
||||
let tileSize = remoteMapConfig.tileSize
|
||||
let halfTile = tileSize / 2;
|
||||
let tilePosition = { // position in world coordinates (same as used by factorio)
|
||||
x: Math.floor((playerPosition.x + mousePosition.x) / tileSize),
|
||||
y: Math.floor((playerPosition.y + mousePosition.y) / tileSize),
|
||||
}
|
||||
// Long thing to correct for offset between playerPosition, cachePosition (tile grid) and mouse position and draw the box so it aligns with tiles.
|
||||
selectionCtx.rect(tilePosition.x * tileSize - playerPosition.x, tilePosition.y * tileSize - playerPosition.y, tileSize, tileSize);
|
||||
selectionCtx.stroke();
|
||||
}
|
||||
|
||||
// render background canvas
|
||||
let dw = 1024 / 32 * remoteMapConfig.tileSize;
|
||||
let dh = 64 / 32 * remoteMapConfig.tileSize;
|
||||
|
||||
// 0-dw and x < ?? + dw is to make sure we draw well outside the canvas on both sides to avoid ugly gfx
|
||||
for(let x = 0-dw; x < remoteMapConfig.tileSize * remoteMapConfig.mapSize + dw; x += dw){
|
||||
for(let y = 0-dh; y < remoteMapConfig.tileSize * remoteMapConfig.mapSize + dh; y += dh){
|
||||
let dx = x - playerPosition.x % dw;
|
||||
let dy = y - playerPosition.y % dh;
|
||||
if(global.grassBackground && isImageOk(global.grassBackground)) backgroundCtx.drawImage(global.grassBackground, 0, 64, 1024, 64, dx, dy, dw, dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
var entityImages = {}; // cache to store images and details about entities, populated by drawEntity();
|
||||
window.logEntCache = function(){console.log(entityCache)}
|
||||
function drawEntity(entity, dontCache){
|
||||
if(entity.x !== undefined && entity.y !== undefined){
|
||||
if(!dontCache){
|
||||
// cache entity for later draws (like panning)
|
||||
if(entity.x - cachePosition.x/remoteMapConfig.tileSize >= 0 && entity.x - cachePosition.x/remoteMapConfig.tileSize < remoteMapConfig.mapSize && entity.y - cachePosition.y/remoteMapConfig.tileSize >= 0 && entity.y - cachePosition.y/remoteMapConfig.tileSize < remoteMapConfig.mapSize){
|
||||
if(entity.entity){
|
||||
entityCache[entity.x - cachePosition.x/remoteMapConfig.tileSize][entity.y - cachePosition.y/remoteMapConfig.tileSize] = entity;
|
||||
} else {
|
||||
// delete this entity because we just heard the tile is empty and stuff
|
||||
entityCache[entity.x - cachePosition.x/remoteMapConfig.tileSize][entity.y - cachePosition.y/remoteMapConfig.tileSize] = " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
if(entity.entity && entity.entity.name && typeof entity.entity.name == "string"){
|
||||
let name = entity.entity.name;
|
||||
if(!entityImages[name]){
|
||||
console.log("Downloading image "+name);
|
||||
// download the entityImages and add stuff to queue
|
||||
entityImages[name] = {
|
||||
img: new Image(),
|
||||
queue: [],
|
||||
loaded: false,
|
||||
};
|
||||
entityImages[name].draw = function(entity){
|
||||
if(this.loaded){
|
||||
let name = entity.entity.name;
|
||||
let image, sprWidth, sprHeight, offLeft, offTop, size, offsetX, offsetY;
|
||||
let flipX = false, flipY = false;
|
||||
let rotation = 0;
|
||||
// check hardcoded entity draw rules for specifics (otherwise draw icon as 1x1 entity with rotation if specified)
|
||||
if(entityDrawRules[name] && (entityDrawRules[name].positionOffset || entityDrawRules[name].spritesheet)){
|
||||
let rules = entityDrawRules[name]
|
||||
if(rules.positionOffset){
|
||||
offsetX = rules.positionOffset.x;
|
||||
offsetY = rules.positionOffset.y;
|
||||
}
|
||||
if(rules.sizeInTiles){
|
||||
size = {
|
||||
x: remoteMapConfig.tileSize * rules.sizeInTiles.x,
|
||||
y: remoteMapConfig.tileSize * rules.sizeInTiles.y,
|
||||
};
|
||||
}
|
||||
if(rules.spritesheet && Array.isArray(rules.spritesheet)){
|
||||
let dir = Number(entity.entity.rot || 0);
|
||||
// 2 animated sprites, we will need to flip it for rotations.
|
||||
if(rules.spritesheet.length == 2){
|
||||
let spritesheetAnim = dir / 2 % 2;
|
||||
let animationStage = Math.floor(Date.now()/16) % rules.spritesheet[spritesheetAnim].spritesheet.length;
|
||||
|
||||
try{
|
||||
sprWidth = rules.spritesheet[spritesheetAnim].spritesheet[animationStage].frame.w;
|
||||
sprHeight = rules.spritesheet[spritesheetAnim].spritesheet[animationStage].frame.h;
|
||||
offLeft = rules.spritesheet[spritesheetAnim].spritesheet[animationStage].frame.x;
|
||||
offTop = rules.spritesheet[spritesheetAnim].spritesheet[animationStage].frame.y;
|
||||
} catch(e){
|
||||
// console.log(name)
|
||||
// console.log(rules.spritesheet[spritesheetAnim].spritesheet)
|
||||
}
|
||||
image = global.spritesheet;
|
||||
|
||||
// handle rotation (not handled by generic handler because we have to do flips and special stuff)
|
||||
if(dir == 4){
|
||||
// rotation = (dir/2)*90
|
||||
// console.log("rotating")
|
||||
flipY = true;
|
||||
} else if(dir == 6){
|
||||
flipX = true;
|
||||
// rotation = ((dir+2)/2)*90+180 // this formula is terrible, don't try to repair, there is no sense to it
|
||||
}
|
||||
|
||||
if(rules.spritesheet[spritesheetAnim].positionOffset){
|
||||
offsetX = rules.spritesheet[spritesheetAnim].positionOffset.x;
|
||||
offsetY = rules.spritesheet[spritesheetAnim].positionOffset.y;
|
||||
}
|
||||
if(rules.spritesheet[spritesheetAnim].sizeInTiles){
|
||||
size = {
|
||||
x: remoteMapConfig.tileSize * rules.spritesheet[spritesheetAnim].sizeInTiles.x,
|
||||
y: remoteMapConfig.tileSize * rules.spritesheet[spritesheetAnim].sizeInTiles.y,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// correct for whether we have 1, 4 or 8 directions
|
||||
let spriteSheetRotIndex = dir / (8 / rules.spritesheet.length);
|
||||
if(rules.spritesheet.length > 8) {
|
||||
// apparently this entity has more than 8 directions. That isn't supported.
|
||||
// therefore we can conclude that they are instead intended as animation frames.
|
||||
// That is why we treat them as animation frames.
|
||||
spriteSheetRotIndex = Math.floor(Date.now()/32) % rules.spritesheet.length;
|
||||
} else if(rules.spritesheet[spriteSheetRotIndex].spritesheet.length){
|
||||
var animationStage = Math.floor(Date.now()/16) % rules.spritesheet[spriteSheetRotIndex].spritesheet.length;
|
||||
var usesAnimation = true;
|
||||
} else {
|
||||
var usesAnimation = false;
|
||||
}
|
||||
|
||||
if(usesAnimation){
|
||||
try{
|
||||
sprWidth = rules.spritesheet[spriteSheetRotIndex].spritesheet[animationStage].frame.w;
|
||||
sprHeight = rules.spritesheet[spriteSheetRotIndex].spritesheet[animationStage].frame.h;
|
||||
offLeft = rules.spritesheet[spriteSheetRotIndex].spritesheet[animationStage].frame.x;
|
||||
offTop = rules.spritesheet[spriteSheetRotIndex].spritesheet[animationStage].frame.y;
|
||||
}catch(e){} // TODO throws error due to yellow belt rendering
|
||||
} else {
|
||||
sprWidth = rules.spritesheet[spriteSheetRotIndex].spritesheet.frame.w;
|
||||
sprHeight = rules.spritesheet[spriteSheetRotIndex].spritesheet.frame.h;
|
||||
offLeft = rules.spritesheet[spriteSheetRotIndex].spritesheet.frame.x;
|
||||
offTop = rules.spritesheet[spriteSheetRotIndex].spritesheet.frame.y;
|
||||
}
|
||||
image = global.spritesheet;
|
||||
|
||||
if(rules.spritesheet[spriteSheetRotIndex].positionOffset){
|
||||
offsetX = rules.spritesheet[spriteSheetRotIndex].positionOffset.x;
|
||||
offsetY = rules.spritesheet[spriteSheetRotIndex].positionOffset.y;
|
||||
}
|
||||
if(rules.spritesheet[spriteSheetRotIndex].sizeInTiles){
|
||||
size = {
|
||||
x: remoteMapConfig.tileSize * rules.spritesheet[spriteSheetRotIndex].sizeInTiles.x,
|
||||
y: remoteMapConfig.tileSize * rules.spritesheet[spriteSheetRotIndex].sizeInTiles.y,
|
||||
};
|
||||
}
|
||||
if(rules.spritesheet[spriteSheetRotIndex].flip){
|
||||
let flip = rules.spritesheet[spriteSheetRotIndex].flip;
|
||||
if(flip == "x") flipX = true;
|
||||
if(flip == "y") flipY = true;
|
||||
}
|
||||
}
|
||||
} else if(rules.spritesheet){
|
||||
sprWidth = rules.spritesheet.frame.w;
|
||||
sprHeight = rules.spritesheet.frame.h;
|
||||
offLeft = rules.spritesheet.frame.x;
|
||||
offTop = rules.spritesheet.frame.y;
|
||||
image = global.spritesheet;
|
||||
}
|
||||
} else {
|
||||
offsetX = 0, offsetY = 0;
|
||||
size = {
|
||||
x:remoteMapConfig.tileSize, y: remoteMapConfig.tileSize,
|
||||
};
|
||||
}
|
||||
let xPos = ((entity.x + offsetX) * remoteMapConfig.tileSize) - playerPosition.x;
|
||||
let yPos = ((entity.y + offsetY) * remoteMapConfig.tileSize) - playerPosition.y;
|
||||
if(entity.entity.rot && !isNaN(Number(entity.entity.rot)) && !(entityDrawRules[name] && Array.isArray(entityDrawRules[name].spritesheet))){
|
||||
let rules = entityDrawRules[name];
|
||||
if(rules && rules.rotOffset){
|
||||
rotation = ((entity.entity.rot * 45) + entityDrawRules[name].rotOffset);
|
||||
} else {
|
||||
rotation = entity.entity.rot * 45;
|
||||
}
|
||||
}
|
||||
if(!image) image = entityImages[name].img;
|
||||
// if(name == "oil-refinery") console.log(offsetX)
|
||||
drawImageWithRotation(ctx, image, xPos, yPos, size.x, size.y, rotation%360, sprWidth, sprHeight, offLeft, offTop, flipX, flipY);
|
||||
//console.log("Drawing "+name+" at X: "+xPos+", Y: "+yPos+" with with rotation "+rotation);
|
||||
} else {
|
||||
// we are waiting for the image to load, push the task to our queue. It will be processed once the image loads.
|
||||
this.queue.push(entity);
|
||||
}
|
||||
}
|
||||
let imageLoaded = function(){
|
||||
// process queue and display
|
||||
console.log("Image loaded!");
|
||||
entityImages[name].loaded = true;
|
||||
entityImages[name].queue.forEach(entity => {
|
||||
entityImages[name].draw(entity);
|
||||
});
|
||||
entityImages[name].queue = [];
|
||||
}
|
||||
entityImages[name].queue.push(entity);
|
||||
entityImages[name].img.onload = imageLoaded()
|
||||
|
||||
if(entityDrawRules[name] && entityDrawRules[name].spritesheet){
|
||||
// imageLoaded();
|
||||
} else entityImages[name].img.src = getImageFromName(name);
|
||||
} else {
|
||||
// console.log("Already got image, drawing")
|
||||
entityImages[name].draw(entity);
|
||||
}
|
||||
} else {
|
||||
// we got an empty coordinate pair, that usually means this tile was occupied before but is empty now
|
||||
console.log("Clearing at X: "+entity.x+", Y: "+entity.y);
|
||||
ctx.clearRect((entity.x + playerPosition.x/remoteMapConfig.tileSize) * remoteMapConfig.tileSize, (entity.y + playerPosition.y/remoteMapConfig.tileSize) * remoteMapConfig.tileSize, remoteMapConfig.tileSize, remoteMapConfig.tileSize);
|
||||
}
|
||||
} else {
|
||||
console.log(entity);
|
||||
throw new Error("drawEntity on entity without x and y coordinates");
|
||||
}
|
||||
}
|
||||
function clear(){
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
selectionCtx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height);
|
||||
backgroundCtx.clearRect(0, 0, backgroundCanvas.width, backgroundCanvas.height);
|
||||
}
|
||||
// setInterval(()=>console.log(entityDrawRules),5000);
|
||||
@@ -1,85 +0,0 @@
|
||||
export class itemSelector {
|
||||
constructor(containerSelector, entities){
|
||||
this.containerSelector = containerSelector;
|
||||
this.container = document.querySelector(containerSelector);
|
||||
if(!this.container) throw new Error("Could not find container "+container);
|
||||
this.entities = entities;
|
||||
this.direction = 0;
|
||||
|
||||
// populate container
|
||||
let cont = this.container;
|
||||
let HTML = "";
|
||||
HTML += "<div class='header'>";
|
||||
HTML += "<h2>Item selection</h2>";
|
||||
HTML += "</div>";
|
||||
HTML += "<div class='itemSelector'>";
|
||||
HTML += "<span>Name: </span>";
|
||||
HTML += "<select class='blackText'>";
|
||||
this.entities.forEach(entity => HTML += "<option value='"+entity.name+"'>"+entity.name+"</option>");
|
||||
HTML += "</select>";
|
||||
HTML += "</div>";
|
||||
// direction selector
|
||||
HTML += "<div id='directionSelector'>";
|
||||
HTML += "<h2>Direction: </h2>";
|
||||
HTML += "<h2 id='directionDisplay'>"+this.directionToCardinal(0)+"</h2>"
|
||||
HTML += "<p>Press R to rotate</p>";
|
||||
HTML += "</div>";
|
||||
|
||||
// write styles
|
||||
let styles = {
|
||||
".header > h2":{
|
||||
color:"white",
|
||||
},
|
||||
".blackText":{
|
||||
color:"black",
|
||||
},
|
||||
".blackText > option":{
|
||||
color:"black",
|
||||
}
|
||||
}
|
||||
|
||||
// turn style object into style tag
|
||||
HTML += "<style>";
|
||||
for(let style in styles){
|
||||
HTML += containerSelector+" "+style+"{";
|
||||
for(let styleName in styles[style]){
|
||||
let styleValue = styles[style][styleName];
|
||||
HTML += styleName+":"+styleValue+";";
|
||||
}
|
||||
HTML += "}";
|
||||
}
|
||||
cont.innerHTML += HTML + "</style>";
|
||||
|
||||
// handle direction keyboard event
|
||||
Mousetrap.bind('r', ()=>this.rotate());
|
||||
}
|
||||
directionToCardinal(dir){
|
||||
if(dir == 0){
|
||||
return "north";
|
||||
} else if(dir == 1){
|
||||
return "north-east";
|
||||
} else if(dir == 2){
|
||||
return "east";
|
||||
} else if(dir == 3){
|
||||
return "south-east";
|
||||
} else if(dir == 4){
|
||||
return "south";
|
||||
} else if(dir == 5){
|
||||
return "south-west";
|
||||
} else if(dir == 6){
|
||||
return "west";
|
||||
} else if(dir == 7){
|
||||
return "north-west";
|
||||
}
|
||||
}
|
||||
rotate(){
|
||||
this.direction = (this.direction + 2) % 8;
|
||||
// update display
|
||||
document.querySelector("#directionDisplay").innerHTML = this.directionToCardinal(this.direction);
|
||||
}
|
||||
getItem(){
|
||||
let name = document.querySelector(this.containerSelector+" > .itemSelector > select").value;
|
||||
let direction = this.direction;
|
||||
return {name, direction};
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,6 @@ setTimeout(function() {
|
||||
HTML += "<a href='nodeDetails?instanceID="+slaveData[key].unique+"'><h2>" + slaveData[key].instanceName + "</h2></a><p>ID: " + slaveData[key].unique + "</p><p>Last seen: "+seenDate+"</p><p>Online players: "+slaveData[key].playerCount+"</p><p>IP: "+slaveData[key].publicIP +":"+ slaveData[key].serverPort+"</p>"
|
||||
HTML += "<p>Host: "+slaveData[key].mac+"</p>";
|
||||
|
||||
// detect if remoteMap mod is installed, if it is we want to show the link for it
|
||||
let hasRemoteMap = false;
|
||||
if(slaveData[key].mods){ // not all instances have mods apparently
|
||||
slaveData[key].mods.forEach(mod => {
|
||||
console.log(mod.modName);
|
||||
if(mod.modName.includes("remoteMap")){
|
||||
hasRemoteMap = true; // we are still doing the logic on the outside, in case there are multiple instances of remoteMap installed....
|
||||
}
|
||||
});
|
||||
}
|
||||
if(hasRemoteMap){
|
||||
HTML += "<a href='/remoteMap?instanceID="+slaveData[key].unique+"'>Remote map</a>"
|
||||
}
|
||||
|
||||
HTML += "<br><p>Graph tools:</p>";
|
||||
HTML += "<button style='width:100px;' onclick='hideAllDatasets(\"" + slaveData[key].unique + "\")\'>Hide all</button>";
|
||||
HTML += "<button style='width:100px;' onclick='showAllDatasets(\"" + slaveData[key].unique + "\")\'>Show all</button>";
|
||||
|
||||
Reference in new issue
Block a user