Working inventory imports resolves #50

This commit is contained in:
Danielv123 committed 2017-06-14 21:46:30 +02:00
1 parent f9a2e8519b
commit c4ead8c699
3 files changed
+181 -96

No files matched your search

+9 -8
View File
@@ -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);
@@ -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?
+82 -14
View File
@@ -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