From 51bb9f4d71496aee60a75d2af7394be32e4ebb76 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 3 Jan 2018 18:34:27 +0100 Subject: [PATCH] Live updates to map as things are placed or removed --- master.js | 21 ++++++-- sharedPlugins/remoteMap/chunkStore.js | 3 +- sharedPlugins/remoteMap/index.js | 18 +++++-- static/remoteMap.html | 2 +- static/remoteMap.js | 76 ++++++++++++++++++++------- 5 files changed, 91 insertions(+), 29 deletions(-) diff --git a/master.js b/master.js index 9b713d98..a50684bf 100644 --- a/master.js +++ b/master.js @@ -621,8 +621,18 @@ class slaveMapper { mapRequesters[data.requesterID].socket.emit("displayChunk", data); }); // slaveMapper sent us an entity update, process - this.socket.on("sendEntity", function(entity){ - mapRequesters[entity.requesterID].socket.emit("displayEntity", entity); + this.socket.on("sendEntity", entity => { + Object.keys(mapRequesters).forEach(requesterName => { + let requester = mapRequesters[requesterName]; + if(requester.lastBeat < (Date.now() - 30000)){ + console.log("There are currently "+Object.keys(mapRequesters).length+" map requesters, deleting one on timeout"); + delete mapRequesters[requesterName]; // we have to use the full name here or we will only kill the pointer + } + if(requester.instanceID == this.instanceID){ + // this mapRequester is listening to this slaveMapper, so we send it updates + requester.socket.emit("displayEntity", entity); + } + }); }); } } @@ -632,7 +642,12 @@ class mapRequester { this.requesterID = requesterID; this.socket = socket; this.instanceID = instanceID; + this.lastBeat = Date.now(); + this.socket.on("heartbeat", () => { + // we aren't ready to die yet apparently + this.lastBeat = Date.now(); + }); this.socket.on("requestChunk", loc => { loc.requesterID = this.requesterID; let instanceID = loc.instanceID || this.instanceID; @@ -659,7 +674,7 @@ io.on('connection', function (socket) { // data {instanceID:""} let requesterID = Math.random().toString(); mapRequesters[requesterID] = new mapRequester(requesterID, socket, data.instanceID); - + socket.emit("mapRequesterReady", true); console.log("SOCKET registered map requester for "+data.instanceID); }); }); diff --git a/sharedPlugins/remoteMap/chunkStore.js b/sharedPlugins/remoteMap/chunkStore.js index 69cd9c63..f8a4ee66 100644 --- a/sharedPlugins/remoteMap/chunkStore.js +++ b/sharedPlugins/remoteMap/chunkStore.js @@ -48,7 +48,7 @@ module.exports = class chunkStore { }); }); resolve(); - this.eventEmitter.emit("change", [{x,y}]); + this.eventEmitter.emit("change", {x,y}); } }); }); @@ -58,7 +58,6 @@ module.exports = class chunkStore { this.db.find({x:Math.floor(x), y:Math.floor(y)}, (err, docs) => { if(err) reject(err); resolve(docs); - this.eventEmitter.emit("change", docs); }); }); } diff --git a/sharedPlugins/remoteMap/index.js b/sharedPlugins/remoteMap/index.js index 6244275c..4b613399 100644 --- a/sharedPlugins/remoteMap/index.js +++ b/sharedPlugins/remoteMap/index.js @@ -12,12 +12,23 @@ class remoteMap { // initialize chunk database this.chunkMap = new chunkStore(this.config.unique, 64, "./database/chunkStore/"); + this.chunkMap.onEntityChange(entity => { + console.log("Entity changed: ") + console.log(entity); + // check valid coordinates (dunno what could go wrong, but can never be too sure) + if(entity && entity.x !== undefined && !isNaN(Number(entity.x)) && entity.y !== undefined && !isNaN(Number(entity.y))){ + this.socket.emit("sendEntity", entity); + } + }); // set up websocket communication and handle requests from web interface users (which are going through master) // socket should be a global this.socket = ioClient("http://"+this.config.masterIP+":"+this.config.masterPort); this.socket.on("hello", data => { this.socket.emit("registerSlaveMapper", {instanceID: this.config.unique}); + setInterval(()=>{ + this.socket.emit("heartbeat"); // send our heartbeat to prevent being assumed dead + },10000); }); this.socket.on("getChunk", req => { console.log("getChunk is not supported!"); @@ -27,11 +38,11 @@ class remoteMap { });*/ }); this.socket.on("getEntity", req => { - if(req.requesterID && req.x && !isNaN(Number(req.x)) && req.y && !isNaN(Number(req.y))){ + // check for valid coordinates + if(req.x !== undefined && !isNaN(Number(req.x)) && req.y !== undefined && !isNaN(Number(req.y))){ this.chunkMap.getEntity(req.x, req.y).then(entities => { if(entities && entities.length > 0){ entities.forEach(entity => { - entity.requesterID = req.requesterID; this.socket.emit("sendEntity", entity); }); } @@ -45,9 +56,8 @@ class remoteMap { } scriptOutput(data){ if(data){ - //this.messageInterface(data); + this.messageInterface(data); // express-transport-belt -35.5 -14.5 - data = data.split(" "); if(data && data[0] == undefined){ console.log("empty: "); diff --git a/static/remoteMap.html b/static/remoteMap.html index a21e4791..a0d1a77a 100644 --- a/static/remoteMap.html +++ b/static/remoteMap.html @@ -9,7 +9,7 @@ <%- include("sidebar.html") %>

remoteMap

- +
diff --git a/static/remoteMap.js b/static/remoteMap.js index 307eefe7..2ec41e4e 100644 --- a/static/remoteMap.js +++ b/static/remoteMap.js @@ -1,7 +1,23 @@ +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); +} + var socket = io.connect('http://localhost:8080'); socket.on('hello', function (data) { console.log(data); - socket.emit("registerMapRequester", {instanceID: '1611985668'}); + socket.emit("registerMapRequester", {instanceID: getParameterByName("instanceID")}); + socket.on("mapRequesterReady", function(){ + setInterval(()=>{ + socket.emit("heartbeat"); // send our heartbeat to prevent being assumed dead + },10000); + requestMapDraw(); + }); socket.on("displayChunk", function(chunk){ console.log(chunk); @@ -14,14 +30,11 @@ socket.on('hello', function (data) { }); function requestChunk(x,y){ - socket.emit('requestChunk', {x:x, y:y, instanceID: '1611985668'}); + socket.emit('requestChunk', {x:x, y:y, instanceID: getParameterByName("instanceID")}); } setTimeout(function(){ canvas = document.getElementById("remoteMap"); ctx = canvas.getContext("2d"); - ctx.beginPath(); - ctx.arc(95,50,40,0,2*Math.PI); - ctx.stroke(); ctx.font = "30px Arial"; ctx.fillText("Hello World",10,50); @@ -31,11 +44,12 @@ setTimeout(function(){ }; img.src = 'https://wiki.factorio.com/images/Lab.png'; - requestChunk(0, -1); -}, 500); +}, 1); entityImages = {}; +/* entityImages["express-transport-belt"] = new Image(); entityImages["express-transport-belt"].src = 'https://wiki.factorio.com/images/Express_transport_belt.png'; +*/ // map view position, top left corner (or another corner?) playerPosition = { @@ -50,28 +64,49 @@ function requestMapDraw(){ let yHigh = yLow+64; for(let x = xLow; x < xHigh; x++){ for(let y = yLow; y < yHigh; y++){ - socket.emit("requestEntity", {x, y, instanceID: '1611985668'}); + socket.emit("requestEntity", {x, y, instanceID: getParameterByName("instanceID")}); } } } -function drawEntity(entity, ctx){ +function drawEntity(entity){ if(entity.x && entity.y){ if(entity.entity && entity.entity.name && typeof entity.entity.name == "string"){ let name = entity.entity.name; - let xPos = (entity.x * 16) - playerPosition.x; - let yPos = (entity.y * 16) - playerPosition.y; if(!entityImages[name]){ - // download the entityImages - entityImages[name] = new Image(); - entityImages[name].onload = function(){ - ctx.drawImage(entityImages[name], xPos, yPos, 16, 16); - console.log("Drawing "+name+" at X: "+xPos+", Y: "+yPos); + // download the entityImages and add stuff to queue + entityImages[name] = {}; + entityImages[name].img = new Image(); + entityImages[name].queue = []; + entityImages[name].queue.push(entity); + entityImages[name].loaded = false; + entityImages[name].img.onload = 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].src = getImageFromName(name); + entityImages[name].draw = function(entity){ + if(this.loaded){ + let name = entity.entity.name; + let xPos = (entity.x * 16) - playerPosition.x; + let yPos = (entity.y * 16) - playerPosition.y; + ctx.drawImage(entityImages[name].img, xPos, yPos, 16, 16); + console.log("Drawing "+name+" at X: "+xPos+", Y: "+yPos); + } else { + this.queue.push(entity); + } + } + entityImages[name].img.src = getImageFromName(name); + } else { + entityImages[name].draw(entity); } - } else { - throw new Error(entity); + // 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 * 16, entity.y * 16, 16, 16); } } else { throw new Error("drawEntity on entity without x and y coordinates") @@ -96,3 +131,6 @@ function drawChunk(chunk, ctx){ }); }); } +function clear(){ + ctx.clearRect(0, 0, canvas.width, canvas.height); +} \ No newline at end of file