diff --git a/README.md b/README.md index 31ed24ed..c1d6d509 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ #factorioClusterio -- The first ever mod allowing you to transfer items between worlds +Features: + +- Entities to send/recieve items + +- Cross dimensional storage + +- Sending of liquids + +- Sending of circuit network signals #Windows setup @@ -42,3 +50,5 @@ Once finished, do [factorioDirectory]/bin/x64/factorio.exe [flags] node client.js + +See launchEverything.bat for example command. \ No newline at end of file diff --git a/client.js b/client.js index 60952455..a900666a 100644 --- a/client.js +++ b/client.js @@ -12,11 +12,19 @@ var client = new Rcon({ timeout: 0 }).connect(); +if (!fs.existsSync(config.factorioDirectory)){ + console.error("FATAL ERROR: config.factorioDirectory DOES NOT EXIST, PLEASE UPDATE CONFIG.JSON"); + process.exit(1); +} +// make sure we got the files we need if (!fs.existsSync(config.factorioDirectory + "/script-output/")){ fs.mkdirSync(config.factorioDirectory + "/script-output/"); } -fs.writeFileSync(config.factorioDirectory + "/script-output/output.txt", "") +if (!fs.existsSync(config.factorioDirectory + "/script-output/")){ + fs.writeFileSync(config.factorioDirectory + "/script-output/output.txt", "") +} fs.writeFileSync(config.factorioDirectory + "/script-output/orders.txt", "") +fs.writeFileSync(config.factorioDirectory + "/script-output/txbuffer.txt", ""); client.on('authenticated', function() { console.log('Authenticated!'); @@ -30,7 +38,8 @@ client.on('authenticated', function() { // set some globals confirmedOrders = []; - +lastSignalCheck = Date.now(); +// provide items -------------------------------------------------------------- // trigger when something happens to output.txt fs.watch(config.factorioDirectory + "/script-output/output.txt", "utf8", function(eventType, filename) { // get array of lines in file @@ -52,7 +61,7 @@ fs.watch(config.factorioDirectory + "/script-output/output.txt", "utf8", functio } } }) - +// request items -------------------------------------------------------------- setInterval(function() { // get array of lines in file items = fs.readFileSync(config.factorioDirectory + "/script-output/orders.txt", "utf8").split("\n"); @@ -61,7 +70,7 @@ setInterval(function() { fs.writeFileSync(config.factorioDirectory + "/script-output/orders.txt", ""); // prepare a package of all our requested items in a more tranfer friendly format var preparedPackage = {}; - for(i = 2;i < items.length; i++) { + for(i = 0;i < items.length; i++) { (function(i){ if(items[i]) { items[i] = items[i].split(" "); @@ -97,8 +106,49 @@ setInterval(function() { client.exec("/silent-command remote.call('clusterio', 'importMany', '" + sadas + "')"); } }, 3000) -/* -fs.watch(config.factorioDirectory + "/script-output/orders.txt", "utf8", function(eventType, filename) { +// COMBINATOR SIGNALS --------------------------------------------------------- +// send any signals the slave has been told to send +setInterval(function() { + // Fetch combinator signals from the server + needle.post(config.masterIP + ":" + config.masterPort + '/readSignal', {since:lastSignalCheck}, function(err, response, body){ + if(response && response.body && typeof response.body == "object" && response.body[0]) { + // Take the new combinator frames and compress them so we can use a single command + frameset = []; + for(i=0;i 0 then GiveItemsToStorage(requestItem.name, itemsNotInsertedCount) end @@ -214,16 +233,15 @@ function HandleOutputTanks() local MAX_FLUID_AMOUNT = 1000 for k,v in pairs(global.outputTanks) do --.recipe.products[1].name - if v.recipe ~= nil then local fluidName = v.recipe.products[1].name - + --either get the fluid or reset it to the requested fluid local fluid = v.fluidbox[1] or {type = fluidName, amount = 0} if fluid.type ~= fluidName then fluid = {type = fluidName, amount = 0} end - + --if any fluid is missing then request the fluid --from store and give either what it's missing or --the rest of the liquid in the system @@ -237,11 +255,11 @@ function HandleOutputTanks() AddItemToOutputList(fluid.type, fluidToRequestAmount) end end - - v.fluidbox[1] = fluid - end + + v.fluidbox[1] = fluid end end +end function AddItemToInputList(itemName, itemCount) @@ -261,9 +279,9 @@ function ExportInputList() end global.inputList = {} if #exportStrings > 0 then - + --only write to file once as i/o is slow - --it's much faster to concatenate all the lines with table.concat + --it's much faster to concatenate all the lines with table.concat --instead of doing it with the .. operator game.write_file(OUTPUT_FILE, table.concat(exportStrings), true) end @@ -276,9 +294,9 @@ function ExportOutputList() end global.outputList = {} if #exportStrings > 0 then - + --only write to file once as i/o is slow - --it's much faster to concatenate all the lines with table.concat + --it's much faster to concatenate all the lines with table.concat --instead of doing it with the .. operator game.write_file(ORDER_FILE, table.concat(exportStrings), true) end @@ -296,7 +314,7 @@ function RequestItemsFromStorage(itemName, itemCount) --requested then take the number of items there are left otherwise take the requested amount local itemsTakenFromStorage = math.min(global.itemStorage[itemName], itemCount) global.itemStorage[itemName] = global.itemStorage[itemName] - itemsTakenFromStorage - + return itemsTakenFromStorage end @@ -311,10 +329,110 @@ end +function AddFrameToRXBuffer(frame) + -- Add a frame to the buffer. return remaining space in buffer + local validsignals = { + ["virtual"] = game.virtual_signal_prototypes, + ["fluid"] = game.fluid_prototypes, + ["item"] = game.item_prototypes + } + + global.rxBuffer = global.rxBuffer or {} + + -- if buffer is full, drop frame + if #global.rxBuffer >= MAX_RX_BUFFER_SIZE then return 0 end + + -- frame = {{count=42,name="signal-grey",type="virtual"},{...},...} + local signals = {} + local index = 1 + + for _,signal in pairs(frame) do + if validsignals[signal.type] and validsignals[signal.type][signal.name] then + signals[index] = + { + index=index, + count=signal.count, + signal={ name=signal.name, type=signal.type } + } + index = index + 1 + --TODO: break if too many? + --TODO: error token on mismatched signals? maybe mismatch1-n signals? + end + end + + if index > 1 then table.insert(global.rxBuffer,signals) end + + return MAX_RX_BUFFER_SIZE - #global.rxBuffer +end + +function HandleTXCombinators() + -- Check all TX Combinators, and if condition satisfied, add frame to transmit buffer + + -- frame = {{count=42,name="signal-grey",type="virtual"},{...},...} + local signals = {["item"]={},["virtual"]={},["fluid"]={}} + for i,txControl in pairs(global.txControls) do + if txControl.valid then + local frame = txControl.signals_last_tick + if frame then + for _,signal in pairs(frame) do + signals[signal.signal.type][signal.signal.name]= + (signals[signal.signal.type][signal.signal.name] or 0) + signal.count + end + end + else + table.remove(global.txControls,i) + end + end + + local frame = {} + for type,arr in pairs(signals) do + for name,count in pairs(arr) do + table.insert(frame,{count=count,name=name,type=type}) + end + end + + if #frame > 0 then + table.insert(frame,{count=game.tick,name="signal-srctick",type="virtual"}) + game.write_file(TX_BUFFER_FILE, json:encode(frame).."\n", true) + + -- Loopback for testing + --AddFrameToRXBuffer(frame) + + end +end + +function SetRXCombinators() + -- if the RX buffer is not empty, get a frame from it and output on all RX Combinators + if global.rxBuffer and #global.rxBuffer > 0 then + local frame = table.remove(global.rxBuffer) + for i,rxControl in pairs(global.rxControls) do + if rxControl.valid then + rxControl.parameters={parameters=frame} + rxControl.enabled=true + else + table.remove(global.rxControls,i) + end + end + end +end + +function ClearRXCombinators() + -- Clear all RX Combinators. + -- This makes them emit pulses, which are easier to + -- detect than slowly changing continusous signals. + for i,rxControl in pairs(global.rxControls) do + if rxControl.valid then + rxControl.enabled=false + else + table.remove(global.rxControls,i) + end + end +end + --[[ Remote Thing ]]-- -remote.add_interface("clusterio", +remote.add_interface("clusterio", { - import = function(itemName, itemCount) + import = function(itemName, itemCount) GiveItemsToStorage(itemName, itemCount) end, importMany = function(jsonString) @@ -332,23 +450,19 @@ remote.add_interface("clusterio", end game.print(items) end, - reset = Reset + reset = Reset, + receiveFrame = function(jsonframe) + local frame = json:decode(jsonframe) + -- frame = {tick=123456,frame={{count=42,name="signal-grey",type="virtual"},{...},...}} + return AddFrameToRXBuffer(frame) + end, + receiveMany = function(jsonframes) + local frames = json:decode(jsonframes) + local buffer + for _,frame in pairs(frames) do + buffer = AddFrameToRXBuffer(frame) + if buffer==0 then return 0 end + end + return buffer + end, }) - - - - - - - - - - - - - - - - - - diff --git a/clusterio_0.0.2/data.lua b/clusterio_0.0.2/data.lua index fa1bb0a4..a1ae8af6 100644 --- a/clusterio_0.0.2/data.lua +++ b/clusterio_0.0.2/data.lua @@ -18,10 +18,10 @@ function MakeLogisticEntity(entity, name, pictureFilename, pictureTablePath, ico ChangePictureFilename(entity, pictureTablePath, pictureFilename) --if no icon is defined then use the default one entity.icon = iconPath or entity.icon - + -- add the entity to a technology so it can be unlocked --local wasAddedToTech = AddEntityToTech("construction-robotics", name) - + data:extend( { -- add the entity @@ -70,7 +70,7 @@ function AddEntityToTech(techName, name) type = "unlock-recipe", recipe = name } - --if a new table for the effects is made then the effects has to be attached to the + --if a new table for the effects is made then the effects has to be attached to the -- tech again because the table won't otherwise be owned by the tech data.raw["technology"][techName].effects = effects return true @@ -86,22 +86,10 @@ MakeLogisticEntity(table.deepcopy(data.raw["container"]["iron-chest"]), --make tanks MakeLogisticEntity(table.deepcopy(data.raw["storage-tank"]["storage-tank"]), INPUT_TANK_NAME, INPUT_TANK_PICTURE_PATH, { "pictures", "picture", "sheet" }, INPUT_TANK_ICON_PATH) - - - - - - -------------------------------------------------------- --[[This section is purely to create the output tank]]-- -------------------------------------------------------- - - - - - - local CRAFING_FLUID_CATEGORY_NAME = "crafting-fluids" data:extend( @@ -121,10 +109,10 @@ fluidCreator.fluid_boxes = pipe_covers = pipecoverspictures(), base_area = 250, base_level = 1, - pipe_connections = + pipe_connections = { - { - type="output", position = {0, 2} + { + type="output", position = {0, 2} } } }, @@ -157,12 +145,96 @@ for k,v in pairs(data.raw.fluid) do end +-- Virtual signals +data:extend{ + { + type = "item-subgroup", + name = "virtual-signal-clusterio", + group = "signals", + order = "e" + }, + { + type = "virtual-signal", + name = "signal-srctick", + icon = "__base__/graphics/icons/signal/signal_grey.png", + subgroup = "virtual-signal-clusterio", + order = "e[clusterio]-[1srctick]" + }, + --[[{ + type = "virtual-signal", + name = "signal-srcid", + icon = "__base__/graphics/icons/signal/signal_grey.png", + subgroup = "virtual-signal-clusterio", + order = "e[clusterio]-[2srcid]" + },]] + --[[{ + type = "virtual-signal", + name = "signal-localid", + icon = "__base__/graphics/icons/signal/signal_grey.png", + subgroup = "virtual-signal-clusterio", + order = "e[clusterio]-[3localid]" + },]] +} +-- TX Combinator +local tx = table.deepcopy(data.raw["decider-combinator"]["decider-combinator"]) +tx.name = TX_COMBINATOR_NAME +tx.minable.result = TX_COMBINATOR_NAME +data:extend{ + tx, + { + type = "item", + name = TX_COMBINATOR_NAME, + icon = tx.icon, + flags = {"goes-to-quickbar"}, + subgroup = "storage", + place_result=TX_COMBINATOR_NAME, + order = "a[items]-b["..TX_COMBINATOR_NAME.."]", + stack_size = 50, + }, + { + type = "recipe", + name = TX_COMBINATOR_NAME, + enabled = true, -- TODO do this on a tech somewhere + ingredients = + { + {"decider-combinator", 1}, + {"electronic-circuit", 3}, + {"advanced-circuit", 1} + }, + result = TX_COMBINATOR_NAME, + requester_paste_multiplier = 1 + }, +} - - - - - - - +-- RX Combinator +local rx = table.deepcopy(data.raw["constant-combinator"]["constant-combinator"]) +rx.name = RX_COMBINATOR_NAME +rx.minable.result = RX_COMBINATOR_NAME +rx.item_slot_count = 100 +data:extend{ + rx, + { + type = "item", + name = RX_COMBINATOR_NAME, + icon = rx.icon, + flags = {"goes-to-quickbar"}, + subgroup = "storage", + place_result=RX_COMBINATOR_NAME, + order = "a[items]-b["..RX_COMBINATOR_NAME.."]", + stack_size = 50, + }, + { + type = "recipe", + name = RX_COMBINATOR_NAME, + enabled = true, -- TODO do this on a tech somewhere + ingredients = + { + {"constant-combinator", 1}, + {"electronic-circuit", 3}, + {"advanced-circuit", 1} + }, + result = RX_COMBINATOR_NAME, + requester_paste_multiplier = 1 + }, +} diff --git a/clusterio_0.0.2/info.json b/clusterio_0.0.2/info.json index 93e439f0..e9a8572e 100644 --- a/clusterio_0.0.2/info.json +++ b/clusterio_0.0.2/info.json @@ -4,8 +4,8 @@ "title": "Clusterio", "version": "0.0.2", "date": "19-08-2016", -"author": "Danielv123 & Keyboardhack", +"author": "Danielv123 & Keyboardhack & justarandomgeek", "dependencies": ["base >= 0.13"], "factorio_version": "0.14", "description": "Adds 2 chests to transfer items between saves and servers over the internet." -} \ No newline at end of file +} diff --git a/clusterio_0.0.2/locale/en/item-names.cfg b/clusterio_0.0.2/locale/en/item-names.cfg index 2a3d0920..08399bce 100644 --- a/clusterio_0.0.2/locale/en/item-names.cfg +++ b/clusterio_0.0.2/locale/en/item-names.cfg @@ -3,10 +3,13 @@ put-chest=Put chest get-chest=Get chest put-tank=Put tank get-tank=Get tank - +put-combinator=Transmit combinator +get-combinator=Receive combinator [item-name] put-chest=Put chest get-chest=Get chest put-tank=Put tank -get-tank=Get tank \ No newline at end of file +get-tank=Get tank +put-combinator=Transmit combinator +get-combinator=Receive combinator diff --git a/database/items.db b/database/items.db deleted file mode 100644 index 08f2850e..00000000 --- a/database/items.db +++ /dev/null @@ -1,4 +0,0 @@ -{"name":"raw-fish","count":"2","_id":"a0TVkMwP8M99LLbp"} -{"name":"iron-ore","count":25823,"_id":"bEcyT728rLO8JvqY"} -{"name":"electronic-circuit","count":200,"_id":"n3JBn8ksl9YIkCWy"} -{"name":"coal","count":10406,"_id":"qnEjvk1EypjWAEQk"} diff --git a/master.js b/master.js index 6e1bdaf7..47fa55f7 100644 --- a/master.js +++ b/master.js @@ -24,6 +24,8 @@ app.use(express.static('bower_components/jquery/dist')); var Datastore = require('nedb'); db = {}; db.items = new Datastore({ filename: 'database/items.db', autoload: true }); +db.signals = new Datastore({ filename: 'database/signals.db', autoload: true, inMemoryOnly: true}); +db.signals.ensureIndex({ fieldName: 'time', expireAfterSeconds: 3600 }, function (err) {}); db.items.additem = function(object) { db.items.findOne({name:object.name}, function (err, doc) { @@ -84,6 +86,26 @@ app.post("/remove", function(req, res) { } }) }); + +// circuit stuff +app.post("/setSignal", function(req,res) { + if(typeof req.body == "object" && req.body.time){ + db.signals.insert(req.body); + // console.log("signal set") + } +}); + +app.post("/readSignal", function(req,res) { + // request.body should be an object + // {since:UNIXTIMESTAMP,} + // we should send an array of all signals since then + db.signals.find({time:{$gte: req.body.since}}, function (err, docs) { + // $gte means greater than or equal to, meaning we only get entries newer than the timestamp + res.send(docs); + // console.log(docs) + }); +}); + // endpoint for getting an inventory of what we got app.get("/inventory", function(req, res) { res.header("Access-Control-Allow-Origin", "*");