Actually use version control
This commit is contained in:
commit
d30317b8e8
45
README.md
Normal file
45
README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Dragon
|
||||||
|
A centralized storage management system for Minecraft.
|
||||||
|
Requires Plethora Peripherals by SquidDev and ideally CC: Tweaked.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
### Networking & Hardware
|
||||||
|
|
||||||
|
```
|
||||||
|
Chests ───────┐
|
||||||
|
│ │
|
||||||
|
Server ─── Buffer
|
||||||
|
│ │
|
||||||
|
└────┬─────┘
|
||||||
|
│
|
||||||
|
├────────── Processing (not implemented yet)
|
||||||
|
├────────── Introspection Module in manipulator (optional)
|
||||||
|
│
|
||||||
|
┌────┴──┬───────┐
|
||||||
|
Client Client Client
|
||||||
|
```
|
||||||
|
**Chests** is a set of chests and/or shulker boxes, connected via networking cables and modems, to the **Server** and **Buffer**.
|
||||||
|
**Buffer** is a set of two droppers, each with two wired connections - one on the internal side, connected to the chests, and one on the external side, connected to the clients.
|
||||||
|
**Server** is a computer running `server.lua`. It must be connected to the chests, clients, and both sides of the buffers.
|
||||||
|
**Client** is a crafty turtle running `client.lua`. It must be connected to the server and external side of the buffers.
|
||||||
|
**Processing** will be used for autocrafting systems. It is not yet implemented.
|
||||||
|
**Introspection Module** is an introspection module, bound to a user, in a manipulator. To use it, it must be connected to a client with it configured, and the external side of the buffers. It is recommended that you interact with the client connected to it via a Plethora keyboard.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
Configuration must be saved in a file called `conf` (no `.lua` extension). It is in lua table/textutils.serialise syntax.
|
||||||
|
|
||||||
|
#### Server/Client
|
||||||
|
Both server and client require `modem` keys indicating which side their (connected) modems are on.
|
||||||
|
|
||||||
|
#### Client
|
||||||
|
A client requires a `name` key indicating its name on the network. This should be displayed when you rightclick its modem.
|
||||||
|
If you are using an introspection module, an `introspection` key must be added, indicating the network name of the manipulator it is in.
|
||||||
|
|
||||||
|
#### Server
|
||||||
|
`buffer(In/Out)(External/Internal)` keys must contain the network names of each buffer dropper on the chest-side and client-side networks.
|
||||||
|
Which buffer is external or internal does not matter, as long as the internal and external network names for out and in point to the same inventory.
|
||||||
|
|
||||||
|
## Warnings
|
||||||
|
* Inserting/extracting items manually into/out of chests will result in the index being desynchronised with the actual items. To remedy this, run `r` in the CLI after doing so.
|
||||||
|
* Items with different names but the same ID/metadata may be labelled under the wrong name, as the system uses caching to avoid lag-inducing calls on every slot of chests.
|
||||||
|
* Errors are likely to be very cryptic in this version, as I have not implemented proper error handling.
|
98
client.lua
Normal file
98
client.lua
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
local util = require "util"
|
||||||
|
local conf = util.conf
|
||||||
|
|
||||||
|
rednet.open(conf.modem)
|
||||||
|
|
||||||
|
if conf.introspection then
|
||||||
|
conf.introspection = peripheral.call(conf.introspection, "getInventory")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split(str, sep)
|
||||||
|
local t = {}
|
||||||
|
for sub in str:gmatch("[^" .. sep .. "]+") do
|
||||||
|
table.insert(t, sub)
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fetchItem(item, toGet)
|
||||||
|
local result
|
||||||
|
repeat
|
||||||
|
local toGetNow = 64
|
||||||
|
if toGet < 64 then toGetNow = toGet end
|
||||||
|
|
||||||
|
result = query { cmd = "extract", dname = item, destInv = conf.name, qty = toGetNow }
|
||||||
|
if result and type(result) == "table" and result[1] then
|
||||||
|
toGet = toGet - result[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
if conf.introspection then
|
||||||
|
conf.introspection.pullItems(conf.name, 1)
|
||||||
|
end
|
||||||
|
until toGet <= 0 or result == "ERROR"
|
||||||
|
end
|
||||||
|
|
||||||
|
function dump(slot)
|
||||||
|
if conf.introspection then
|
||||||
|
conf.introspection.pushItems(conf.name, slot)
|
||||||
|
slot = 1
|
||||||
|
end
|
||||||
|
query { cmd = "insert", fromInv = conf.name, fromSlot = slot }
|
||||||
|
end
|
||||||
|
|
||||||
|
function tryNumber(tokens)
|
||||||
|
local fst = table.remove(tokens, 1)
|
||||||
|
local qty = tonumber(fst)
|
||||||
|
|
||||||
|
if not qty then
|
||||||
|
table.insert(tokens, 1, fst)
|
||||||
|
end
|
||||||
|
|
||||||
|
return qty
|
||||||
|
end
|
||||||
|
|
||||||
|
local help = [[
|
||||||
|
Welcome to the Dragon CLI.
|
||||||
|
Commands:
|
||||||
|
w [name] - withdraw all items whose names contain [name]
|
||||||
|
w [qty] [name] - withdraw [qty] items whose names contain [name]
|
||||||
|
c - Craft item, using the turtle's inventory as a grid (turtle.craft)
|
||||||
|
d - Dump all items into storage
|
||||||
|
d [slot] - Dump items in [slot] into storage
|
||||||
|
r - Force connected storage server to reindex
|
||||||
|
help - Display this
|
||||||
|
This is an unstable version and does not support a GUI or multiple storage servers.]]
|
||||||
|
|
||||||
|
print "Dragon CLI"
|
||||||
|
while true do
|
||||||
|
write "|> "
|
||||||
|
local tokens = split(read(), " ")
|
||||||
|
local cmd = table.remove(tokens, 1)
|
||||||
|
|
||||||
|
if cmd == "w" then
|
||||||
|
local qty = tryNumber(tokens)
|
||||||
|
if not qty then
|
||||||
|
qty = math.huge
|
||||||
|
end
|
||||||
|
|
||||||
|
local item = table.concat(tokens, " ")
|
||||||
|
fetchItem(item, qty)
|
||||||
|
elseif cmd == "c" then
|
||||||
|
turtle.craft()
|
||||||
|
elseif cmd == "d" then
|
||||||
|
local slot = tryNumber(tokens)
|
||||||
|
|
||||||
|
if slot then dump(slot) else
|
||||||
|
local size = 16
|
||||||
|
if conf.introspection then size = conf.introspection.size() end
|
||||||
|
for i = 1, size do
|
||||||
|
dump(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif cmd == "r" then
|
||||||
|
query { cmd = "reindex" }
|
||||||
|
elseif cmd == "help" then
|
||||||
|
textutils.pagedPrint(help)
|
||||||
|
end
|
||||||
|
end
|
128
server.lua
Normal file
128
server.lua
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
local util = require "util"
|
||||||
|
local conf = util.conf
|
||||||
|
|
||||||
|
rednet.open(conf.modem)
|
||||||
|
|
||||||
|
local inventories = {}
|
||||||
|
for _, n in pairs(peripheral.getNames()) do
|
||||||
|
local p = peripheral.wrap(n)
|
||||||
|
if
|
||||||
|
string.find(n, "chest") or
|
||||||
|
string.find(n, "shulker") then
|
||||||
|
inventories[n] = p
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local nameCache = {}
|
||||||
|
|
||||||
|
function cache(item, chest, slot)
|
||||||
|
local idx = item.name .. ":" .. item.damage
|
||||||
|
|
||||||
|
if nameCache[idx] then
|
||||||
|
return nameCache[idx]
|
||||||
|
else
|
||||||
|
local n = chest.getItemMeta(slot).displayName
|
||||||
|
nameCache[idx] = n
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local index = {}
|
||||||
|
function updateIndexFor(name)
|
||||||
|
local inv = inventories[name]
|
||||||
|
local data = inv.list()
|
||||||
|
|
||||||
|
for slot, item in pairs(data) do
|
||||||
|
data[slot].displayName = cache(item, inv, slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
index[name] = data
|
||||||
|
end
|
||||||
|
|
||||||
|
function updateIndex()
|
||||||
|
for n in pairs(inventories) do
|
||||||
|
updateIndexFor(n)
|
||||||
|
sleep()
|
||||||
|
end
|
||||||
|
print "Indexing complete."
|
||||||
|
end
|
||||||
|
|
||||||
|
function find(predicate)
|
||||||
|
for name, items in pairs(index) do
|
||||||
|
for slot, item in pairs(items) do
|
||||||
|
if predicate(item) then
|
||||||
|
return name, slot, item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function findSpace()
|
||||||
|
for name, items in pairs(index) do
|
||||||
|
if #items < inventories[name].size() then
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function processRequest(msg)
|
||||||
|
print(textutils.serialise(msg))
|
||||||
|
|
||||||
|
if msg.cmd == "extract" then
|
||||||
|
local inv, slot, item = find(function(item)
|
||||||
|
return
|
||||||
|
(not msg.meta or item.damage == msg.meta) and
|
||||||
|
(not msg.name or item.name == msg.name) and
|
||||||
|
(not msg.dname or string.find(item.displayName:lower(), msg.dname:lower()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
index[inv][slot] = nil
|
||||||
|
|
||||||
|
local moved = peripheral.call(conf.bufferOutInternal, "pullItems", inv, slot, msg.qty or 64, 1)
|
||||||
|
|
||||||
|
if msg.destInv then
|
||||||
|
moved = peripheral.call(conf.bufferOutExternal, "pushItems", msg.destInv, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {moved, item}
|
||||||
|
elseif msg.cmd == "insert" then
|
||||||
|
if msg.fromInv and msg.fromSlot then
|
||||||
|
peripheral.call(conf.bufferInExternal, "pullItems", msg.fromInv, msg.fromSlot, msg.qty or 64, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local toInv = findSpace()
|
||||||
|
if not toInv then return "ERROR" end
|
||||||
|
|
||||||
|
peripheral.call(conf.bufferInInternal, "pushItems", toInv, 1)
|
||||||
|
|
||||||
|
updateIndexFor(toInv) -- I don't know a good way to figure out where exactly the items went
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
elseif msg.cmd == "buffers" then
|
||||||
|
return { conf.bufferInExternal, conf.bufferOutExternal }
|
||||||
|
elseif msg.cmd == "reindex" then
|
||||||
|
updateIndex()
|
||||||
|
return "OK"
|
||||||
|
elseif msg.cmd == "list" then
|
||||||
|
return index
|
||||||
|
elseif msg.cmd == "name" then
|
||||||
|
msg.meta = msg.meta or 0
|
||||||
|
return msg.name and msg.meta and nameCache[msg.name .. ":" .. msg.meta]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function processRequests()
|
||||||
|
while true do
|
||||||
|
local id, msg = rednet.receive "dragon"
|
||||||
|
if msg and msg.cmd then
|
||||||
|
local ok, r = pcall(processRequest, msg)
|
||||||
|
|
||||||
|
if not ok then r = "ERROR" end
|
||||||
|
|
||||||
|
rednet.send(id, r, "dragon")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
updateIndex()
|
||||||
|
processRequests()
|
14
util.lua
Normal file
14
util.lua
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
local f = fs.open("conf", "r")
|
||||||
|
local conf = textutils.unserialise(f.readAll())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
local function query(m)
|
||||||
|
local msg
|
||||||
|
repeat
|
||||||
|
rednet.broadcast(m, "dragon")
|
||||||
|
_, msg = rednet.receive("dragon", 1)
|
||||||
|
until msg
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
return { conf = conf, query = query }
|
Loading…
Reference in New Issue
Block a user