Compare commits

...

6 Commits

Author SHA1 Message Date
b47a8ad4fa Update shit 2024-02-26 02:58:42 -06:00
a1bb89a4d2 Remove extraneous code 2024-02-26 02:30:48 -06:00
53bfc85164 Improve algo for sorting items away 2024-02-26 02:25:39 -06:00
bcbb419179 Working on a bunch of things 2024-02-26 01:46:12 -06:00
e45b1ea995 Remove extraneous configs 2024-02-26 01:37:38 -06:00
4b4c656af1 Rework the script into something much simpler holy shit 2024-02-26 01:37:03 -06:00
5 changed files with 77 additions and 202 deletions

View File

@@ -4,72 +4,10 @@ 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. First, consider using [someone else's project](https://github.com/lewark/inv.lua) instead of this one. It's liable to be a lot better than mine.
1. Deploy at least one slave turtle 1. Deploy a Smart Computer
* The inventory it is to monitor must be above the unit 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
* The unit should be able to drop below itself to push items to the master node 3. Put a chest on top of the Smart Computer
* Any subsequent slaves should be placed in front of the unit 4. Install Basalt (`wget run https://basalt.madefor.cc/install.lua release latest.lua`)
2. Set up a return system that pushes into the first slave node (can be a hopper or similar) 5. Deploy the script (as `startup`, of course)
## 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

View File

@@ -3,142 +3,87 @@
-- 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)
-- 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 = getConnectedChests()
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"...
for k,chest in ipairs(chests) do
-- First, make an attempt to find slots that we can shove the item into
for cslot,citem in pairs(chest.list()) do
if if
(not message["type"]) or -- Message type is required citem["name"] == hitem["name"] and -- We have the same item
(message["sourcetype"] == mode) or -- Ignore packets from our class of machines citem["count"] < chest.getItemLimit(cslot) -- There's space in this slot
(message["networkid"] ~= networkid) or -- Ignore packets from other networks
(message["targetid"] and message["targetid"] ~= os.getComputerID) -- Ignore packets for other machines
then then
return nil hopper.pushItems(peripheral.getName(chest),hslot,hitem["count"],cslot)
end end
return message
end 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 end
--print("Transmitting message: " .. textutils.serialize(msg)) -- We've fallen through trying to fill up existing stacks. Fragmentation is not a concern, put it wherever
rednet.broadcast(msg, packet_magic) if hopper.getItemDetail(hslot) then
end for k,chest in ipairs(chests) do
hopper.pushItems(peripheral.getName(chest),hslot)
-- Master functions end
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 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 end
end end
function m_ping() function requestItem(name, count)
-- Clear out the list of all slaves and send out a fresh ping -- Pull a number of an item into the output chest above the machine
-- Ping information isn't used for much, so delay in clearing cache and remaining = count
-- repopulating the data isn't a big deal. local chests = getConnectedChests()
m_slaves = {} -- For each chest on the network...
c_sendMessage({type="ping"}) for k,chest in ipairs(chests) do
end -- For each slot in that chest...
for cslot,citem in pairs(chest.list()) do
-- Slave functions if
function s_loop() citem["name"] == name and
-- The main loop of any slave nodes citem["count"] > 0
-- Listen for packets from the master then
local msg = c_waitForMessage() desired = math.max(remaining, citem["count"])
if not msg then output.pullItems(peripheral.getName(chest),cslot,desired)
return remaining = remaining - desired
end end
if (msg["type"] == "ping") then if remaining <= 0 then return true end
-- 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 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
if remaining > 0 then return false end
end end
-- Application entrypoint -- Application entrypoint
function main () function main()
if (mode == "master") then requestItem("minecraft:sandstone", 12)
print("Beginning initialization as master...") while true do
rednet.open(modem_side) -- Manage inventory cleanup tasks
pushDepositsToChests() -- Take all deposit terminals and push them into the inventory network
print("Pinging for slaves...") -- Sleep for a tick
m_ping() sleep(0.05) -- This is to stop CC from killing us
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

View File

@@ -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/startup"
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

View File

@@ -1 +0,0 @@
mode = "master"

View File

@@ -1 +0,0 @@
mode = "slave"