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){ // 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; } 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":{}}}} {"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 = {}; // 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){ // 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 { confirmedItems[stack.name] = stack.count; } } if(itemsToConfirm == 0 && Object.keys(confirmedItems).length > 0){ // return LUA callback(insertItemsFromObject(confirmedItems, playerNames[i])); } } // 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, {json:true,headers: {'x-access-token': config.masterAuthToken}}, 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 { collectStacks(false); } }); } else { collectStacks(false); } }); } } // {"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], {headers: {'x-access-token': config.masterAuthToken}}, function (err, resp, body) { // console.log(body); }); } } }); } } 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]; if(count > 0){ itemTable += '["'+name+'"]='+Math.floor(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,true,0)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") //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='+"'"+'{"players":{'+"'"+'local b=false;for c,d in pairs(game.players)do if d.connected and d.character 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={}local z={}for j,k in pairs(e)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end for j,k in pairs(f)do if z[j] ~= nil then z[j] = z[j] + k else z[j] = k end end 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(z)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,true,0)' /* needle.post(config.masterIP+':'+config.masterPort+'/api/editSlaveMeta', {instanceID: config.unique, password: config.clientPassword, meta: {UPS:UPS}}, function(err, resp) { // success? }); */ } module.exports = { handleInventory: handleInventory, parseJsString: parseJsString, pollInventories: pollInventories, insertItemsFromObject: insertItemsFromObject, }