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