Compare commits
2 Commits
1f593d0542
...
e45b1ea995
Author | SHA1 | Date | |
---|---|---|---|
e45b1ea995 | |||
4b4c656af1 |
@ -4,72 +4,7 @@ This is an implementation of a centralized storage network architecture that use
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
The master node must have a modem attached and be within range of all slave nodes.
|
1. Deploy a Smart Computer
|
||||||
|
2. Connect the computer to at least 1 chest and 1 hopper via physical modem lines. Chests need to be attached from the bottom and hoppers from their output direction
|
||||||
1. Deploy at least one slave turtle
|
3. Put a chest on top of the Smart Computer
|
||||||
* The inventory it is to monitor must be above the unit
|
4. Deploy the script
|
||||||
* The unit should be able to drop below itself to push items to the master node
|
|
||||||
* Any subsequent slaves should be placed in front of the unit
|
|
||||||
2. Set up a return system that pushes into the first slave node (can be a hopper or similar)
|
|
||||||
|
|
||||||
## Communication Protocol
|
|
||||||
|
|
||||||
Messages are sent over rednet with the protocol `ccstoragenet`.
|
|
||||||
|
|
||||||
### Packet Format
|
|
||||||
|
|
||||||
Packet format is uniform between master <-> slaves and is structured like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
// Metadata
|
|
||||||
networkid: int()
|
|
||||||
sourceid: int()
|
|
||||||
sourcetype: str()
|
|
||||||
destid: int()
|
|
||||||
|
|
||||||
// Payload
|
|
||||||
type: str()
|
|
||||||
|
|
||||||
// Optional Arguments
|
|
||||||
itemname: str()
|
|
||||||
itemquant: int()
|
|
||||||
destination: int()
|
|
||||||
location: [int(),int(),int()]
|
|
||||||
|
|
||||||
// Dump for additional body data
|
|
||||||
body: any
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| Name | Required? | Type | Description
|
|
||||||
| :-- | :--: | :-- | :--
|
|
||||||
| `networkid` | true | int | The ID of the network |
|
|
||||||
| `sourceid` | true | int | The CC ID of the machine sending the packet |
|
|
||||||
| `sourcetype` | true | string | The mode of the machine sending the packet |
|
|
||||||
| `destid` | true | int | The CC ID of the intended recipient of the packet |
|
|
||||||
| `type` | true | string | An arbitrary string literal corresponding to the type of the request. Common examplse include `ping`, `query`, etc. |
|
|
||||||
| `itemname` | false | string | The unlocalized name of an item. Used for querying, crafting, movement, etc. |
|
|
||||||
| `itemquant` | false | int | Quantity of the aforementioned item |
|
|
||||||
| `destination` | false | int | The CC ID of the intended recipient of the item |
|
|
||||||
| `body` | false | any | Any additional body data per the requirements of a query, such as a detailed list of inventory contents |
|
|
||||||
|
|
||||||
### Slave Node Initialization
|
|
||||||
|
|
||||||
* The slave starts up and immediately listens for messages
|
|
||||||
|
|
||||||
### Master Node Initialization
|
|
||||||
|
|
||||||
* The master node starts up
|
|
||||||
* The master node sends a packet with type `ping`.
|
|
||||||
* The master node listens for `pong` packets from all slaves.
|
|
||||||
* The `sourceid` of all `pong` packets is recorded for statistics displays. This cached data is only used for user display. Node availability is evaluated at request time.
|
|
||||||
|
|
||||||
### Inventory Search (query)
|
|
||||||
|
|
||||||
* The sender node sends a packet with type `query`. Field `itemname` is populated with an item to query for.
|
|
||||||
* Each available slave responds with type `query`.
|
|
||||||
* `itemname` is populated with data from the previous packet
|
|
||||||
* `itemquantity` is populated with the quantity of that item attached to storages the slave has access to.
|
|
||||||
* The requester waits for a response from each slave that responded to the `ping` or until a configurable timeout is reached.
|
|
||||||
* Results are consumed
|
|
@ -3,142 +3,53 @@
|
|||||||
-- For information on this script, physical in-world setup, and configuration,
|
-- For information on this script, physical in-world setup, and configuration,
|
||||||
-- see: https://git.desu.ltd/salt/mc-scripts/src/branch/master/storage-net
|
-- see: https://git.desu.ltd/salt/mc-scripts/src/branch/master/storage-net
|
||||||
|
|
||||||
-- Default configuration values. Override in config.lua. DO NOT CHANGE HERE
|
|
||||||
mode = "ConfigureMe" -- The mode of function for this node
|
|
||||||
networkid = 0 -- Unique ID for this network
|
|
||||||
port_broadcast = 42914 -- Port for M->S traffic
|
|
||||||
port_return = 42915 -- Port for S->M traffic
|
|
||||||
packet_magic = "ccstoragenet" -- Just a random string
|
|
||||||
|
|
||||||
-- This loads config.lua. See masterconfig.lua and slaveconfig.lua for example
|
|
||||||
-- configurations.
|
|
||||||
require "config"
|
|
||||||
|
|
||||||
-- Startup diagnostics
|
-- Startup diagnostics
|
||||||
print("Salt's CC Storage Net")
|
print("Salt's CC Storage Net")
|
||||||
print("Computer " .. os.getComputerID() .. " configured as " .. mode)
|
print("Computer " .. os.getComputerID() .. " configured as " .. mode)
|
||||||
|
|
||||||
-- Common globals
|
-- Global scope locals
|
||||||
modem = peripheral.find("modem") or error("No modem attached", 0)
|
local output = peripheral.wrap("top") or error("Put a chest on top of this terminal for output items", 0)
|
||||||
modem_side = peripheral.getName(modem)
|
|
||||||
-- Master globals
|
-- Helper functions
|
||||||
m_slaves = {}
|
function getDepositHoppers()
|
||||||
-- Slave globals
|
-- Get all connected inventories that push items into the system
|
||||||
s_chest = peripheral.wrap("top") or nil
|
return {peripheral.find("inventory", function(name,i)
|
||||||
|
return string.find(name, "minecraft:hopper")
|
||||||
|
end)}
|
||||||
|
end
|
||||||
|
function getConnectedChests()
|
||||||
|
-- Get all connected inventories that store items
|
||||||
|
return {peripheral.find("inventory", function(name,i)
|
||||||
|
return string.find(name, "minecraft:chest")
|
||||||
|
end)}
|
||||||
|
end
|
||||||
|
|
||||||
-- Common functions
|
-- Common functions
|
||||||
function c_mainLoop(loopfunc)
|
function pushDepositsToChests()
|
||||||
-- Loops a thing forever
|
-- Take all the contents of all connected hoppers and stuff them into any other connected inventory
|
||||||
while true do
|
-- These things are wrapped into tables because that's how you take multiple return values and stuff them in a table, apparently
|
||||||
loopfunc()
|
local hoppers = getDepositHoppers()
|
||||||
end
|
local chests = {peripheral.find("inventory", function(name,i) return string.find(name, "minecraft:chest") end)}
|
||||||
end
|
-- For each hopper connected to the network...
|
||||||
function c_waitForMessage()
|
for k,hopper in ipairs(hoppers) do
|
||||||
-- Waits for a message on the modem, timing out after some duration
|
-- For each item in that hopper's inventory...
|
||||||
local sender, message = rednet.receive(packet_magic, 1)
|
for hslot,hitem in pairs(hopper.list()) do
|
||||||
if message then
|
-- For each connected "chest"...
|
||||||
if
|
for k,chest in ipairs(chests) do
|
||||||
(not message["type"]) or -- Message type is required
|
-- Attempt to push our items in
|
||||||
(message["sourcetype"] == mode) or -- Ignore packets from our class of machines
|
hopper.pushItems(peripheral.getName(chest),hslot)
|
||||||
(message["networkid"] ~= networkid) or -- Ignore packets from other networks
|
end
|
||||||
(message["targetid"] and message["targetid"] ~= os.getComputerID) -- Ignore packets for other machines
|
|
||||||
then
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
return message
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
function c_sendMessage(message)
|
|
||||||
-- Sends a message, either to the master or all slaves depending on mode
|
|
||||||
local msg = {
|
|
||||||
networkid = networkid,
|
|
||||||
sourceid = os.getComputerID(),
|
|
||||||
sourcetype = mode,
|
|
||||||
}
|
|
||||||
-- Override basic message object with args
|
|
||||||
for k,v in pairs(message) do
|
|
||||||
msg[k] = v
|
|
||||||
end
|
|
||||||
--print("Transmitting message: " .. textutils.serialize(msg))
|
|
||||||
rednet.broadcast(msg, packet_magic)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Master functions
|
|
||||||
function m_loop()
|
|
||||||
-- The main loop of the master server
|
|
||||||
-- Listen for packets on the return net
|
|
||||||
local msg = c_waitForMessage()
|
|
||||||
if not msg then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if msg["type"] == "pong" then
|
|
||||||
local source = msg["sourceid"]
|
|
||||||
print("Received pong from slave: " .. source)
|
|
||||||
if (not m_slaves[source]) then
|
|
||||||
m_slaves[source] = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
function m_ping()
|
|
||||||
-- Clear out the list of all slaves and send out a fresh ping
|
|
||||||
-- Ping information isn't used for much, so delay in clearing cache and
|
|
||||||
-- repopulating the data isn't a big deal.
|
|
||||||
m_slaves = {}
|
|
||||||
c_sendMessage({type="ping"})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Slave functions
|
|
||||||
function s_loop()
|
|
||||||
-- The main loop of any slave nodes
|
|
||||||
-- Listen for packets from the master
|
|
||||||
local msg = c_waitForMessage()
|
|
||||||
if not msg then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if (msg["type"] == "ping") then
|
|
||||||
-- Respond to pings with pongs
|
|
||||||
print("Received ping from master: " .. msg["sourceid"])
|
|
||||||
c_sendMessage({type="pong"})
|
|
||||||
elseif (msg["type"] == "query") then
|
|
||||||
-- Analyze the attached inventory and see if we have the item
|
|
||||||
else
|
|
||||||
print("Unknown message: " .. textutils.serialize(msg))
|
|
||||||
end
|
|
||||||
-- If we have any items, we should stow them away. Put them in our chest if
|
|
||||||
-- we can fit it, otherwise pass it along
|
|
||||||
for i=1,16 do
|
|
||||||
turtle.select(i)
|
|
||||||
while turtle.getItemCount(i) > 0 do
|
|
||||||
-- We have items in this slot -- push up until we can't anymore
|
|
||||||
if not turtle.dropUp() then break end
|
|
||||||
end
|
|
||||||
if turtle.getItemCount(i) > 0 then turtle.drop() end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Application entrypoint
|
-- Application entrypoint
|
||||||
function main ()
|
function main()
|
||||||
if (mode == "master") then
|
while true do
|
||||||
print("Beginning initialization as master...")
|
-- Manage inventory cleanup tasks
|
||||||
rednet.open(modem_side)
|
pushDepositsToChests() -- Take all deposit terminals and push them into the inventory network
|
||||||
|
-- Sleep for a tick
|
||||||
print("Pinging for slaves...")
|
sleep(0.05) -- This is to stop CC from killing us
|
||||||
m_ping()
|
|
||||||
|
|
||||||
print("Entering main loop")
|
|
||||||
c_mainLoop(m_loop)
|
|
||||||
elseif (mode == "slave") then
|
|
||||||
print("Beginning initialization as slave...")
|
|
||||||
rednet.open(modem_side)
|
|
||||||
if not s_chest then
|
|
||||||
error("No connected inventory. Place one above this node.", 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
print("Entering main loop")
|
|
||||||
c_mainLoop(s_loop)
|
|
||||||
else
|
|
||||||
error("Invalid mode: " .. mode .. ", please configure this node appropriately", 0)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -10,17 +10,11 @@ mcroot="$HOME/.var/app/org.prismlauncher.PrismLauncher/data/PrismLauncher/instan
|
|||||||
world="New World"
|
world="New World"
|
||||||
|
|
||||||
masterid="0"
|
masterid="0"
|
||||||
slaves=("1" "8" "9")
|
|
||||||
|
|
||||||
_ccroot="$mcroot/.minecraft/saves/$world/computercraft/computer"
|
_ccroot="$mcroot/.minecraft/saves/$world/computercraft/computer"
|
||||||
echo "Root: $_ccroot"
|
echo "Root: $_ccroot"
|
||||||
for computer in "$masterid" ${slaves[*]}; do
|
for computer in "$masterid"; do
|
||||||
echo "Deploying to: $computer"
|
echo "Deploying to: $computer"
|
||||||
mkdir -p "$_ccroot/$computer"
|
mkdir -p "$_ccroot/$computer"
|
||||||
cp common.lua "$_ccroot/$computer/autostart.lua"
|
cp common.lua "$_ccroot/$computer/autostart"
|
||||||
done
|
|
||||||
cp masterconfig.lua "$_ccroot/$masterid/config.lua"
|
|
||||||
for slave in ${slaves[*]}; do
|
|
||||||
echo "Deploying slave config: $slave"
|
|
||||||
cp slaveconfig.lua "$_ccroot/$slave/config.lua"
|
|
||||||
done
|
done
|
@ -1 +0,0 @@
|
|||||||
mode = "master"
|
|
@ -1 +0,0 @@
|
|||||||
mode = "slave"
|
|
Loading…
Reference in New Issue
Block a user