From 103f01b6a204f30b9f669c184ab88051540246e6 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Thu, 1 Jun 2017 13:25:59 +0200 Subject: [PATCH 01/21] inventory reports, work on #50 --- sharedPlugins/inventoryImports/config.js | 20 ++++++++++ sharedPlugins/inventoryImports/index.js | 39 +++++++++++++++++++ sharedPlugins/inventoryImports/isJson.js | 9 +++++ .../inventoryImports/lua commands.lua | 25 ++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 sharedPlugins/inventoryImports/config.js create mode 100644 sharedPlugins/inventoryImports/index.js create mode 100644 sharedPlugins/inventoryImports/isJson.js create mode 100644 sharedPlugins/inventoryImports/lua commands.lua diff --git a/sharedPlugins/inventoryImports/config.js b/sharedPlugins/inventoryImports/config.js new file mode 100644 index 00000000..4f49d047 --- /dev/null +++ b/sharedPlugins/inventoryImports/config.js @@ -0,0 +1,20 @@ +/* + +This clusterio plugin is tasked to read your inventory and logistic request slots +it will then attempt to fulfill those requests using items provided by the master +server. There *should* be an in game GUI to toggle this. + +*/ + +module.exports = { + // Name of package. For display somewhere I guess. + name: "inventoryImports", + version: "1.0.0", + // Binary entrypoint for plugin. Don't let it crash. Stdout is sent to game as server chat (run /c commands from here for interface) + // Make sure its cross platform somehow. + binary: "node", + arguments: ["index.js"], + description: "Clusterio plugin to fill character logistic slots with master imports", + // We'll send everything in this file to your stdin. Beware. + scriptOutputFileSubscription: "inventoryImports.txt", +} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js new file mode 100644 index 00000000..b4db6e89 --- /dev/null +++ b/sharedPlugins/inventoryImports/index.js @@ -0,0 +1,39 @@ +const needle = require("needle"); +const isJson = require("./isJson.js"); +const pluginConfig = require("./config.js"); + +console.log("/silent-command game.print('UPSdisplay enabled')\n"); + +setInterval(function(){ + console.log("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)\n"); +},1000); +var config = {}; +process.stdin.setEncoding('utf8'); +let historicalTicks = []; +process.stdin.on('readable', () => { + var chunk = process.stdin.read(); + if (chunk !== null && isNaN(chunk)){ + let inventory = ""; + // eval because my lua is writing a JS object instead of traditional JSON + // this was done because I hate escaping strings and there are more quotes + // in JSON than JS objects. + eval("inventory = " + chunk); + console.log(inventory); + } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ + config = JSON.parse(chunk); + } +}); +process.stdin.on('end', () => { + process.stdout.write('end'); +}); + +// poll inventories +setInterval(function(){ + // TODO: Make it work for players other than myself, don't error when inventory does not exist, check logistic request slots + console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); + /* + needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { + // success? + }); + */ +}, 10000); diff --git a/sharedPlugins/inventoryImports/isJson.js b/sharedPlugins/inventoryImports/isJson.js new file mode 100644 index 00000000..6bd89302 --- /dev/null +++ b/sharedPlugins/inventoryImports/isJson.js @@ -0,0 +1,9 @@ +module.exports = function(x){ + var isNotJson = false + try{ + z = JSON.parse(x); + } catch (e) { + isNotJson = true; + } + return !isNotJson; +} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua new file mode 100644 index 00000000..1c8c1065 --- /dev/null +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -0,0 +1,25 @@ +-- Lua cheatsheet + +/c game.write_file("test.txt",serpent.block(game.players["Danielv123"].get_quickbar().get_contents())) +/c game.write_file("test.txt",serpent.block(game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents())) + +-- print JSON of entire inventory and request slots to file +-- TODO: Get requester slot data as well +/c +local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents() +local quickbar = game.players["Danielv123"].get_quickbar().get_contents() +local requestSlots = {} +local o = "{inventory:{" +for k,v in pairs(inventory) do + o = o.."['"..k.."']:"..v.."," +end +for k,v in pairs(quickbar) do + o = o.."['"..k.."']:"..v.."," +end +o = o.."},requestSlots:{" +for k,v in pairs(requestSlots) do +o = o.."['"..k.."']:"..v.."," +end +o = o.."}}" +game.write_file("test.txt", o) + From 18a40ca80644007f69166dd1a92687c43aab2bed Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 2 Jun 2017 22:29:28 +0200 Subject: [PATCH 02/21] More lua experimenting --- .../inventoryImports/lua commands.lua | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index 1c8c1065..897ffe30 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -5,10 +5,33 @@ -- print JSON of entire inventory and request slots to file -- TODO: Get requester slot data as well + +-- requester slot data is game.players.character.get_request_slot(slot) +-- returns {name="copper-plate", count=47} or nil if nothing is set +-- maybe game.players.character.item_requests? http://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.item_requests + +/c +local requests = {} +for i = 1, game.player.force.character_logistic_slot_count do + requests[i] = game.players["Danielv123"].character.get_request_slot(i) +end + +local o = "Requests: " +for k,v in pairs(requests) do + o = o..v["name"]..": "..v["count"]..", " +end +game.print(o) + /c local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents() local quickbar = game.players["Danielv123"].get_quickbar().get_contents() -local requestSlots = {} +--[[ Get contents of requester slots into a table ]] +local requests = {} +for i = 1, game.player.force.character_logistic_slot_count do + requests[i] = game.players["Danielv123"].character.get_request_slot(i) +end + +--[[ generate JS table (not JSON) ]] local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['"..k.."']:"..v.."," @@ -16,10 +39,12 @@ end for k,v in pairs(quickbar) do o = o.."['"..k.."']:"..v.."," end + o = o.."},requestSlots:{" -for k,v in pairs(requestSlots) do -o = o.."['"..k.."']:"..v.."," +for k,v in pairs(requests) do + o = o.."['"..v["name"].."']:"..v["count"]..", " end o = o.."}}" +game.print(o) game.write_file("test.txt", o) From ee133761f7e589c2a674948f190263ce01537dbb Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 3 Jun 2017 11:48:07 +0200 Subject: [PATCH 03/21] fixed slaves not registering on windows --- client.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client.js b/client.js index 6570e837..c8efcd25 100644 --- a/client.js +++ b/client.js @@ -66,9 +66,9 @@ function messageInterface(command, callback) { client.exec(command, callback); } catch (err) { console.log(err); - if(typeof callback == "function"){ - callback(err); - } + } + if(typeof callback == "function"){ + callback(); } } } @@ -305,7 +305,7 @@ function instanceManagement() { // load plugins and execute onLoad event let pluginDirectories = fileOps.getDirectoriesSync("./sharedPlugins/"); let plugins = []; - for(i=0; i Date: Sat, 3 Jun 2017 15:06:27 +0200 Subject: [PATCH 04/21] possibly fixed rare crash on connect in UPSdisplay --- sharedPlugins/UPSdisplay/index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sharedPlugins/UPSdisplay/index.js b/sharedPlugins/UPSdisplay/index.js index 05dfab0f..7b71bc03 100644 --- a/sharedPlugins/UPSdisplay/index.js +++ b/sharedPlugins/UPSdisplay/index.js @@ -31,8 +31,12 @@ setInterval(function(){ let ticksInPeriod = historicalTicks[historicalTicks.length-1].tick - historicalTicks[0].tick; let UPS = Math.round(ticksInPeriod / (timePeriod/1000)); // console.log("UPS: " + UPS); - needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { - // success? - }); + try{ + needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { + // success? + }); + } catch (err){ + console.log(err); + } } }, 10000); \ No newline at end of file From 53cd6c846a4c29bae774dc5b952b198eb4ca363e Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 3 Jun 2017 15:07:26 +0200 Subject: [PATCH 05/21] inventoryImport now reads inventory successfully --- sharedPlugins/inventoryImports/index.js | 6 ++++-- .../inventoryImports/lua commands.lua | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index b4db6e89..eb61903a 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -13,10 +13,10 @@ let historicalTicks = []; process.stdin.on('readable', () => { var chunk = process.stdin.read(); if (chunk !== null && isNaN(chunk)){ - let inventory = ""; // eval because my lua is writing a JS object instead of traditional JSON // this was done because I hate escaping strings and there are more quotes // in JSON than JS objects. + let inventory = ""; eval("inventory = " + chunk); console.log(inventory); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ @@ -30,7 +30,9 @@ process.stdin.on('end', () => { // poll inventories setInterval(function(){ // TODO: Make it work for players other than myself, don't error when inventory does not exist, check logistic request slots - console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); + // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); + // console.log("Running inventory LUA") + console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') /* needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { // success? diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index 897ffe30..c2173160 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -27,7 +27,7 @@ local inventory = game.players["Danielv123"].get_inventory(defines.inventory.pla local quickbar = game.players["Danielv123"].get_quickbar().get_contents() --[[ Get contents of requester slots into a table ]] local requests = {} -for i = 1, game.player.force.character_logistic_slot_count do +for i = 1, game.players["Danielv123"].force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end @@ -48,3 +48,19 @@ o = o.."}}" game.print(o) game.write_file("test.txt", o) + +--[[ + +Minification procedure: + +Run through https://mothereff.in/lua-minifier +Replace ' with '+"'"+' +Prepend /silent-command +enclose in '' + +]] + +-- minified strings + +local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.print(e) +local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local quickbar = game.players["Danielv123"].get_quickbar().get_contents()local requests = {}for i = 1, game.player.force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end for k,v in pairs(quickbar) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end o = o.."},requestSlots:{" for k,v in pairs(requests) do o = o.."['+"'"+'"..v["name"].."'+"'"+']:"..v["count"]..", " end o = o.."}}" game.write_file("test.txt", o) \ No newline at end of file From 6466e1250450cd5294130bfcec962bcbf7f68df8 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Mon, 5 Jun 2017 12:52:40 +0200 Subject: [PATCH 06/21] working on multi player reports --- sharedPlugins/inventoryImports/lua commands.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index c2173160..4af884fd 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -23,6 +23,10 @@ end game.print(o) /c + +local o = "" + +for a,b in pairs(game.players) do local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents() local quickbar = game.players["Danielv123"].get_quickbar().get_contents() --[[ Get contents of requester slots into a table ]] @@ -31,8 +35,8 @@ for i = 1, game.players["Danielv123"].force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end +o = o.."{inventory:{" --[[ generate JS table (not JSON) ]] -local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['"..k.."']:"..v.."," end @@ -45,6 +49,7 @@ for k,v in pairs(requests) do o = o.."['"..v["name"].."']:"..v["count"]..", " end o = o.."}}" +end game.print(o) game.write_file("test.txt", o) From a7fcde60b80c9ccccb4a9f79cabeb00acd4cdd82 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Tue, 6 Jun 2017 10:45:44 +0200 Subject: [PATCH 07/21] Reporting for multiple players --- sharedPlugins/inventoryImports/index.js | 54 +++++++++++++-- .../inventoryImports/lua commands.lua | 65 ++++++++++++------- 2 files changed, 90 insertions(+), 29 deletions(-) diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index eb61903a..85898ccc 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -1,12 +1,10 @@ const needle = require("needle"); const isJson = require("./isJson.js"); const pluginConfig = require("./config.js"); +const objectOps = require("../../lib/objectOps.js"); -console.log("/silent-command game.print('UPSdisplay enabled')\n"); +console.log("/silent-command game.print('InventoryImports enabled')\n"); -setInterval(function(){ - console.log("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)\n"); -},1000); var config = {}; process.stdin.setEncoding('utf8'); let historicalTicks = []; @@ -18,7 +16,51 @@ process.stdin.on('readable', () => { // in JSON than JS objects. let inventory = ""; eval("inventory = " + chunk); + /* Example output: + {'1':{ + inventory: { + 'raw-wood': 4 + }, + requestSlots:{ + 'underground-belt': 50, + 'fast-transport-belt': 50, + 'express-underground-belt': 50 + }}} + */ console.log(inventory); + // we want to seperate players, parse their inventory and compare to request slots + // then ask master for the difference + // we must then prepare it all and inject it into players inventories with lua before + // the next inventory report arrives to avoid duplicating orders + if(inventory && typeof inventory != "string"){ + let players = Object.keys(inventory); + for(let i = 0; i < players.length; i++) { + let pinv = inventory[players[i]]; + // create a copy of player.requestSlots + let toRequest = objectOps.deepclone(pinv.requestSlots); + // subtract items already in inventory + Object.keys(pinv.inventory).forEach(function(key){ + let itemCount = pinv.inventory[key]; + if(toRequest[key]){ + toRequest[key] -= itemCount; + } + let request = { + name:key, + count:toRequest[key], + } + // request the difference from master + needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { + if (response && response.body && typeof response.body == "object") { + // confirmed orders, already removed from master inventory + //response.body.name: response.body.count + + } + }); + }); + + } + } + } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ config = JSON.parse(chunk); } @@ -32,7 +74,9 @@ setInterval(function(){ // TODO: Make it work for players other than myself, don't error when inventory does not exist, check logistic request slots // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); // console.log("Running inventory LUA") - console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') + //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') + // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') + console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') /* needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { // success? diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index 4af884fd..30de641c 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -22,37 +22,51 @@ for k,v in pairs(requests) do end game.print(o) +-- sum up our inventory and request slots /c - -local o = "" - +local o = "{" for a,b in pairs(game.players) do -local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents() -local quickbar = game.players["Danielv123"].get_quickbar().get_contents() ---[[ Get contents of requester slots into a table ]] -local requests = {} -for i = 1, game.players["Danielv123"].force.character_logistic_slot_count do - requests[i] = game.players["Danielv123"].character.get_request_slot(i) -end + if b.connected then + local inventory = game.players[a].get_inventory(defines.inventory.player_main).get_contents() + local quickbar = game.players[a].get_quickbar().get_contents() + --[[ Get contents of requester slots into a table ]] + local requests = {} + for i = 1, game.players[a].force.character_logistic_slot_count do + requests[i] = game.players[a].character.get_request_slot(i) + end -o = o.."{inventory:{" ---[[ generate JS table (not JSON) ]] -for k,v in pairs(inventory) do - o = o.."['"..k.."']:"..v.."," -end -for k,v in pairs(quickbar) do - o = o.."['"..k.."']:"..v.."," -end + o = o..a..":{inventory:{" + --[[ generate JS table (not JSON) ]] + for k,v in pairs(inventory) do + o = o.."['"..k.."']:"..v.."," + end + for k,v in pairs(quickbar) do + o = o.."['"..k.."']:"..v.."," + end -o = o.."},requestSlots:{" -for k,v in pairs(requests) do - o = o.."['"..v["name"].."']:"..v["count"]..", " -end -o = o.."}}" + o = o.."},requestSlots:{" + for k,v in pairs(requests) do + o = o.."['"..v["name"].."']:"..v["count"]..", " + end + o = o.."}}," + end end +o = o.."}" game.print(o) game.write_file("test.txt", o) +-- insert items in players inventory and export leftovers +/c +local name = "Danielv123" +local itemName = "iron-ore" +local itemCount = 100 +if game.players[name] do + local items = {name=itemName, count=itemCount} + if game.players[name].can_insert(items) do + local insertedItems = game.players[name].insert(items) + local restItems = itemCount - insertedItems + end +end --[[ @@ -66,6 +80,9 @@ enclose in '' ]] -- minified strings - +-- offline playes +local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.print(a) +-- multiple players +local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.print(a) local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.print(e) local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local quickbar = game.players["Danielv123"].get_quickbar().get_contents()local requests = {}for i = 1, game.player.force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end for k,v in pairs(quickbar) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end o = o.."},requestSlots:{" for k,v in pairs(requests) do o = o.."['+"'"+'"..v["name"].."'+"'"+']:"..v["count"]..", " end o = o.."}}" game.write_file("test.txt", o) \ No newline at end of file From dbf209de61ff723c0cfd053cf78b3bc4225e032e Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Tue, 6 Jun 2017 11:12:05 +0200 Subject: [PATCH 08/21] Tests, 90% coverage --- package.json | 2 +- sharedPlugins/inventoryImports/isJson.js | 2 ++ sharedPlugins/inventoryImports/isJson.spec.js | 20 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 sharedPlugins/inventoryImports/isJson.spec.js diff --git a/package.json b/package.json index 3dfc8462..b0fe3e4b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "test": "npm run test-unit", "test-unit": "mocha --recursive \"./{,!(node_modules)/**/}*.spec.js\" || echo .", "start": "echo Run node client.js start [instance] to start slaves && node master.js", - "cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- lib/*.js -R spec" + "cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{,!(node_modules)/**/}*.spec.js\" -R spec" }, "keywords": [ "factorio" diff --git a/sharedPlugins/inventoryImports/isJson.js b/sharedPlugins/inventoryImports/isJson.js index 6bd89302..597564df 100644 --- a/sharedPlugins/inventoryImports/isJson.js +++ b/sharedPlugins/inventoryImports/isJson.js @@ -1,9 +1,11 @@ module.exports = function(x){ + if(x === false || x === true || x === null) return true; // === false is valid JSON, so is true and null var isNotJson = false try{ z = JSON.parse(x); } catch (e) { isNotJson = true; } + return !isNotJson; } \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isJson.spec.js b/sharedPlugins/inventoryImports/isJson.spec.js new file mode 100644 index 00000000..4f76fe48 --- /dev/null +++ b/sharedPlugins/inventoryImports/isJson.spec.js @@ -0,0 +1,20 @@ +const isJson = require("./isJson"); +const assert = require("assert"); + +describe("isJson.js()", function(){ + it("checks if a thing is valid JSON", function(){ + assert(isJson("string") === false); + assert(isJson("{js_object_notation='very false'}") === false); + assert(isJson('{ "name":"John", "age":31, "city":"New York" }') === true); + }); + it("recognizes that true and false are both valid JSON", function(){ + assert(isJson(true) === true); + assert(isJson(false) === true); + }); + it("Handles null as valid JSON (after spec)", function(){ + assert(isJson(null) === true); + }); + it("Recognizes that JSON doesn't allow trailing commas (,)", function(){ + assert(isJson('["a", "b", ]') === false); + }); +}); From 913887fd2cb6659affc21bcd41f064120021144d Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 7 Jun 2017 07:26:14 +0200 Subject: [PATCH 09/21] Removed \n from UPSdisplay --- sharedPlugins/UPSdisplay/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sharedPlugins/UPSdisplay/index.js b/sharedPlugins/UPSdisplay/index.js index 7b71bc03..269067ec 100644 --- a/sharedPlugins/UPSdisplay/index.js +++ b/sharedPlugins/UPSdisplay/index.js @@ -1,9 +1,9 @@ const needle = require("needle"); -console.log("/silent-command game.print('UPSdisplay enabled')\n"); +console.log("/silent-command game.print('UPSdisplay enabled')"); setInterval(function(){ - console.log("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)\n"); + console.log("/silent-command game.write_file('UPSdisplay.txt', game.tick, true, 0)"); },1000); var config = {}; process.stdin.setEncoding('utf8'); From 00dd22e59fa14580b1e61cd66e23ed71d40deddd Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 7 Jun 2017 20:33:20 +0200 Subject: [PATCH 10/21] Slight improvements to existing tests --- lib/fileOps.spec.js | 41 ++++++++++--------- sharedPlugins/inventoryImports/isJson.spec.js | 4 +- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/fileOps.spec.js b/lib/fileOps.spec.js index 77ffb249..b9eda49c 100644 --- a/lib/fileOps.spec.js +++ b/lib/fileOps.spec.js @@ -3,26 +3,29 @@ var fileOps = require('./fileOps'); var fs = require('fs'); // create folders for testing -if (!fs.existsSync("lib/test")){ - fs.mkdirSync("lib/test"); +function setupTestingEnv(){ + if (!fs.existsSync("lib/test")){ + fs.mkdirSync("lib/test"); + } + if (!fs.existsSync("lib/test/folder")){ + fs.mkdirSync("lib/test/folder"); + } + if (!fs.existsSync("lib/test/another folder")){ + fs.mkdirSync("lib/test/another folder"); + } + if (!fs.existsSync("lib/test/file.txt")){ + fs.writeFileSync("lib/test/file.txt", "contents"); + } + if (!fs.existsSync("lib/test/another file.txt")){ + fs.writeFileSync("lib/test/another file.txt", "more contents"); + } + // we need this xfile to test a branch in fileOps.getNewestFile()s propagation + if (!fs.existsSync("lib/test/xfile.txt")){ + fs.writeFileSync("lib/test/xfile.txt", "contents"); + } } -if (!fs.existsSync("lib/test/folder")){ - fs.mkdirSync("lib/test/folder"); -} -if (!fs.existsSync("lib/test/another folder")){ - fs.mkdirSync("lib/test/another folder"); -} -if (!fs.existsSync("lib/test/file.txt")){ - fs.writeFileSync("lib/test/file.txt", "contents"); -} -if (!fs.existsSync("lib/test/another file.txt")){ - fs.writeFileSync("lib/test/another file.txt", "more contents"); -} -// we need this xfile to test a branch in fileOps.getNewestFile()s propagation -if (!fs.existsSync("lib/test/xfile.txt")){ - fs.writeFileSync("lib/test/xfile.txt", "contents"); -} - +setupTestingEnv(); +setupTestingEnv(); // run twice to get branch coverage describe("fileOps.js", function(){ describe("fileOps.getDirectoriesSync()", function(){ it("gets an array of directory names from a path", function(){ diff --git a/sharedPlugins/inventoryImports/isJson.spec.js b/sharedPlugins/inventoryImports/isJson.spec.js index 4f76fe48..ece8c324 100644 --- a/sharedPlugins/inventoryImports/isJson.spec.js +++ b/sharedPlugins/inventoryImports/isJson.spec.js @@ -5,7 +5,7 @@ describe("isJson.js()", function(){ it("checks if a thing is valid JSON", function(){ assert(isJson("string") === false); assert(isJson("{js_object_notation='very false'}") === false); - assert(isJson('{ "name":"John", "age":31, "city":"New York" }') === true); + assert(isJson('{ "name":"John", "age":31, "city":"New York" }') === true, "This is valid JSON, lib disagrees"); }); it("recognizes that true and false are both valid JSON", function(){ assert(isJson(true) === true); @@ -15,6 +15,6 @@ describe("isJson.js()", function(){ assert(isJson(null) === true); }); it("Recognizes that JSON doesn't allow trailing commas (,)", function(){ - assert(isJson('["a", "b", ]') === false); + assert(isJson('["a", "b", ]') === false, "JSON should not allow trailing commas"); }); }); From e5b6a9452482148c8b930b5da5ce189039c20bab Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 7 Jun 2017 20:33:58 +0200 Subject: [PATCH 11/21] less clutter on npm run cover --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b0fe3e4b..02f9653b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "test": "npm run test-unit", "test-unit": "mocha --recursive \"./{,!(node_modules)/**/}*.spec.js\" || echo .", "start": "echo Run node client.js start [instance] to start slaves && node master.js", - "cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{,!(node_modules)/**/}*.spec.js\" -R spec" + "cover": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- \"./{,!(node_modules)/**/}*.spec.js\" -R spec || echo ." }, "keywords": [ "factorio" From 0ada84e1532bf0a346d3a78c763be2f663f4a9bf Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 7 Jun 2017 20:38:54 +0200 Subject: [PATCH 12/21] #50 nearing completion, needs taping together and testing Includes great unit test coverage (npm run cover reports ~85%) --- .gitignore | 9 +- package.json | 1 + sharedPlugins/inventoryImports/index.js | 85 +++-------- .../inventoryImports/interfacingFunctions.js | 134 ++++++++++++++++++ .../interfacingFunctions.spec.js | 61 ++++++++ .../inventoryImports/lua commands.lua | 54 +++++-- 6 files changed, 264 insertions(+), 80 deletions(-) create mode 100644 sharedPlugins/inventoryImports/interfacingFunctions.js create mode 100644 sharedPlugins/inventoryImports/interfacingFunctions.spec.js diff --git a/.gitignore b/.gitignore index 9b288416..b31280a2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,11 @@ Factorio* /instances /sharedMods /database -/sharedPlugins database/items.db -config.json \ No newline at end of file +config.json + +# weird pattern to ignore sharedPlugins except not ignore some core modules +!sharedPlugins/ +sharedPlugins/* +!sharedPlugins/inventoryImports/ +!sharedPlugins/UPSdisplay/ \ No newline at end of file diff --git a/package.json b/package.json index 02f9653b..38547851 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "fs-extra": "^1.0.0", "getmac": "^1.2.1", "hash-files": "^1.1.1", + "luamin": "^1.0.4", "mkdirp": "^0.5.1", "ncp": "^2.0.0", "nedb": "^1.8.0", diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index 85898ccc..94a98101 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -1,68 +1,36 @@ const needle = require("needle"); const isJson = require("./isJson.js"); const pluginConfig = require("./config.js"); -const objectOps = require("../../lib/objectOps.js"); + +const functions = require("./interfacingFunctions"); console.log("/silent-command game.print('InventoryImports enabled')\n"); var config = {}; process.stdin.setEncoding('utf8'); -let historicalTicks = []; process.stdin.on('readable', () => { var chunk = process.stdin.read(); - if (chunk !== null && isNaN(chunk)){ - // eval because my lua is writing a JS object instead of traditional JSON - // this was done because I hate escaping strings and there are more quotes - // in JSON than JS objects. - let inventory = ""; - eval("inventory = " + chunk); - /* Example output: - {'1':{ - inventory: { - 'raw-wood': 4 - }, - requestSlots:{ - 'underground-belt': 50, - 'fast-transport-belt': 50, - 'express-underground-belt': 50 - }}} - */ + if (chunk !== null && isNaN(chunk) && config && !isJson(chunk)&& !chunk.includes("[")){ + // returns JS object, throws if there are syntax errors in input string + let inventory = functions.parseJsString(chunk); console.log(inventory); - // we want to seperate players, parse their inventory and compare to request slots - // then ask master for the difference - // we must then prepare it all and inject it into players inventories with lua before - // the next inventory report arrives to avoid duplicating orders - if(inventory && typeof inventory != "string"){ - let players = Object.keys(inventory); - for(let i = 0; i < players.length; i++) { - let pinv = inventory[players[i]]; - // create a copy of player.requestSlots - let toRequest = objectOps.deepclone(pinv.requestSlots); - // subtract items already in inventory - Object.keys(pinv.inventory).forEach(function(key){ - let itemCount = pinv.inventory[key]; - if(toRequest[key]){ - toRequest[key] -= itemCount; - } - let request = { - name:key, - count:toRequest[key], - } - // request the difference from master - needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { - if (response && response.body && typeof response.body == "object") { - // confirmed orders, already removed from master inventory - //response.body.name: response.body.count - - } - }); - }); - - } - } - + // process inventory of players and ask master for more stuff + functions.handleInventory(inventory, config); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ config = JSON.parse(chunk); + // if its a LUA table message returning with the exports send em off to master + } else if(!isJson(chunk) && !!chunk && config){ + let thing = functions.parseJsString(chunk.replace(/=/g, ":")); + Object.keys(thing).forEach(function(playerName){ + Object.keys(thing[playerName]).forEach(function(itemName){ + needle.post(config.masterIP + ":" + config.masterPort + '/api/place', { + name: itemName, + count: thing[playerName][itemName] + }, function (err, resp, body) { + // console.log(body); + }); + }); + }); } }); process.stdin.on('end', () => { @@ -71,15 +39,6 @@ process.stdin.on('end', () => { // poll inventories setInterval(function(){ - // TODO: Make it work for players other than myself, don't error when inventory does not exist, check logistic request slots - // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); - // console.log("Running inventory LUA") - //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') - // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') - console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') - /* - needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { - // success? - }); - */ + console.log("Polling inventories") + console.log(functions.pollInventories(pluginConfig.scriptOutputFileSubscription)); }, 10000); diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js new file mode 100644 index 00000000..d1bebc22 --- /dev/null +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -0,0 +1,134 @@ +const objectOps = require("../../lib/objectOps.js"); +const needle = require("needle"); + +// returns JS object, throws if there are syntax errors in input string +function parseJsString(string){ + // eval because my lua is writing a JS object instead of traditional JSON + // this was done because I hate escaping strings and there are more quotes + // in JSON than JS objects. + + // since eval is unsafe, maybe use https://www.npmjs.com/package/eval-sanitizer + if(string.includes("require") || string.includes(";") || string.includes("eval")){ + throw "parseJsString might have gotten something that could be a xss attempt"; + } + let inventory = ""; + eval("inventory = " + string); + console.log(string) + console.log(inventory) + return inventory; + /* Example output: + {'1':{ + inventory: { + 'raw-wood': 4 + }, + requestSlots:{ + 'underground-belt': 50, + 'fast-transport-belt': 50, + 'express-underground-belt': 50 + }}} + */ +} +function handleInventory(inventory, config){ + // we want to seperate players, parse their inventory and compare to request slots + // then ask master for the difference + // we must then prepare it all and inject it into players inventories with lua before + // the next inventory report arrives to avoid duplicating orders + if(inventory && typeof inventory != "string" && typeof inventory == "object"){ + let exports = {}; + let players = Object.keys(inventory); + for(let i = 0; i < players.length; i++) { + let pinv = inventory[players[i]]; + // create a copy of player.requestSlots + let toRequest = objectOps.deepclone(pinv.requestSlots); + // create a place to store the object we have confirmed for importing + let confirmedItems = {}; + let itemsToConfirm = Object.keys(pinv.inventory).length; + + // subtract items already in inventory + Object.keys(pinv.inventory).forEach(function(key){ + let itemCount = pinv.inventory[key]; + if(toRequest[key]){ + toRequest[key] -= itemCount; + } + if(toRequest[key] > 0){ + let request = { + name:key, + count:toRequest[key], + } + // request the difference from master + needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { + if (response && response.body && typeof response.body == "object") { + // confirmed orders, already removed from master inventory + //response.body.name: response.body.count + let stack = {name:response.body.name,count:response.body.count} + collectStacks(stack); + } else { + collectStacks(false); + } + }); + } + }); + function collectStacks(stack){ + itemsToConfirm--; + if(stack){ + if(confirmedItems[stack.name]){ + confirmedItems[stack.name] += stack.count; + } else { + confirmedItems[stack.name] = stack.count; + } + } + if(itemsToConfirm <= 0){ + // make lua table + // we gotta construct a string because I can't find any nice library for this + let itemTable = "{"; + Object.keys(confirmedItems).forEach(function(name){ + let count = confirmedItems[name]; + itemTable += '["'+name+'"]='+count+','; + }); + itemTable += "}"; + + console.log('/c local a="'+players[i]+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then for d,e in pairs(b)do local f=game.players[a].insert{name=d,count=e}if not c[a]then c[a]={}end;if c[a][d]then c[a][d]=c[a][d]+e-f else c[a][d]=e-f end end;game.write_file("'+outputFile+'",serpent.line(c,{["comment"]=false,["compact"]=true}))end'); + } + } + + // handle exports (why would they be there? I don't know!) + if(pinv.exports){ + Object.keys(pinv.exports).forEach(function(name){ + if(exports[name]){ + exports[name] += pinv.exports[name]; + } else { + exports[name] = pinv.exports[name]; + } + }); + } + } + + // keep handling exports here, now we send em off + Object.keys(exports).forEach(function(name){ + needle.post(config.masterIP + ":" + config.masterPort + '/api/place', { + name: name, + count: exports[name] + }, function (err, resp, body) { + // console.log(body); + }); + }); + } else return false; // inventory is falsey or a string +} +function pollInventories(outputFile){ + // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); + // console.log("Running inventory LUA") + //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') + // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') + return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)' + /* + needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { + // success? + }); + */ +} + +module.exports = { + handleInventory: handleInventory, + parseJsString: parseJsString, + pollInventories: pollInventories, +} \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js new file mode 100644 index 00000000..c005fe41 --- /dev/null +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -0,0 +1,61 @@ +const assert = require("assert"); +const child_process = require("child_process"); +const functions = require("./interfacingFunctions"); +const luamin = require("luamin"); + +describe("inventoryImports/interfacingFunctions.js", function(){ + describe("handleInventory(inventory)", function(){ + it("does things to an object", function(){ + let inventory = {'1':{inventory: {"iron-plate":63,'raw-wood': 4},requestSlots:{'raw-wood': 50,'fast-transport-belt': 50,'express-underground-belt': 50}}}; + assert.throws(function(){ + functions.handleInventory(inventory); + }, function(err){ + // convert error to string + let error = ""+err + // expected error is here + /* istanbul ignore else */ + if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true + }, "threw unexpected error"); + }); + it("returns false if given invalid input", function(){ + assert(functions.handleInventory("this is a string") === false, "when given strings it should return false and do nothing"); + }); + }); + describe("pollInventories()", function(){ + it("Returns a big factorio command as string", function(){ + let x = functions.pollInventories("output.txt"); + assert(typeof x == "string", "pollInventories() should always return a string"); + assert(x.includes("/silent-command "), "This is not a command factorio will accept, please prepend /c"); + assert(x.length > 20, "LUA string seems to short, this ain't good"); + }); + it("Returns valid LUA", function(){ + let x = functions.pollInventories("output.txt"); + x = x.replace("/silent-command ","").replace("/c ",""); + let y = false; + assert.doesNotThrow(function(){ + y = luamin.minify(x); + }, "Invalid LUA supplied"); + assert(typeof y == "string", "luamin does not return string???"); + assert(y.length > 20, "Minified LUA too short, something is up"); + }); + }); + describe("parseJsString(string) => object", function(){ + it("converts a string with JS object notation to object", function(){ + let x = functions.parseJsString("{hello:'thisAString!', yes:function(){return 'JS string!'}, not:'json'}"); + assert(!!x == true, "Should under no circumstance return a falsey value, even !!{} is truthy"); + assert(typeof x == "object", "parseJsString should return an object in this case"); + assert(typeof x.yes == "function", "parseJsString is not JSON, so it should be able to contain functions"); + }); + it("*Tries* to avoid XSS attempts by throwing on certain symbols", function(){ + assert.throws(function(){ + let x = functions.parseJsString("{};throw 'fail'"); + }, function(err) { + let error = err+"" + /* istanbul ignore else */ + if(error.includes('parseJsString might have gotten something that could be a xss attempt')){ + return true; + } + }, "should throw when ; are passed because those are the simplest form of xss"); + }); + }) +}); \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index 30de641c..d12bee6b 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -55,19 +55,6 @@ o = o.."}" game.print(o) game.write_file("test.txt", o) --- insert items in players inventory and export leftovers -/c -local name = "Danielv123" -local itemName = "iron-ore" -local itemCount = 100 -if game.players[name] do - local items = {name=itemName, count=itemCount} - if game.players[name].can_insert(items) do - local insertedItems = game.players[name].insert(items) - local restItems = itemCount - insertedItems - end -end - --[[ Minification procedure: @@ -75,14 +62,51 @@ Minification procedure: Run through https://mothereff.in/lua-minifier Replace ' with '+"'"+' Prepend /silent-command +remove game.print(o) +replace "test.txt" with "'+outputFile+'" enclose in '' ]] --- minified strings +-- minified strings (newest on the top) -- offline playes local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.print(a) -- multiple players local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.print(a) local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.print(e) -local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local quickbar = game.players["Danielv123"].get_quickbar().get_contents()local requests = {}for i = 1, game.player.force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end for k,v in pairs(quickbar) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end o = o.."},requestSlots:{" for k,v in pairs(requests) do o = o.."['+"'"+'"..v["name"].."'+"'"+']:"..v["count"]..", " end o = o.."}}" game.write_file("test.txt", o) \ No newline at end of file +local inventory = game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local quickbar = game.players["Danielv123"].get_quickbar().get_contents()local requests = {}for i = 1, game.player.force.character_logistic_slot_count do requests[i] = game.players["Danielv123"].character.get_request_slot(i) end local o = "{inventory:{" for k,v in pairs(inventory) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end for k,v in pairs(quickbar) do o = o.."['+"'"+'"..k.."'+"'"+']:"..v.."," end o = o.."},requestSlots:{" for k,v in pairs(requests) do o = o.."['+"'"+'"..v["name"].."'+"'"+']:"..v["count"]..", " end o = o.."}}" game.write_file("test.txt", o) + + +-- insert items in players inventory and export leftovers +-- name should be players[i] + +/c +local name = "Danielv123" +local items = {["iron-ore"]=30, ["steel-plate"]=120} +local items_failed = {} +if(game.players[name] and game.players[name].connected) then + for item,count in pairs(items) do + local i = game.players[name].insert{name=item,count=count} + if not items_failed[name] then + items_failed[name] = {} + end + if items_failed[name][item] then + items_failed[name][item] = items_failed[name][item] + count - i + else + items_failed[name][item] = count - i + end + end + game.write_file("t.txt", serpent.line(items_failed, {["comment"]=false, ["compact"]=true})) +end + +--[[ +Minification procedure: + +Run through https://mothereff.in/lua-minifier +Prepend /silent-command +replace "t.txt" with "'+outputFile+'" +enclose in '' +]] + +local a="Danielv123"local b={["iron-ore"]=30,["steel-plate"]=120}local c={}if game.players[a]and game.players[a].connected then for d,e in pairs(b)do local f=game.players[a].insert{name=d,count=e}if not c[a]then c[a]={}end;if c[a][d]then c[a][d]=c[a][d]+e-f else c[a][d]=e-f end end;game.write_file("'+outputFile+'",serpent.line(c,{["comment"]=false,["compact"]=true}))end + From c24cb51f133495c7f4a8f178983ecce88f154b1d Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Fri, 9 Jun 2017 06:58:06 +0200 Subject: [PATCH 13/21] . --- sharedPlugins/inventoryImports/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index 94a98101..a06e6fde 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -10,7 +10,10 @@ var config = {}; process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { var chunk = process.stdin.read(); - if (chunk !== null && isNaN(chunk) && config && !isJson(chunk)&& !chunk.includes("[")){ + console.log(chunk) + if (chunk !== null && isNaN(chunk) && config && !isJson(chunk)){ + // example chunk Stdout: {1:{inventory:{['iron-ore']:100,['steel-plate']:5800,['iron-ore']:50,}, + // requestSlots:{['underground-belt']:50, ['fast-transport-belt']:50, ['express-underground-belt']:50, ['underground-belt']:50, }},} // returns JS object, throws if there are syntax errors in input string let inventory = functions.parseJsString(chunk); console.log(inventory); From f9a2e8519b98f47c64859320066f6dc9a3a39267 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 14 Jun 2017 15:08:34 +0200 Subject: [PATCH 14/21] Hide steam from production graph --- static/slaveDisplay.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/static/slaveDisplay.js b/static/slaveDisplay.js index 959491b5..c901e1d2 100644 --- a/static/slaveDisplay.js +++ b/static/slaveDisplay.js @@ -42,6 +42,10 @@ setTimeout(function() { xmlhttp.send(); }, 0) +let chartIgnoreList = [ + "water", + "steam" +] // ID of slave, ID of canvasjs div without # function makeGraph(slaveID, selector) { @@ -56,7 +60,7 @@ function makeGraph(slaveID, selector) { } let chartData = []; for(let o = 0; o < itemNames.length; o++) { - if(itemNames[o] != "water"){ + if(!chartIgnoreList.includes(itemNames[o])){ chartData[chartData.length] = generateLineChartArray(data, itemNames[o]); } } From c4ead8c699e860cdf1ea9b87bfc3a68d10322878 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 14 Jun 2017 21:46:30 +0200 Subject: [PATCH 15/21] Working inventory imports resolves #50 --- sharedPlugins/inventoryImports/index.js | 17 +- .../inventoryImports/interfacingFunctions.js | 164 ++++++++++-------- .../inventoryImports/lua commands.lua | 96 ++++++++-- 3 files changed, 181 insertions(+), 96 deletions(-) diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index a06e6fde..e2021066 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -4,25 +4,26 @@ const pluginConfig = require("./config.js"); const functions = require("./interfacingFunctions"); -console.log("/silent-command game.print('InventoryImports enabled')\n"); +console.log("/silent-command game.print('InventoryImports enabled')"); var config = {}; process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { var chunk = process.stdin.read(); - console.log(chunk) - if (chunk !== null && isNaN(chunk) && config && !isJson(chunk)){ + // console.log(chunk) + if (chunk !== null && isNaN(chunk) && config && isJson(chunk) && !JSON.parse(chunk).factorioPort){ // example chunk Stdout: {1:{inventory:{['iron-ore']:100,['steel-plate']:5800,['iron-ore']:50,}, // requestSlots:{['underground-belt']:50, ['fast-transport-belt']:50, ['express-underground-belt']:50, ['underground-belt']:50, }},} // returns JS object, throws if there are syntax errors in input string - let inventory = functions.parseJsString(chunk); - console.log(inventory); + // let inventory = functions.parseJsString(chunk); + let inventory = JSON.parse(chunk) + // console.log(inventory); // process inventory of players and ask master for more stuff functions.handleInventory(inventory, config); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ config = JSON.parse(chunk); // if its a LUA table message returning with the exports send em off to master - } else if(!isJson(chunk) && !!chunk && config){ + }/* else if(!isJson(chunk) && !!chunk && config){ let thing = functions.parseJsString(chunk.replace(/=/g, ":")); Object.keys(thing).forEach(function(playerName){ Object.keys(thing[playerName]).forEach(function(itemName){ @@ -34,7 +35,7 @@ process.stdin.on('readable', () => { }); }); }); - } + }*/ }); process.stdin.on('end', () => { process.stdout.write('end'); @@ -42,6 +43,6 @@ process.stdin.on('end', () => { // poll inventories setInterval(function(){ - console.log("Polling inventories") + // console.log("Polling inventories\n") console.log(functions.pollInventories(pluginConfig.scriptOutputFileSubscription)); }, 10000); diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index d1bebc22..04e0394a 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -1,5 +1,6 @@ const objectOps = require("../../lib/objectOps.js"); const needle = require("needle"); +const pluginConfig = require("./config") // returns JS object, throws if there are syntax errors in input string function parseJsString(string){ @@ -28,90 +29,104 @@ function parseJsString(string){ }}} */ } -function handleInventory(inventory, config){ - // we want to seperate players, parse their inventory and compare to request slots - // then ask master for the difference - // we must then prepare it all and inject it into players inventories with lua before - // the next inventory report arrives to avoid duplicating orders - if(inventory && typeof inventory != "string" && typeof inventory == "object"){ +function handleInventory(json, config){ + /* + json is one of these two samples + {"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} + {"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} + + we want to seperate players, parse their json and compare to request slots + then ask master for the difference + we must then prepare it all and inject it into players inventories with lua before + the next json report arrives to avoid duplicating orders + */ + if(json && typeof json != "string" && typeof json == "object"){ let exports = {}; - let players = Object.keys(inventory); - for(let i = 0; i < players.length; i++) { - let pinv = inventory[players[i]]; - // create a copy of player.requestSlots - let toRequest = objectOps.deepclone(pinv.requestSlots); - // create a place to store the object we have confirmed for importing - let confirmedItems = {}; - let itemsToConfirm = Object.keys(pinv.inventory).length; - - // subtract items already in inventory - Object.keys(pinv.inventory).forEach(function(key){ - let itemCount = pinv.inventory[key]; - if(toRequest[key]){ - toRequest[key] -= itemCount; - } - if(toRequest[key] > 0){ - let request = { - name:key, - count:toRequest[key], - } - // request the difference from master - needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { - if (response && response.body && typeof response.body == "object") { - // confirmed orders, already removed from master inventory - //response.body.name: response.body.count - let stack = {name:response.body.name,count:response.body.count} - collectStacks(stack); + + // this is an inventory report, check whats missing and request from master + // {"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} + if(json.players){ + let playerNames = Object.keys(json.players); + for(let i = 0; i < playerNames.length; i++) { + // haha, this turned into bit of a mess didn't it? + let playerInv = json.players[playerNames[i]]; + // create a copy of player.requestSlots + let toRequest = objectOps.deepclone(playerInv.requestSlots); + let inventory = playerInv.inventory; + let itemsToConfirm = Object.keys(toRequest).length; + // create a place to store the object we have confirmed for importing + let confirmedItems = {}; + let collectStacks = function(stack){ + itemsToConfirm--; + if(stack){ + if(confirmedItems[stack.name]){ + confirmedItems[stack.name] += stack.count; } else { - collectStacks(false); + confirmedItems[stack.name] = stack.count; } - }); - } - }); - function collectStacks(stack){ - itemsToConfirm--; - if(stack){ - if(confirmedItems[stack.name]){ - confirmedItems[stack.name] += stack.count; - } else { - confirmedItems[stack.name] = stack.count; + } + if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ + // make lua table + // we gotta construct a string because I can't find any nice library for this + let itemTable = "{"; + Object.keys(confirmedItems).forEach(function(name){ + let count = confirmedItems[name]; + itemTable += '["'+name+'"]='+count+','; + }); + itemTable += "}"; + let outputFile = pluginConfig.scriptOutputFileSubscription; + // a="'+players[i]+'" + // b='+itemTable+' + console.log('/silent-command local a="'+playerNames[i]+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end'); } } - if(itemsToConfirm <= 0){ - // make lua table - // we gotta construct a string because I can't find any nice library for this - let itemTable = "{"; - Object.keys(confirmedItems).forEach(function(name){ - let count = confirmedItems[name]; - itemTable += '["'+name+'"]='+count+','; - }); - itemTable += "}"; - - console.log('/c local a="'+players[i]+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then for d,e in pairs(b)do local f=game.players[a].insert{name=d,count=e}if not c[a]then c[a]={}end;if c[a][d]then c[a][d]=c[a][d]+e-f else c[a][d]=e-f end end;game.write_file("'+outputFile+'",serpent.line(c,{["comment"]=false,["compact"]=true}))end'); - } - } - - // handle exports (why would they be there? I don't know!) - if(pinv.exports){ - Object.keys(pinv.exports).forEach(function(name){ - if(exports[name]){ - exports[name] += pinv.exports[name]; + // subtract items already in inventory + Object.keys(playerInv.requestSlots).forEach(function(key){ + // if this item in inventory is also in a request slot, subtract invCount from requestCount + if(inventory[key]){ + toRequest[key] -= inventory[key]; + } + // if the difference is more than 0, request item from master + if(toRequest[key] > 0){ + let request = { + name:key, + count:toRequest[key], + } + //console.log("Requesting: "+JSON.stringify(request)); + // request the difference from master + needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { + if (response && response.body && typeof response.body == "object") { + // confirmed orders, already removed from master inventory + //response.body.name: response.body.count + let stack = {name:response.body.name,count:Number(response.body.count)} + collectStacks(stack); + } else { + collectStacks(false); + } + }); } else { - exports[name] = pinv.exports[name]; + collectStacks(false); } }); } } - - // keep handling exports here, now we send em off - Object.keys(exports).forEach(function(name){ - needle.post(config.masterIP + ":" + config.masterPort + '/api/place', { - name: name, - count: exports[name] - }, function (err, resp, body) { - // console.log(body); + // {"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} + // this is a report of stuff we didn't have space for, send back to master + if(json.exports){ + // loop over players (normally only one, but whatever) + Object.keys(json.exports).forEach(function(playerName){ + // loop over simpleItemStacks to export + let stackArray = json.exports[playerName]; + for(let i = 0; i < stackArray.length; i++){ + if(stackArray[i].count > 0){ + console.log("Returning overflow: " + JSON.stringify(stackArray[i])); + needle.post(config.masterIP + ":" + config.masterPort + '/api/place', stackArray[i], function (err, resp, body) { + // console.log(body); + }); + } + } }); - }); + } } else return false; // inventory is falsey or a string } function pollInventories(outputFile){ @@ -119,7 +134,8 @@ function pollInventories(outputFile){ // console.log("Running inventory LUA") //console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}for d=1,game.players["Danielv123"].force.character_logistic_slot_count do c[d]=game.players["Danielv123"].character.get_request_slot(d)end;local e="{inventory:{"for f,g in pairs(a)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;for f,g in pairs(b)do e=e.."['+"'"+'"..f.."'+"'"+']:"..g..","end;e=e.."},requestSlots:{"for f,g in pairs(c)do e=e.."['+"'"+'"..g["name"].."'+"'"+']:"..g["count"]..", "end;e=e.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",e)') // console.log("/silent-command "+'local a="{"for b,c in pairs(game.players)do local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end;a=a.."}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",a)') - return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)' + //return "/silent-command "+'local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.write_file("'+outputFile+'",a)' + return '/silent-command '+'local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(e)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;for j,k in pairs(f)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a)' /* needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {slaveID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { // success? diff --git a/sharedPlugins/inventoryImports/lua commands.lua b/sharedPlugins/inventoryImports/lua commands.lua index d12bee6b..d3668a33 100644 --- a/sharedPlugins/inventoryImports/lua commands.lua +++ b/sharedPlugins/inventoryImports/lua commands.lua @@ -22,11 +22,18 @@ for k,v in pairs(requests) do end game.print(o) --- sum up our inventory and request slots +-- sum up our inventory and request slots and print it as valid JSON +-- turns out the valid part is important /c -local o = "{" +local o = '{"players":{' +local notFirst = false for a,b in pairs(game.players) do if b.connected then + if notFirst then + o = o .. ',' + else + notFirst = true + end local inventory = game.players[a].get_inventory(defines.inventory.player_main).get_contents() local quickbar = game.players[a].get_quickbar().get_contents() --[[ Get contents of requester slots into a table ]] @@ -35,27 +42,46 @@ for a,b in pairs(game.players) do requests[i] = game.players[a].character.get_request_slot(i) end - o = o..a..":{inventory:{" - --[[ generate JS table (not JSON) ]] + o = o..'"'..a..'":{"inventory":{' + --[[ generate JSON table ]] + local addComma = false for k,v in pairs(inventory) do - o = o.."['"..k.."']:"..v.."," + if addComma then + o = o..',' + else + addComma = true + end + o = o..'"'..k..'":'..v end for k,v in pairs(quickbar) do - o = o.."['"..k.."']:"..v.."," + if addComma then + o = o..',' + else + addComma = true + end + o = o..'"'..k..'":'..v end - o = o.."},requestSlots:{" + o = o..'},"requestSlots":{' + addComma = false for k,v in pairs(requests) do - o = o.."['"..v["name"].."']:"..v["count"]..", " + if addComma then + o = o..',' + else + addComma = true + end + o = o..'"'..v['name']..'":'..v['count'] end - o = o.."}}," + o = o..'}}' --[[ We need a comma after these two if there is more than 1 player, handled up top by notFirst]] end end -o = o.."}" +o = o.."}}" +game.write_file("t.txt", o) game.print(o) -game.write_file("test.txt", o) --[[ +Example output +{"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} Minification procedure: @@ -63,12 +89,13 @@ Run through https://mothereff.in/lua-minifier Replace ' with '+"'"+' Prepend /silent-command remove game.print(o) -replace "test.txt" with "'+outputFile+'" +replace "t.txt" with "'+outputFile+'" enclose in '' ]] -- minified strings (newest on the top) +local a='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected then if b then a=a..'+"'"+','+"'"+'else b=true end;local e=game.players[c].get_inventory(defines.inventory.player_main).get_contents()local f=game.players[c].get_quickbar().get_contents()local g={}for h=1,game.players[c].force.character_logistic_slot_count do g[h]=game.players[c].character.get_request_slot(h)end;a=a..'+"'"+'"'+"'"+'..c..'+"'"+'":{"inventory":{'+"'"+'local i=false;for j,k in pairs(e)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;for j,k in pairs(f)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..j..'+"'"+'":'+"'"+'..k end;a=a..'+"'"+'},"requestSlots":{'+"'"+'i=false;for j,k in pairs(g)do if i then a=a..'+"'"+','+"'"+'else i=true end;a=a..'+"'"+'"'+"'"+'..k['+"'"+'name'+"'"+']..'+"'"+'":'+"'"+'..k['+"'"+'count'+"'"+']end;a=a..'+"'"+'}}'+"'"+'end end;a=a.."}}"game.write_file("'+outputFile+'",a) -- offline playes local a="{"for b,c in pairs(game.players)do if c.connected then local d=game.players[b].get_inventory(defines.inventory.player_main).get_contents()local e=game.players[b].get_quickbar().get_contents()local f={}for g=1,game.players[b].force.character_logistic_slot_count do f[g]=game.players[b].character.get_request_slot(g)end;a=a..b..":{inventory:{"for h,i in pairs(d)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;for h,i in pairs(e)do a=a.."['+"'"+'"..h.."'+"'"+']:"..i..","end;a=a.."},requestSlots:{"for h,i in pairs(f)do a=a.."['+"'"+'"..i["name"].."'+"'"+']:"..i["count"]..", "end;a=a.."}},"end end;a=a.."}"game.print(a) -- multiple players @@ -81,7 +108,7 @@ local inventory = game.players["Danielv123"].get_inventory(defines.inventory.pla -- name should be players[i] /c -local name = "Danielv123" +local name = "1" local items = {["iron-ore"]=30, ["steel-plate"]=120} local items_failed = {} if(game.players[name] and game.players[name].connected) then @@ -96,17 +123,58 @@ if(game.players[name] and game.players[name].connected) then items_failed[name][item] = count - i end end - game.write_file("t.txt", serpent.line(items_failed, {["comment"]=false, ["compact"]=true})) + game.write_file("t.txt", serpent.line({["exports"]=items_failed}, {["comment"]=false, ["compact"]=true})) +end + +--[[ +Writes something like + +{exports={["1"]={["iron-ore"]=0,["steel-plate"]=0}}} + +converting to JS would be +replace "[" and "]" with ""??? +replace "=" with ":" +]] + +--[[ +JSON edition + +{"exports":{"1":[{"name":"iron-ore","count":0},{"name":"steel-plate","count":0}]}} + +Should always return valid JSON +]] +/c +local name = "1" +local items = {["iron-ore"]=30, ["steel-plate"]=120} +if(game.players[name] and game.players[name].connected) then + local o = '{"exports":{"'..name..'":[' + local addComma = false + for item,count in pairs(items) do + local i = game.players[name].insert{name=item,count=count} + if addComma then + o = o..',' + else + addComma = true + end + o = o .. '{"name":"'..item..'","count":'..count - i..'}' + end + o = o .. ']}}' + game.write_file("t.txt", o) end --[[ Minification procedure: Run through https://mothereff.in/lua-minifier +Replace ' with '+"'"+' Prepend /silent-command replace "t.txt" with "'+outputFile+'" enclose in '' ]] + +-- JSON outputFile +local a="1"local b={["iron-ore"]=30,["steel-plate"]=120}local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end +-- output to serpented LUA table local a="Danielv123"local b={["iron-ore"]=30,["steel-plate"]=120}local c={}if game.players[a]and game.players[a].connected then for d,e in pairs(b)do local f=game.players[a].insert{name=d,count=e}if not c[a]then c[a]={}end;if c[a][d]then c[a][d]=c[a][d]+e-f else c[a][d]=e-f end end;game.write_file("'+outputFile+'",serpent.line(c,{["comment"]=false,["compact"]=true}))end From fdf8a4b4b1823d0edd9888a53375b295e6593edd Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Wed, 14 Jun 2017 21:52:38 +0200 Subject: [PATCH 16/21] Housekeeping --- sharedPlugins/inventoryImports/index.js | 6 +----- sharedPlugins/inventoryImports/interfacingFunctions.js | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index e2021066..32c88bc3 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -12,12 +12,8 @@ process.stdin.on('readable', () => { var chunk = process.stdin.read(); // console.log(chunk) if (chunk !== null && isNaN(chunk) && config && isJson(chunk) && !JSON.parse(chunk).factorioPort){ - // example chunk Stdout: {1:{inventory:{['iron-ore']:100,['steel-plate']:5800,['iron-ore']:50,}, - // requestSlots:{['underground-belt']:50, ['fast-transport-belt']:50, ['express-underground-belt']:50, ['underground-belt']:50, }},} - // returns JS object, throws if there are syntax errors in input string - // let inventory = functions.parseJsString(chunk); + let inventory = JSON.parse(chunk) - // console.log(inventory); // process inventory of players and ask master for more stuff functions.handleInventory(inventory, config); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index 04e0394a..c3d73e62 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -14,8 +14,8 @@ function parseJsString(string){ } let inventory = ""; eval("inventory = " + string); - console.log(string) - console.log(inventory) + /*console.log(string) + console.log(inventory)*/ return inventory; /* Example output: {'1':{ @@ -119,7 +119,7 @@ function handleInventory(json, config){ let stackArray = json.exports[playerName]; for(let i = 0; i < stackArray.length; i++){ if(stackArray[i].count > 0){ - console.log("Returning overflow: " + JSON.stringify(stackArray[i])); + // console.log("Returning overflow: " + JSON.stringify(stackArray[i])); needle.post(config.masterIP + ":" + config.masterPort + '/api/place', stackArray[i], function (err, resp, body) { // console.log(body); }); From 8ee3109fbc4f491f07d7287c026a271b3cac71b5 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Thu, 15 Jun 2017 17:14:01 +0200 Subject: [PATCH 17/21] NPM5.0.1, housekeeping --- package-lock.json | 1573 +++++++++++++++++ package.json | 4 +- sharedPlugins/inventoryImports/index.js | 15 +- .../inventoryImports/interfacingFunctions.js | 11 - sharedPlugins/inventoryImports/isJson.spec.js | 2 +- 5 files changed, 1578 insertions(+), 27 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..6d3a24e0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1573 @@ +{ + "name": "clusterio", + "version": "1.0.0", + "lockfileVersion": 1, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=" + }, + "acorn": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz", + "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=" + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", + "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "assertion-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "dev": true + }, + "ast-types": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.15.tgz", + "integrity": "sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base62": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/base62/-/base62-0.1.1.tgz", + "integrity": "sha1-e0F0wvlESXU7EcJlHAg9qEGnsIQ=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true + }, + "binary-search-tree": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", + "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", + "dependencies": { + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + } + } + }, + "body-parser": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz", + "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=" + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=" + }, + "bytes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "optional": true + }, + "canvasjs": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/canvasjs/-/canvasjs-1.8.1.tgz", + "integrity": "sha1-aYpOAC4zbOg+8vkQGmvSdrp2V6M=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "optional": true + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true + }, + "child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "optional": true, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "optional": true + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=" + }, + "command-exists": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.2.tgz", + "integrity": "sha1-EoGcZPr5VEbsCuB/5sr7brNwiyI=" + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz", + "integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==" + }, + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + }, + "string_decoder": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", + "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=" + } + } + }, + "connect-busboy": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/connect-busboy/-/connect-busboy-0.0.2.tgz", + "integrity": "sha1-rFyclmchcYheV2xmsr/ZXTuxEJc=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "optional": true + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.4.3.tgz", + "integrity": "sha512-UrRUpZKUXhj8OJU3OOcmfRuZB8h2dFy63xPUpCVTw/CvCukhykRYmb3XoXzkaGQaPOLmWSPDlqlx/oNHyw11Rw==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=" + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "eachr": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz", + "integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true + }, + "editions": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.3.tgz", + "integrity": "sha1-CQcQG92iD6w8vjNMJ8vQaI3Jmls=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "ejs": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.6.tgz", + "integrity": "sha1-R5Y2v6P+Ox3r1SCH8KyyBLTxnIg=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "es3ify": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/es3ify/-/es3ify-0.1.4.tgz", + "integrity": "sha1-rZ+l3xrjTz8x4SEbWBiy1RB439E=", + "dependencies": { + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", + "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" + } + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true + } + } + }, + "esmangle-evaluator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esmangle-evaluator/-/esmangle-evaluator-1.0.1.tgz", + "integrity": "sha1-Yg2GbvSGGzMR91dm1SqFcrs8YzY=" + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=" + }, + "express": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz", + "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=" + }, + "express-fileupload": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-0.0.6.tgz", + "integrity": "sha1-bCtAXtEV4LGgfIKF2UCWLfLfmeA=", + "dependencies": { + "fs-extra": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.22.1.tgz", + "integrity": "sha1-X9b4BJ3JdsoZ6yNV1lgXPKvM4FY=" + } + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extract-opts": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-3.3.1.tgz", + "integrity": "sha1-WrvtyYwNUgLjJ4cn+Rktfghsa+E=" + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + }, + "falafel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-1.2.0.tgz", + "integrity": "sha1-wY0k71CRF0pJfzGM0ksCaiXN2rQ=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "finalhandler": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", + "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=" + }, + "find-remove": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-remove/-/find-remove-1.0.1.tgz", + "integrity": "sha1-cqLeYnzeotSocjvl+7JKv7c/m1I=" + }, + "fmerge": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fmerge/-/fmerge-1.2.0.tgz", + "integrity": "sha1-NumdKuJV4+4a9ma033gFU2cc9pI=" + }, + "follow-redirects": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.3.tgz", + "integrity": "sha1-AauuyoXjYJg32fzaMWen5C/ayiE=" + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=" + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" + }, + "fs": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz", + "integrity": "sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg=" + }, + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "get-port": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.1.0.tgz", + "integrity": "sha1-7wGxioTKZIaXD/meVERhQac//T4=" + }, + "getmac": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/getmac/-/getmac-1.2.1.tgz", + "integrity": "sha1-DQlf0GJ4UAQ+rB3PoLEgu9wUJtE=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==" + }, + "glob-all": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", + "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=" + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "handlebars": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", + "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true + } + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=" + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "hash-files": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/hash-files/-/hash-files-1.1.1.tgz", + "integrity": "sha1-X4nGTvIezmnIJhJUt2Wb1A6R2N4=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=" + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-basic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", + "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "http-errors": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", + "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=" + }, + "http-response-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=" + }, + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inline-process-browser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz", + "integrity": "sha1-RqYbFT3TybFiSxoAYm7bT39BTyI=" + }, + "ipaddr.js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", + "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=" + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true + } + } + }, + "js-yaml": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", + "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "jstransform": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-3.0.0.tgz", + "integrity": "sha1-olkats7o2XvzvoMNv6IxO4fNZAs=", + "dependencies": { + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz", + "integrity": "sha1-t303q8046gt3Qmu4vCkizmtCZBE=" + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "optional": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true + }, + "lie": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.0.2.tgz", + "integrity": "sha1-/9oh17uibzd8rYZdNkmy/Izjn+o=" + }, + "localforage": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.5.0.tgz", + "integrity": "sha1-a5lOGbVmEfqF3zmS3zl6xKtm6BU=" + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "luamin": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/luamin/-/luamin-1.0.4.tgz", + "integrity": "sha1-lEUptY/G+k0x6s4uA1PUEhDw49M=" + }, + "luaparse": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/luaparse/-/luaparse-0.2.1.tgz", + "integrity": "sha1-qo9WEysN6X0388mRqd9C4OF/ZWw=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + }, + "minimist": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", + "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "mocha": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.4.2.tgz", + "integrity": "sha1-0O9NMyEm2/GNDWQMmzgt1IvpdZQ=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.0.tgz", + "integrity": "sha1-vFlryr52F/Edn6FTYe3tVgi4SZs=", + "dev": true + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true + } + } + }, + "mock-fs": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.4.1.tgz", + "integrity": "sha512-C8aapOvl77Bs18WCkejdLuX2kX8DaqaJ7ZmqUmX9U6HD2g31Pd0tZfNBAEVulmJWKyzUIyutrtxiIoNdXLAYsw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" + }, + "nedb": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", + "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=", + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" + } + } + }, + "needle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-1.6.0.tgz", + "integrity": "sha1-9SpYWJchIWGOAC+OY4TK2sItYk8=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "nock": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/nock/-/nock-9.0.13.tgz", + "integrity": "sha1-0Lw570PTF5mB4isujqBp+RbFeBo=", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object-path": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + }, + "opter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/opter/-/opter-1.1.0.tgz", + "integrity": "sha1-JZiuu2Cz8acyKvEJcIb055jISnY=" + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "private": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=" + }, + "propagate": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-0.4.0.tgz", + "integrity": "sha1-8/zKCm/gZzanulcpZgaWF8EwtIE=", + "dev": true + }, + "proxy-addr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", + "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", + "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=" + }, + "read-files": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/read-files/-/read-files-0.1.0.tgz", + "integrity": "sha1-YGu3oBd5Utai+YPVmAlglDFNMh4=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=" + }, + "recast": { + "version": "0.10.43", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.43.tgz", + "integrity": "sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8=", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", + "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + } + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=" + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "optional": true + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=" + }, + "safe-buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==" + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "send": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", + "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=" + }, + "serve-static": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", + "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "simple-rcon": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/simple-rcon/-/simple-rcon-0.3.1.tgz", + "integrity": "sha1-IwPbzDE5yJPscApyeLukP4yxBDs=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=" + }, + "source-map": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", + "integrity": "sha1-n3BNDWnZ4TioG63267T94z0VHGE=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "streamifier": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz", + "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=" + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true + }, + "sync-request": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-4.1.0.tgz", + "integrity": "sha512-iFbOBWYaznBNbheIKaMkj+3EabpEsXbuwcTVuYkRjoav+Om5L8VXXLIXms0cHxkouXMRCQaSfhfau9/HyIbM2Q==" + }, + "then-request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", + "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=" + } + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=" + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=" + }, + "typechecker": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.4.1.tgz", + "integrity": "sha1-+XuV9RsDhBchLWd9RaNz7nvO1+Y=" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "optional": true, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "optional": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "optional": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unreachable-branch-transform": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unreachable-branch-transform/-/unreachable-branch-transform-0.3.0.tgz", + "integrity": "sha1-2ZzExudG0mSSiEW2EdtUsPNHTKo=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=" + }, + "validator": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz", + "integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g=" + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=" + }, + "which": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "yargs": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", + "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=" + }, + "z-schema": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.2.tgz", + "integrity": "sha1-5CIZa17+YLRq3vPD8q7y3qqREWE=" + }, + "z-schema-errors": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/z-schema-errors/-/z-schema-errors-0.0.1.tgz", + "integrity": "sha1-4GJwpMpDklcp8ldkeJyp9Gv/D30=" + } + } +} diff --git a/package.json b/package.json index 38547851..6bf26bdb 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "clusterio", "version": "1.0.0", "description": "Interserver communication for factorio", + "repository": "https://github.com/Danielv123/factorioClusterio", "main": "master.js", "scripts": { "test": "npm run test-unit", @@ -44,6 +45,7 @@ "chai": "^3.5.0", "istanbul": "^0.4.5", "mocha": "^3.4.1", - "mock-fs": "^4.3.0" + "mock-fs": "^4.3.0", + "nock": "^9.0.13" } } diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index 32c88bc3..a00f15c4 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -18,20 +18,7 @@ process.stdin.on('readable', () => { functions.handleInventory(inventory, config); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ config = JSON.parse(chunk); - // if its a LUA table message returning with the exports send em off to master - }/* else if(!isJson(chunk) && !!chunk && config){ - let thing = functions.parseJsString(chunk.replace(/=/g, ":")); - Object.keys(thing).forEach(function(playerName){ - Object.keys(thing[playerName]).forEach(function(itemName){ - needle.post(config.masterIP + ":" + config.masterPort + '/api/place', { - name: itemName, - count: thing[playerName][itemName] - }, function (err, resp, body) { - // console.log(body); - }); - }); - }); - }*/ + } }); process.stdin.on('end', () => { process.stdout.write('end'); diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index c3d73e62..0713735f 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -17,17 +17,6 @@ function parseJsString(string){ /*console.log(string) console.log(inventory)*/ return inventory; - /* Example output: - {'1':{ - inventory: { - 'raw-wood': 4 - }, - requestSlots:{ - 'underground-belt': 50, - 'fast-transport-belt': 50, - 'express-underground-belt': 50 - }}} - */ } function handleInventory(json, config){ /* diff --git a/sharedPlugins/inventoryImports/isJson.spec.js b/sharedPlugins/inventoryImports/isJson.spec.js index ece8c324..db0f2657 100644 --- a/sharedPlugins/inventoryImports/isJson.spec.js +++ b/sharedPlugins/inventoryImports/isJson.spec.js @@ -1,7 +1,7 @@ const isJson = require("./isJson"); const assert = require("assert"); -describe("isJson.js()", function(){ +describe("inventoryImports/isJson.js()", function(){ it("checks if a thing is valid JSON", function(){ assert(isJson("string") === false); assert(isJson("{js_object_notation='very false'}") === false); From 2a1badf7cb762d0dee6f73c6f305e3647c8671b3 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Thu, 15 Jun 2017 17:14:29 +0200 Subject: [PATCH 18/21] Lots of tests --- .../inventoryImports/interfacingFunctions.js | 37 +++++++---- .../interfacingFunctions.spec.js | 64 +++++++++++++++++-- .../inventoryImports/isFactorioCommand.js | 23 +++++++ .../isFactorioCommand.spec.js | 18 ++++++ 4 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 sharedPlugins/inventoryImports/isFactorioCommand.js create mode 100644 sharedPlugins/inventoryImports/isFactorioCommand.spec.js diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index 0713735f..0e24a6f9 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -48,25 +48,14 @@ function handleInventory(json, config){ let collectStacks = function(stack){ itemsToConfirm--; if(stack){ - if(confirmedItems[stack.name]){ + if(confirmedItems[stack.name] && confirmedItems[stack.name] > 0){ confirmedItems[stack.name] += stack.count; } else { confirmedItems[stack.name] = stack.count; } } if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ - // make lua table - // we gotta construct a string because I can't find any nice library for this - let itemTable = "{"; - Object.keys(confirmedItems).forEach(function(name){ - let count = confirmedItems[name]; - itemTable += '["'+name+'"]='+count+','; - }); - itemTable += "}"; - let outputFile = pluginConfig.scriptOutputFileSubscription; - // a="'+players[i]+'" - // b='+itemTable+' - console.log('/silent-command local a="'+playerNames[i]+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end'); + console.log(insertItemsFromObject(confirmedItems, playerNames[i])); } } // subtract items already in inventory @@ -118,6 +107,27 @@ function handleInventory(json, config){ } } else return false; // inventory is falsey or a string } +function insertItemsFromObject(confirmedItems, playerName){ + // expeced input + // {["iron-plate"]:100, coal:20} + // make lua table + // we gotta construct a string because I can't find any nice library for this + if(playerName && confirmedItems){ + if(typeof playerName != "string") { + throw "playerName is: '"+typeof playerName+"' instead of string!"; + } + let itemTable = "{"; + Object.keys(confirmedItems).forEach(function(name){ + let count = confirmedItems[name]; + itemTable += '["'+name+'"]='+count+','; + }); + itemTable += "}"; + let outputFile = pluginConfig.scriptOutputFileSubscription; + // a="'+players[i]+'" + // b='+itemTable+' + return '/silent-command local a="'+playerName+'"local b='+itemTable+'local c={}if game.players[a]and game.players[a].connected then local d='+"'"+'{"exports":{"'+"'"+'..a..'+"'"+'":['+"'"+'local e=false;for f,g in pairs(b)do local h=game.players[a].insert{name=f,count=g}if e then d=d..'+"'"+','+"'"+'else e=true end;d=d..'+"'"+'{"name":"'+"'"+'..f..'+"'"+'","count":'+"'"+'..g-h..'+"'"+'}'+"'"+'end;d=d..'+"'"+']}}'+"'"+'game.write_file("'+outputFile+'",d)end'; + } +} function pollInventories(outputFile){ // console.log("/silent-command "+'local a=game.players["Danielv123"].get_inventory(defines.inventory.player_main).get_contents()local b=game.players["Danielv123"].get_quickbar().get_contents()local c={}local d="{inventory:{"for e,f in pairs(a)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;for e,f in pairs(b)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."},requestSlots:{"for e,f in pairs(c)do d=d.."['+"'"+'"..e.."'+"'"+']:"..f..","end;d=d.."}}"game.write_file("'+pluginConfig.scriptOutputFileSubscription+'",d)'); // console.log("Running inventory LUA") @@ -136,4 +146,5 @@ module.exports = { handleInventory: handleInventory, parseJsString: parseJsString, pollInventories: pollInventories, + insertItemsFromObject: insertItemsFromObject, } \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js index c005fe41..d6f24c2f 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -2,10 +2,11 @@ const assert = require("assert"); const child_process = require("child_process"); const functions = require("./interfacingFunctions"); const luamin = require("luamin"); +const isFactorioCommand = require("./isFactorioCommand") describe("inventoryImports/interfacingFunctions.js", function(){ - describe("handleInventory(inventory)", function(){ - it("does things to an object", function(){ + describe("handleInventory(inventory, config)", function(){ + /*it("does things to an object", function(){ let inventory = {'1':{inventory: {"iron-plate":63,'raw-wood': 4},requestSlots:{'raw-wood': 50,'fast-transport-belt': 50,'express-underground-belt': 50}}}; assert.throws(function(){ functions.handleInventory(inventory); @@ -14,24 +15,42 @@ describe("inventoryImports/interfacingFunctions.js", function(){ let error = ""+err // expected error is here /* istanbul ignore else */ - if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true + /*if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true }, "threw unexpected error"); - }); + });*/ it("returns false if given invalid input", function(){ assert(functions.handleInventory("this is a string") === false, "when given strings it should return false and do nothing"); }); + /*it("Sends post requests", function(){ + + var scope = nock('localhost:8080') + .post('/api/remove', { + username: 'pgte', + email: 'pedro.teixeira@gmail.com' + }) + .reply(201, { + name: "copper-plate", + count: 100 + }); + + + });*/ }); - describe("pollInventories()", function(){ + describe("pollInventories(outputfile)", function(){ it("Returns a big factorio command as string", function(){ let x = functions.pollInventories("output.txt"); assert(typeof x == "string", "pollInventories() should always return a string"); - assert(x.includes("/silent-command "), "This is not a command factorio will accept, please prepend /c"); + assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); assert(x.length > 20, "LUA string seems to short, this ain't good"); }); it("Returns valid LUA", function(){ let x = functions.pollInventories("output.txt"); + + // remove string added to make factorio interpret as command x = x.replace("/silent-command ","").replace("/c ",""); + let y = false; + // luamin throws when it recieves something that is invalid LUA assert.doesNotThrow(function(){ y = luamin.minify(x); }, "Invalid LUA supplied"); @@ -39,6 +58,37 @@ describe("inventoryImports/interfacingFunctions.js", function(){ assert(y.length > 20, "Minified LUA too short, something is up"); }); }); + describe("insertItemsFromObject(object)", function(){ + let testObject = {"copper-plate":100, "iron-ore":1337} + let playerName = "Danielv123" + it("Returns undefined if any of the parameters are missing", function(){ + let x = functions.insertItemsFromObject(); + assert(x === undefined); + + let y = functions.insertItemsFromObject(testObject); + assert(y === undefined); + + let z = functions.insertItemsFromObject(undefined, playerName); + assert(z === undefined); + }); + it("Throws if player name is not a string", function(){ + assert.throws(function(){ + let x = functions.insertItemsFromObject(testObject, testObject); + }, "playerName is: 'object' instead of string!"); + }); + it("Returns a big factorio command as string", function(){ + let x = functions.insertItemsFromObject(testObject, playerName); + + assert(typeof x == "string", "LUA returned should be a string"); + assert(x.length > 20, "LUA strings are quite long"); + assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); + }); + it("The returned string is a valid factorio command", function(){ + let x = functions.insertItemsFromObject(testObject, playerName); + + assert(isFactorioCommand(x) == true); + }); + }); describe("parseJsString(string) => object", function(){ it("converts a string with JS object notation to object", function(){ let x = functions.parseJsString("{hello:'thisAString!', yes:function(){return 'JS string!'}, not:'json'}"); @@ -57,5 +107,5 @@ describe("inventoryImports/interfacingFunctions.js", function(){ } }, "should throw when ; are passed because those are the simplest form of xss"); }); - }) + }); }); \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isFactorioCommand.js b/sharedPlugins/inventoryImports/isFactorioCommand.js new file mode 100644 index 00000000..239bbc59 --- /dev/null +++ b/sharedPlugins/inventoryImports/isFactorioCommand.js @@ -0,0 +1,23 @@ +const luamin = require("luamin"); +function isFactorioCommand(command){ + if(typeof command != "string"){ + return false; + } else if(!command.includes("/silent-command ") && !command.includes("/c ")){ + return false; + } else if(command[0] != "/"){ + return false; + } else { + // check that what follows /c is valid LUA + let x = command.replace("/silent-command ", "").replace("/c ",""); + let y + try{ + y = luamin.minify(x); + } catch (e) { + return false; + } + + // by this point it is surely a LUA command + return true; + } +} +module.exports = isFactorioCommand; \ No newline at end of file diff --git a/sharedPlugins/inventoryImports/isFactorioCommand.spec.js b/sharedPlugins/inventoryImports/isFactorioCommand.spec.js new file mode 100644 index 00000000..bc3ac232 --- /dev/null +++ b/sharedPlugins/inventoryImports/isFactorioCommand.spec.js @@ -0,0 +1,18 @@ +const isFactorioCommand = require("./isFactorioCommand"); +const assert = require("assert"); + +describe("inventoryImports/isFactorioCommand.js()", function(){ + it("Returns false if input is not a factorio command", function(){ + assert(isFactorioCommand({}) == false, "This is not even a string so should return false"); + assert(isFactorioCommand("Howdy boys!") == false, "This is not a command so should return false"); + assert(isFactorioCommand("/c this ain't a command because its invalid LUA") == false, "This is not valid LUA so it should return false"); + assert(isFactorioCommand("Trying to trick the system /c game.print('Hello world!')") == false, "this is still not a command"); + + }); + it("Returns true if it is a valid command", function(){ + assert(isFactorioCommand("/c game.print('hello world')"), "This is a valid command, should output true not false") + }); + it("Fails to understand that leading spaces are valid", function(){ + assert(isFactorioCommand(" /c game.print('Hello world!')") == false, "This should be true but often returns false unintentionally. If you see this you probably fixed the bug so change this test, OK?") + }); +}); \ No newline at end of file From d2f122936ee0a94e0b9326658b1969188787fb19 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 17 Jun 2017 17:24:24 +0200 Subject: [PATCH 19/21] Tests for handleInventory(), 93% avg coverage --- package-lock.json | 56 ++++++++ package.json | 4 +- sharedPlugins/inventoryImports/index.js | 2 +- .../inventoryImports/interfacingFunctions.js | 13 +- .../interfacingFunctions.spec.js | 122 ++++++++++++++---- 5 files changed, 163 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6d3a24e0..dfef3f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -549,6 +549,12 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=" }, + "formatio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "dev": true + }, "forwarded": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", @@ -941,6 +947,12 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true }, + "lolex": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", + "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", + "dev": true + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -1041,6 +1053,12 @@ } } }, + "mocha-sinon": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mocha-sinon/-/mocha-sinon-2.0.0.tgz", + "integrity": "sha1-cjqTEOfXN9e3fHpmghI3QlsDLUg=", + "dev": true + }, "mock-fs": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.4.1.tgz", @@ -1052,6 +1070,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", + "dev": true + }, "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", @@ -1290,6 +1314,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==" }, + "samsam": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz", + "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=", + "dev": true + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -1315,6 +1345,26 @@ "resolved": "https://registry.npmjs.org/simple-rcon/-/simple-rcon-0.3.1.tgz", "integrity": "sha1-IwPbzDE5yJPscApyeLukP4yxBDs=" }, + "sinon": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.4.tgz", + "integrity": "sha1-RmrY0brobW21GqIYuS6Ze8Pl24g=", + "dev": true, + "dependencies": { + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true + }, + "type-detect": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", + "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", + "dev": true + } + } + }, "sntp": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", @@ -1378,6 +1428,12 @@ "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-4.1.0.tgz", "integrity": "sha512-iFbOBWYaznBNbheIKaMkj+3EabpEsXbuwcTVuYkRjoav+Om5L8VXXLIXms0cHxkouXMRCQaSfhfau9/HyIbM2Q==" }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + }, "then-request": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", diff --git a/package.json b/package.json index 6bf26bdb..e4a96cf8 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,9 @@ "chai": "^3.5.0", "istanbul": "^0.4.5", "mocha": "^3.4.1", + "mocha-sinon": "^2.0.0", "mock-fs": "^4.3.0", - "nock": "^9.0.13" + "nock": "^9.0.13", + "sinon": "^2.3.4" } } diff --git a/sharedPlugins/inventoryImports/index.js b/sharedPlugins/inventoryImports/index.js index a00f15c4..b90d568e 100644 --- a/sharedPlugins/inventoryImports/index.js +++ b/sharedPlugins/inventoryImports/index.js @@ -15,7 +15,7 @@ process.stdin.on('readable', () => { let inventory = JSON.parse(chunk) // process inventory of players and ask master for more stuff - functions.handleInventory(inventory, config); + functions.handleInventory(inventory, config, console.log); } else if(isJson(chunk) && JSON.parse(chunk).factorioPort){ config = JSON.parse(chunk); } diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.js b/sharedPlugins/inventoryImports/interfacingFunctions.js index 0e24a6f9..60898b98 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.js @@ -1,6 +1,7 @@ const objectOps = require("../../lib/objectOps.js"); const needle = require("needle"); const pluginConfig = require("./config") +const isJson = require("./isJson") // returns JS object, throws if there are syntax errors in input string function parseJsString(string){ @@ -18,7 +19,7 @@ function parseJsString(string){ console.log(inventory)*/ return inventory; } -function handleInventory(json, config){ +function handleInventory(json, config, callback){ /* json is one of these two samples {"players":{"1":{"inventory":{"stone":12,"iron-ore":120,"raw-wood":10,"iron-plate":8,"steel-plate":480},"requestSlots":{}}}} @@ -48,6 +49,8 @@ function handleInventory(json, config){ let collectStacks = function(stack){ itemsToConfirm--; if(stack){ + // because of the way we collect data in a keyed JSON object, this first part of the IF cannot happen. + /* istanbul ignore next */ if(confirmedItems[stack.name] && confirmedItems[stack.name] > 0){ confirmedItems[stack.name] += stack.count; } else { @@ -55,7 +58,8 @@ function handleInventory(json, config){ } } if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ - console.log(insertItemsFromObject(confirmedItems, playerNames[i])); + // return LUA + callback(insertItemsFromObject(confirmedItems, playerNames[i])); } } // subtract items already in inventory @@ -72,10 +76,11 @@ function handleInventory(json, config){ } //console.log("Requesting: "+JSON.stringify(request)); // request the difference from master - needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, function (err, response, body) { - if (response && response.body && typeof response.body == "object") { + needle.post(config.masterIP + ":" + config.masterPort + '/api/remove', request, {json:true}, function (err, response, body) { + if (!err && response && response.body && (typeof response.body == "object" || isJson(response.body))) { // confirmed orders, already removed from master inventory //response.body.name: response.body.count + if(typeof response.body == "string") response.body = JSON.parse(response.body); let stack = {name:response.body.name,count:Number(response.body.count)} collectStacks(stack); } else { diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js index d6f24c2f..90411831 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -2,45 +2,111 @@ const assert = require("assert"); const child_process = require("child_process"); const functions = require("./interfacingFunctions"); const luamin = require("luamin"); -const isFactorioCommand = require("./isFactorioCommand") +const isFactorioCommand = require("./isFactorioCommand"); +const nock = require("nock"); + +// sinon does weird prototype manipulation and so doen't need to be saved to anything. +require("mocha-sinon"); +const sinon = require("sinon"); describe("inventoryImports/interfacingFunctions.js", function(){ describe("handleInventory(inventory, config)", function(){ - /*it("does things to an object", function(){ - let inventory = {'1':{inventory: {"iron-plate":63,'raw-wood': 4},requestSlots:{'raw-wood': 50,'fast-transport-belt': 50,'express-underground-belt': 50}}}; - assert.throws(function(){ - functions.handleInventory(inventory); - }, function(err){ - // convert error to string - let error = ""+err - // expected error is here - /* istanbul ignore else */ - /*if(error.includes("TypeError: Cannot read property 'masterIP' of undefined")) return true - }, "threw unexpected error"); - });*/ + let config = { + masterIP:"localhost", + masterPort:8080, + } it("returns false if given invalid input", function(){ assert(functions.handleInventory("this is a string") === false, "when given strings it should return false and do nothing"); }); - /*it("Sends post requests", function(){ + it("Sends post requests and outputs factorio commands", function(done){ + let inventory = { + "players":{ + "Danielv123":{ + "inventory":{ + "stone":12, + "iron-ore":120, + "raw-wood":10, + "iron-plate":8, + "steel-plate":480 + }, + "requestSlots":{ + "iron-plate":100, + "steel-plate":480, + "bullshit":0, + } + } + } + } + var scope = nock('http://localhost:8080') + .post('/api/remove', function(body){ + // check that we are requesting the right things, () to convert to boolean + // 92 because 100 requested and 8 already in inventory + if(body.name == "iron-plate") assert(body.count == "92"); + // nock wants us to return true if this is the correct body and we should reply + // if we return false we won't send a reply. + return x + }) + // nock destroys the endpoints after being hit once, this changes the number of hits required for destruction + .times(10) + // respond with JSON object instead of url query parameters + .reply(200,function(uri, req){ + return req + },{ + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }) + // nock logging statement .log((data) => console.log(data)); - var scope = nock('localhost:8080') - .post('/api/remove', { - username: 'pgte', - email: 'pedro.teixeira@gmail.com' - }) - .reply(201, { - name: "copper-plate", - count: 100 - }); - - - });*/ + functions.handleInventory(inventory, config, callback); + function callback(x){ + assert(x.includes(92) && x.includes("iron-plate"), "it should have included the items it was going to import"); + assert(!x.includes("steel-plate"), "Inventory is already filled with steel so shouldn't be importing that"); + assert(isFactorioCommand(x), "it should have returned a valid factorio command"); + assert(x.includes('"Danielv123"'), "it should include the name of the player that will recieve the items"); + done(); + } + }); + it("posts leftovers to master", function(done){ + let inventory2 = { + "exports":{ + "1":[ + {"name":"iron-ore","count":1200}, + {"name":"steel-plate","count":1337}, + {"name":"copper-plate","count":0}, + ] + } + } + let timesRan = 0; + let callback = function(){ + timesRan++ + assert(timesRan <= 2, "placed more than 2 items, unexpeceted test result"); + if(timesRan == 2){ + done(); + } + } + var scope = nock('http://localhost:8080') + .post('/api/place', function(body){ + // check that we are requesting the right things + assert(body.name != "copper-plate", "copper plate should not be placed because it is 0"); + assert(body.count > 0, "no need for placing 0 things"); + assert(Number(body.count) != NaN, "count should be a number"); + callback(); + return true; + }) + // nock destroys the endpoints after being hit once, this changes the number of hits required for destruction + .times(10) + .reply(200,"affirmative") + // if it tries using the nonexisting callback (argv 3) it will throw. It shoudln't. + assert.doesNotThrow(function(){ + functions.handleInventory(inventory2, config); + }, "this here threw for some reason, remove assert.doesNotThrow for easier debugging of this issue"); + }); }); describe("pollInventories(outputfile)", function(){ it("Returns a big factorio command as string", function(){ let x = functions.pollInventories("output.txt"); assert(typeof x == "string", "pollInventories() should always return a string"); - assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); + assert(isFactorioCommand(x), "This is not a command factorio will accept, please prepend /c"); assert(x.length > 20, "LUA string seems to short, this ain't good"); }); it("Returns valid LUA", function(){ @@ -81,7 +147,7 @@ describe("inventoryImports/interfacingFunctions.js", function(){ assert(typeof x == "string", "LUA returned should be a string"); assert(x.length > 20, "LUA strings are quite long"); - assert(x.includes("/silent-command ")||x.includes("/c "), "This is not a command factorio will accept, please prepend /c"); + assert(isFactorioCommand(x), "This is not a command factorio will accept, please prepend /c"); }); it("The returned string is a valid factorio command", function(){ let x = functions.insertItemsFromObject(testObject, playerName); From e6da33582c07b638de14e7495271b598868ab014 Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 17 Jun 2017 17:40:46 +0200 Subject: [PATCH 20/21] Cleanup, more coverage --- lib/fileOps.spec.js | 3 +- lib/objectOps.js | 41 ------------------- lib/objectOps.spec.js | 7 ++-- .../interfacingFunctions.spec.js | 2 +- 4 files changed, 6 insertions(+), 47 deletions(-) diff --git a/lib/fileOps.spec.js b/lib/fileOps.spec.js index b9eda49c..ed89f469 100644 --- a/lib/fileOps.spec.js +++ b/lib/fileOps.spec.js @@ -100,7 +100,8 @@ describe("fileOps.js", function(){ fs.statSync("lib/test"); }, Error); assert(x); - }); + // give it some extra time because its sometimes very slow + }).timeout(5000); it("returns false if you give it an invalid path", function(){ let x = fileOps.deleteFolderRecursiveSync("This isn't a real path"); assert(!x); diff --git a/lib/objectOps.js b/lib/objectOps.js index 3bf9e988..e5d4d716 100644 --- a/lib/objectOps.js +++ b/lib/objectOps.js @@ -1,48 +1,7 @@ -var copyChildren = function(d){ - var copied = clone(d); - copy = []; - if(copied.children){ - copied.children.forEach(function(h){ - copy.push(h); - }); - } - if(copied._children){ - copied._children.forEach(function(h){ - copy.push(h); - }); - } -} - -var clone = function(obj) { - if (null == obj || "object" != typeof obj) return obj; - var copy = obj.constructor(); - for (var attr in obj) { - if (obj.hasOwnProperty(attr) && attr != "parent"){ - if(attr == "children" && obj[attr] != null){ - copy[attr] = obj[attr].map(function (d) { - var subCopy = clone(d); - return subCopy; - }); - } - else if(attr == "_children" && obj[attr] != null){ - copy[attr] = obj[attr].map(function(d) { - var subCopy = clone(d); - return subCopy; - }); - } - else{ - copy[attr] = obj[attr]; - } - } - } - return copy; -} - var deepclone = function(obj){ return JSON.parse(JSON.stringify(obj)); } module.exports = { - clone:clone, deepclone:deepclone, }; \ No newline at end of file diff --git a/lib/objectOps.spec.js b/lib/objectOps.spec.js index 2e16ee64..c10a00a9 100644 --- a/lib/objectOps.spec.js +++ b/lib/objectOps.spec.js @@ -2,7 +2,7 @@ var assert = require('assert'); var objectOps = require('./objectOps'); describe("objectOps.js", function(){ - describe("objectOps.clone()", function(){ + /*describe("objectOps.clone()", function(){ it("clones javascript objects", function(){ var object1 = {hello:"world", cat:{legs:4, name:"Kitty", colors:["brown", "yellow", "purple"]}}; var object2 = {}; @@ -20,7 +20,7 @@ describe("objectOps.js", function(){ assert.equal(obj1.cat.colors, "black"); assert.notEqual(obj2.cat.colors[1], "yellow"); }); - }); + });*/ describe("objectOps.deepclone()", function(){ it("deep clones javascript objects", function(){ var obj1 = {hello:"world", cat:{legs:4, name:"Kitty", colors:["brown", "yellow", "purple"]}}; @@ -32,9 +32,8 @@ describe("objectOps.js", function(){ assert.equal(obj2.cat.colors[1], "yellow"); }); it("throws on non JSON parameters", function(){ - x = function(){}; assert.throws(function(){ - y = objectOps.deepclone(x) + let y = objectOps.deepclone(objectOps.deepclone); }); }); }); diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js index 90411831..d7b84808 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -44,7 +44,7 @@ describe("inventoryImports/interfacingFunctions.js", function(){ if(body.name == "iron-plate") assert(body.count == "92"); // nock wants us to return true if this is the correct body and we should reply // if we return false we won't send a reply. - return x + return true; }) // nock destroys the endpoints after being hit once, this changes the number of hits required for destruction .times(10) From 5de0b6319c41f37f2eef7af0cb30a8e41ad61f6d Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Tue, 20 Jun 2017 20:05:54 +0200 Subject: [PATCH 21/21] Error message when started without instanceName And some test coverage --- client.js | 4 ++++ lib/fileOps.spec.js | 2 +- sharedPlugins/inventoryImports/interfacingFunctions.spec.js | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/client.js b/client.js index c8efcd25..cf1bde39 100644 --- a/client.js +++ b/client.js @@ -111,6 +111,10 @@ if (!command || command == "help" || command == "--help") { console.log("Downloaded "+name); }); } +} else if (command == "start" && instance === undefined) { + console.log("ERROR: No instanceName provided!"); + console.log("Usage: node client.js start [instanceName]"); + process.exit(0); } else if (command == "start" && typeof instance == "string" && instance != "/" && !fs.existsSync(instancedirectory)) { // if instance does not exist, create it console.log("Creating instance..."); diff --git a/lib/fileOps.spec.js b/lib/fileOps.spec.js index ed89f469..ef8c141e 100644 --- a/lib/fileOps.spec.js +++ b/lib/fileOps.spec.js @@ -63,7 +63,7 @@ describe("fileOps.js", function(){ }); describe("fileOps.getNewestFile()", function(){ it("gets the newest file in a directory async", function(done){ - fileOps.getNewestFile("lib/test", fileOps.getFileNamesSync("lib/test"), function(err, newest){ + fileOps.getNewestFile("lib/test/", fileOps.getFileNamesSync("lib/test"), function(err, newest){ assert(newest && typeof newest.file == "string"); done(); }); diff --git a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js index d7b84808..9b1639a3 100644 --- a/sharedPlugins/inventoryImports/interfacingFunctions.spec.js +++ b/sharedPlugins/inventoryImports/interfacingFunctions.spec.js @@ -32,6 +32,7 @@ describe("inventoryImports/interfacingFunctions.js", function(){ "requestSlots":{ "iron-plate":100, "steel-plate":480, + "raw-wood":20, "bullshit":0, } }