Polish RA, make it usable.

This commit is contained in:
Soph :3 2026-01-29 18:59:52 +02:00
parent 262c0b5408
commit 6825a05778
14 changed files with 771 additions and 289 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
u2c.py u2c.py
push.sh push.sh
version version
.env

View file

@ -1,15 +1,16 @@
local g = string.gsub local g = string.gsub
sha256 = loadstring(g( sha256 = loadstring(g(
g( g(
g( g(
g( g(
g( g(
g( g(
g( g(
g( g(
'Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1]<q then r[2]=r[2]+1;r[1]=q-(e-1-r[1])-1 else r[1]=r[1]+qVUrVSYs(t)Su=#t;t[#t+1]=0x80;while#t%64~=56Zt[#t+1]=0VSv=q({0,0},u*8)fWw=2,1,-1Zt[#t+1]=a(k(a(v[w]TFF000000),24)TFF)t[#t+1]=a(k(a(v[w]TFF0000),16)TFF)t[#t+1]=a(k(a(v[w]TFF00),8)TFF)t[#t+1]=a(v[w]TFF)VUtVSYx(y,w)Uc(y[w]W0,24)+c(y[w+1]W0,16)+c(y[w+2]W0,8)+(y[w+3]W0)VSYz(t,w,A)SB={}fWC=1,16ZB[C]=x(t,w+(C-1)*4)VfWC=17,64ZSD=B[C-15]SE=b(b(f(B[C-15],7),f(B[C-15],18)),k(B[C-15],3))SF=b(b(f(B[C-2],17),f(B[C-2],19)),k(B[C-2],10))B[C]=(B[C-16]+E+B[C-7]+F)%eVSG,h,H,I,J,j,K,L=d(A)fWC=1,64ZSM=b(b(f(J,6),f(J,11)),f(J,25))SN=b(a(J,j),a(Xbnot(J),K))SO=(L+M+N+p[C]+B[C])%eSP=b(b(f(G,2),f(G,13)),f(G,22))SQ=b(b(a(G,h),a(G,H)),a(h,H))SR=(P+Q)%e;L,K,j,J,I,H,h,G=K,j,J,(I+O)%e,H,h,G,(O+R)%eVA[1]=(A[1]+G)%e;A[2]=(A[2]+h)%e;A[3]=(A[3]+H)%e;A[4]=(A[4]+I)%e;A[5]=(A[5]+J)%e;A[6]=(A[6]+j)%e;A[7]=(A[7]+K)%e;A[8]=(A[8]+L)%eUAVUY(t)t=t W""t=type(t)=="string"and{t:byte(1,-1)}Wt;t=s(t)SA={d(o)}fWw=1,#t,64ZA=z(t,w,A)VU("%08x"):rep(8):format(d(A))V', 'Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1]<q then r[2]=r[2]+1;r[1]=q-(e-1-r[1])-1 else r[1]=r[1]+qVUrVSYs(t)Su=#t;t[#t+1]=0x80;while#t%64~=56Zt[#t+1]=0VSv=q({0,0},u*8)fWw=2,1,-1Zt[#t+1]=a(k(a(v[w]TFF000000),24)TFF)t[#t+1]=a(k(a(v[w]TFF0000),16)TFF)t[#t+1]=a(k(a(v[w]TFF00),8)TFF)t[#t+1]=a(v[w]TFF)VUtVSYx(y,w)Uc(y[w]W0,24)+c(y[w+1]W0,16)+c(y[w+2]W0,8)+(y[w+3]W0)VSYz(t,w,A)SB={}fWC=1,16ZB[C]=x(t,w+(C-1)*4)VfWC=17,64ZSD=B[C-15]SE=b(b(f(B[C-15],7),f(B[C-15],18)),k(B[C-15],3))SF=b(b(f(B[C-2],17),f(B[C-2],19)),k(B[C-2],10))B[C]=(B[C-16]+E+B[C-7]+F)%eVSG,h,H,I,J,j,K,L=d(A)fWC=1,64ZSM=b(b(f(J,6),f(J,11)),f(J,25))SN=b(a(J,j),a(Xbnot(J),K))SO=(L+M+N+p[C]+B[C])%eSP=b(b(f(G,2),f(G,13)),f(G,22))SQ=b(b(a(G,h),a(G,H)),a(h,H))SR=(P+Q)%e;L,K,j,J,I,H,h,G=K,j,J,(I+O)%e,H,h,G,(O+R)%eVA[1]=(A[1]+G)%e;A[2]=(A[2]+h)%e;A[3]=(A[3]+H)%e;A[4]=(A[4]+I)%e;A[5]=(A[5]+J)%e;A[6]=(A[6]+j)%e;A[7]=(A[7]+K)%e;A[8]=(A[8]+L)%eUAVUY(t)t=t W""t=type(t)=="string"and{t:byte(1,-1)}Wt;t=s(t)SA={d(o)}fWw=1,#t,64ZA=z(t,w,A)VU("%08x"):rep(8):format(d(A))V',
"S", " local "), "T", ",0x"), "U", " return "), "V", " end "), "W", "or "), "X", "bit32."), "Y", "function "), "Z", "S", " local "), "T", ",0x"), "U", " return "), "V", " end "), "W", "or "), "X", "bit32."), "Y",
"function "), "Z",
" do "))() " do "))()
local aead = require("lib.plc.aead_chacha_poly") local aead = require("lib.plc.aead_chacha_poly")
@ -27,9 +28,9 @@ local function b64u_encode(bin)
out[#out + 1] = b64abc:sub(bit32.band(bit32.rshift(triple, 18), 63) + 1, bit32.band(bit32.rshift(triple, 18), 63) + 1) out[#out + 1] = b64abc:sub(bit32.band(bit32.rshift(triple, 18), 63) + 1, bit32.band(bit32.rshift(triple, 18), 63) + 1)
out[#out + 1] = b64abc:sub(bit32.band(bit32.rshift(triple, 12), 63) + 1, bit32.band(bit32.rshift(triple, 12), 63) + 1) out[#out + 1] = b64abc:sub(bit32.band(bit32.rshift(triple, 12), 63) + 1, bit32.band(bit32.rshift(triple, 12), 63) + 1)
out[#out + 1] = i + 1 <= n and out[#out + 1] = i + 1 <= n and
b64abc:sub(bit32.band(bit32.rshift(triple, 6), 63) + 1, bit32.band(bit32.rshift(triple, 6), 63) + 1) or '' b64abc:sub(bit32.band(bit32.rshift(triple, 6), 63) + 1, bit32.band(bit32.rshift(triple, 6), 63) + 1) or ''
out[#out + 1] = i + 2 <= n and out[#out + 1] = i + 2 <= n and
b64abc:sub(bit32.band(bit32.rshift(triple, 0), 63) + 1, bit32.band(bit32.rshift(triple, 0), 63) + 1) or '' b64abc:sub(bit32.band(bit32.rshift(triple, 0), 63) + 1, bit32.band(bit32.rshift(triple, 0), 63) + 1) or ''
i = i + 3 i = i + 3
end end
return table.concat(out) return table.concat(out)
@ -151,8 +152,10 @@ local VERSION = "\1"
local KDF_ID = "\1" local KDF_ID = "\1"
local AAD = "SiSS RA rev1|chacha20poly1305" local AAD = "SiSS RA rev1|chacha20poly1305"
local function u32be(n) return string.char(bit32.band(bit32.rshift(n, 24), 255), bit32.band(bit32.rshift(n, 16), 255), local function u32be(n)
bit32.band(bit32.rshift(n, 8), 255), bit32.band(n, 255)) end return string.char(bit32.band(bit32.rshift(n, 24), 255), bit32.band(bit32.rshift(n, 16), 255),
bit32.band(bit32.rshift(n, 8), 255), bit32.band(n, 255))
end
local function u16be(n) return string.char(bit32.band(bit32.rshift(n, 8), 255), bit32.band(n, 255)) end local function u16be(n) return string.char(bit32.band(bit32.rshift(n, 8), 255), bit32.band(n, 255)) end
local function pack_params(iter) local function pack_params(iter)
return u32be(iter) .. u16be(32) return u32be(iter) .. u16be(32)

80
src/lib/encryption.lua Normal file
View file

@ -0,0 +1,80 @@
local g = string.gsub
sha256 = loadstring(g(
g(
g(
g(
g(
g(
g(
g(
'Sa=XbandSb=XbxWSc=XlshiftSd=unpackSe=2^32SYf(g,h)Si=g/2^hSj=i%1Ui-j+j*eVSYk(l,m)Sn=l/2^mUn-n%1VSo={0x6a09e667Tbb67ae85T3c6ef372Ta54ff53aT510e527fT9b05688cT1f83d9abT5be0cd19}Sp={0x428a2f98T71374491Tb5c0fbcfTe9b5dba5T3956c25bT59f111f1T923f82a4Tab1c5ed5Td807aa98T12835b01T243185beT550c7dc3T72be5d74T80deb1feT9bdc06a7Tc19bf174Te49b69c1Tefbe4786T0fc19dc6T240ca1ccT2de92c6fT4a7484aaT5cb0a9dcT76f988daT983e5152Ta831c66dTb00327c8Tbf597fc7Tc6e00bf3Td5a79147T06ca6351T14292967T27b70a85T2e1b2138T4d2c6dfcT53380d13T650a7354T766a0abbT81c2c92eT92722c85Ta2bfe8a1Ta81a664bTc24b8b70Tc76c51a3Td192e819Td6990624Tf40e3585T106aa070T19a4c116T1e376c08T2748774cT34b0bcb5T391c0cb3T4ed8aa4aT5b9cca4fT682e6ff3T748f82eeT78a5636fT84c87814T8cc70208T90befffaTa4506cebTbef9a3f7Tc67178f2}SYq(r,q)if e-1-r[1]<q then r[2]=r[2]+1;r[1]=q-(e-1-r[1])-1 else r[1]=r[1]+qVUrVSYs(t)Su=#t;t[#t+1]=0x80;while#t%64~=56Zt[#t+1]=0VSv=q({0,0},u*8)fWw=2,1,-1Zt[#t+1]=a(k(a(v[w]TFF000000),24)TFF)t[#t+1]=a(k(a(v[w]TFF0000),16)TFF)t[#t+1]=a(k(a(v[w]TFF00),8)TFF)t[#t+1]=a(v[w]TFF)VUtVSYx(y,w)Uc(y[w]W0,24)+c(y[w+1]W0,16)+c(y[w+2]W0,8)+(y[w+3]W0)VSYz(t,w,A)SB={}fWC=1,16ZB[C]=x(t,w+(C-1)*4)VfWC=17,64ZSD=B[C-15]SE=b(b(f(B[C-15],7),f(B[C-15],18)),k(B[C-15],3))SF=b(b(f(B[C-2],17),f(B[C-2],19)),k(B[C-2],10))B[C]=(B[C-16]+E+B[C-7]+F)%eVSG,h,H,I,J,j,K,L=d(A)fWC=1,64ZSM=b(b(f(J,6),f(J,11)),f(J,25))SN=b(a(J,j),a(Xbnot(J),K))SO=(L+M+N+p[C]+B[C])%eSP=b(b(f(G,2),f(G,13)),f(G,22))SQ=b(b(a(G,h),a(G,H)),a(h,H))SR=(P+Q)%e;L,K,j,J,I,H,h,G=K,j,J,(I+O)%e,H,h,G,(O+R)%eVA[1]=(A[1]+G)%e;A[2]=(A[2]+h)%e;A[3]=(A[3]+H)%e;A[4]=(A[4]+I)%e;A[5]=(A[5]+J)%e;A[6]=(A[6]+j)%e;A[7]=(A[7]+K)%e;A[8]=(A[8]+L)%eUAVUY(t)t=t W""t=type(t)=="string"and{t:byte(1,-1)}Wt;t=s(t)SA={d(o)}fWw=1,#t,64ZA=z(t,w,A)VU("%08x"):rep(8):format(d(A))V',
"S", " local "), "T", ",0x"), "U", " return "), "V", " end "), "W", "or "), "X", "bit32."), "Y",
"function "), "Z",
" do "))()
local chacha20 = require("lib.chacha20")
local function hex_to_bytes(hex)
return (hex:gsub("..", function(cc)
return string.char(tonumber(cc, 16))
end))
end
-- hash a human string into a 32-byte key
---@param key string
---@return string -- 32 bytes
local function hashKey(key)
return hex_to_bytes(sha256(key)) -- MUST return raw bytes
end
-- generate secure random bytes
local function random_bytes(n)
local t = {}
for i = 1, n do
t[i] = string.char(math.random(0, 255))
end
return table.concat(t)
end
---@param hashed_key string -- 32 bytes
---@param plaintext string
---@return string -- nonce || ciphertext
local function encrypt(hashed_key, plaintext)
assert(#hashed_key == 32, "key must be 32 bytes")
local nonce = random_bytes(24) -- XChaCha20 nonce
local counter = 0
local ciphertext = chacha20.xchacha20_encrypt(
hashed_key,
counter,
nonce,
plaintext
)
-- prepend nonce so it can be used for decryption
return nonce .. ciphertext
end
---@param hashed_key string
---@param data string -- nonce || ciphertext
---@return string plaintext
local function decrypt(hashed_key, data)
local nonce = data:sub(1, 24)
local ciphertext = data:sub(25)
local counter = 0
return chacha20.xchacha20_encrypt(
hashed_key,
counter,
nonce,
ciphertext
)
end
return {
hashKey = hashKey,
encrypt = encrypt,
decrypt = decrypt
}

View file

@ -2,28 +2,38 @@
---@field port number ---@field port number
---@field password string ---@field password string
---@class ConectionRemote : InnerRemote
---@field remote string
---@class Remote ---@class Remote
---@field ender_storage string ---@field ender_storage string
---@field modem string ---@field modem string
---@field remotes table<string, InnerRemote> ---@field remotes table<string, InnerRemote>|nil
---@field connection InnerRemote|nil
---@class Chatbox ---@class Chatbox
---@field players table<string, string> ---@field players table<string, string>
---@field prefix string|nil ---@field prefix string|nil
---@class Config ---@class Config
---@field inventories string[] ---@field inventories string[]|nil
---@field import string[]|nil ---@field import string[]|nil
---@field chatbox Chatbox|nil ---@field chatbox Chatbox|nil
---@field remote Remote ---@field remote Remote|nil
local inv = require("modules.inv") local config = require("../config") ---@type Config
local inv;
if config.inventories == nil and config.remote.connection then
inv = require("modules.inv_ra")
else
inv = require("modules.inv")
end
local ui = require("modules.ui") local ui = require("modules.ui")
local chatbox = require("modules.chatbox") local chatbox = require("modules.chatbox")
local ra = require("modules.ra") local ra = require("modules.ra")
local config = require("../config") ---@type Config
local function importMechanism() local function importMechanism()
if config.import == nil then if config.import == nil then
@ -55,4 +65,6 @@ local function importMechanism()
end end
inv.sync() inv.sync()
parallel.waitForAll(inv.getAIL().run, chatbox.run, ra.run, ui.run, importMechanism, inv.detectPlayerInsert) parallel.waitForAll(inv.run(), chatbox.run, ra.run,
ui.run,
importMechanism, inv.detectPlayerInsert)

View file

@ -1,6 +1,11 @@
local config = require("../../config") ---@type Config local config = require("../../config") ---@type Config
local inv = require("modules.inv") local inv;
if config.inventories == nil and config.remote.connection then
inv = require("modules.inv_ra")
else
inv = require("modules.inv")
end
local function levDist(s, t) local function levDist(s, t)
local n = #s local n = #s
local m = #t local m = #t
@ -176,7 +181,7 @@ function run()
---@type ccTweaked.peripheral.Inventory ---@type ccTweaked.peripheral.Inventory
local peripInventory = perip.getInventory() local peripInventory = perip.getInventory()
local best = findBest(inv.getAIL().listNames(), args[2]) local best = findBest(inv.listNames(), args[2])
local itemName = "" local itemName = ""

View file

@ -13,85 +13,76 @@ local peripherals = peripheral.getNames();
local ail = nil local ail = nil
local function sync() local function sync()
print("Syncing..") print("Syncing..")
---@type string[] ---@type string[]
local foundInventories = {} local foundInventories = {}
for _, invPattern in ipairs(config.inventories) do for _, invPattern in ipairs(config.inventories) do
for _, inv in ipairs(peripherals) do for _, inv in ipairs(peripherals) do
if string.find(inv, invPattern) then if string.find(inv, invPattern) then
table.insert(foundInventories, inv) table.insert(foundInventories, inv)
end end
end
end end
end
ail = abstractInventoryLib(foundInventories, true, { ail = abstractInventoryLib(foundInventories, true, {})
--[[cache = true,
optimal = true,
unoptimal = true,
api = true,
defrag = true,
redirect = function (e)
print(e)
end,]]
})
print("Synced."); print("Synced.");
end end
---@param itemName string ---@param itemName string
---@param perip ccTweaked.peripheral.Inventory|string|nil ---@param perip ccTweaked.peripheral.Inventory|string|nil
---@param maxAmount number|nil ---@param maxAmount number|nil
local function sendItemToSelf(itemName, perip, maxAmount) local function sendItemToSelf(itemName, perip, maxAmount)
if perip == nil then if perip == nil then
perip = turtleId perip = turtleId
end end
turtleMoveAllowed = false turtleMoveAllowed = false
local remaining = maxAmount or 64 local remaining = maxAmount or 64
local total = 0 local total = 0
if remaining <= 0 then if remaining <= 0 then
turtleMoveAllowed = true
return
end
if #ail.listNBT(itemName) ~= 0 then
local nbtList = ail.listNBT(itemName)
local chosenNBT = nil
if nbtList and #nbtList > 0 then
chosenNBT = nbtList[math.random(1, #nbtList)]
end
if chosenNBT == "NONE" then
chosenNBT = nil
end
while remaining > 0 do
local toSend = math.min(64, remaining)
local ok, moved = pcall(function()
ail.performTransfer()
local amount = ail.pushItems(perip, itemName, toSend, nil, chosenNBT, {
["allowBadTransfers"] = true,
["optimal"] = false
})
ail.performTransfer()
return amount
end)
total = total + moved
if ok and moved and moved > 0 then
remaining = remaining - moved
else
break
end
end
end
turtleMoveAllowed = true turtleMoveAllowed = true
return
end
return total if #ail.listNBT(itemName) ~= 0 then
local nbtList = ail.listNBT(itemName)
local chosenNBT = nil
if nbtList and #nbtList > 0 then
chosenNBT = nbtList[math.random(1, #nbtList)]
end
if chosenNBT == "NONE" then
chosenNBT = nil
end
while remaining > 0 do
local toSend = math.min(64, remaining)
local ok, moved = pcall(function()
ail.performTransfer()
local amount = ail.pushItems(perip, itemName, toSend, nil, chosenNBT, {
["allowBadTransfers"] = true,
["optimal"] = false
})
ail.performTransfer()
return amount
end)
total = total + moved
if ok and moved and moved > 0 then
remaining = remaining - moved
else
break
end
end
end
turtleMoveAllowed = true
return total
end end
---@param slots ccTweaked.turtle.turtleSlot[] ---@param slots ccTweaked.turtle.turtleSlot[]
@ -99,111 +90,127 @@ end
---@param id string|nil ---@param id string|nil
---@param maxAmount number|nil ---@param maxAmount number|nil
local function sendItemAwayMultiple(slots, perip, id, maxAmount) local function sendItemAwayMultiple(slots, perip, id, maxAmount)
if perip == nil then if perip == nil then
perip = turtle perip = turtle
end
local srcId = id or turtleId
if srcId == nil then return end
local itemsInSlots = {}
for _, slot in ipairs(slots) do
local item = perip.getItemDetail(slot)
if item then
itemsInSlots[slot] = item
end end
local srcId = id or turtleId end
if srcId == nil then return end local totalMoved = 0
local remaining = maxAmount or math.huge
local itemsInSlots = {} if remaining <= 0 then
for _, slot in ipairs(slots) do return 0
local item = perip.getItemDetail(slot) end
if item then
itemsInSlots[slot] = item for _, slot in ipairs(slots) do
end if remaining <= 0 then break end
local toSend = math.min(64, remaining)
local ok, pulled = pcall(function()
ail.performTransfer()
local moved = ail.pullItems(srcId, slot, toSend, nil, nil, {
["allowBadTransfers"] = true,
["optimal"] = false
})
ail.performTransfer()
return moved
end)
if ok and pulled and pulled > 0 then
totalMoved = totalMoved + pulled
remaining = remaining - pulled
end end
end
local totalMoved = 0 local numItems = 0
local remaining = maxAmount or math.huge for _, _ in pairs(itemsInSlots) do
numItems = numItems + 1
end
if remaining <= 0 then if numItems > 0 then
return 0 os.queueEvent("update_ui")
end end
for _, slot in ipairs(slots) do return totalMoved
if remaining <= 0 then break end
local toSend = math.min(64, remaining)
local ok, pulled = pcall(function()
ail.performTransfer()
local moved = ail.pullItems(srcId, slot, toSend, nil, nil, {
["allowBadTransfers"] = true,
["optimal"] = false
})
ail.performTransfer()
return moved
end)
if ok and pulled and pulled > 0 then
totalMoved = totalMoved + pulled
remaining = remaining - pulled
end
end
local numItems = 0
for _, _ in pairs(itemsInSlots) do
numItems = numItems + 1
end
if numItems > 0 then
os.queueEvent("update_ui")
end
return totalMoved
end end
local function getTurtleInventory() local function getTurtleInventory()
local inventory = {} local inventory = {}
for slot = 1, 16 do for slot = 1, 16 do
local item = turtle.getItemDetail(slot, false) local item = turtle.getItemDetail(slot, false)
inventory[slot] = item inventory[slot] = item
end end
return inventory return inventory
end end
local function detectPlayerInsert() local function detectPlayerInsert()
if turtle then
previousInventory = getTurtleInventory() previousInventory = getTurtleInventory()
while true do while true do
os.pullEvent("turtle_inventory") os.pullEvent("turtle_inventory")
local currentInventory = getTurtleInventory() local currentInventory = getTurtleInventory()
if turtleMoveAllowed then if turtleMoveAllowed then
local newlyAddedSlots = {} local newlyAddedSlots = {}
for slot = 1, 16 do for slot = 1, 16 do
local prev = previousInventory[slot] local prev = previousInventory[slot]
local curr = currentInventory[slot] local curr = currentInventory[slot]
if not prev and curr then if not prev and curr then
table.insert(newlyAddedSlots, slot) table.insert(newlyAddedSlots, slot)
end end
end
if #newlyAddedSlots > 0 then
sendItemAwayMultiple(newlyAddedSlots)
end
end end
previousInventory = currentInventory if #newlyAddedSlots > 0 then
sendItemAwayMultiple(newlyAddedSlots)
end
end
previousInventory = currentInventory
end end
end
end end
local function getAIL() local function run()
return ail return ail.run
end end
local function listNames()
return ail.listNames()
end
local function listItemAmounts()
return ail.listItemAmounts()
end
local function getItem(z)
return ail.getItem(z)
end
return { return {
detectPlayerInsert = detectPlayerInsert, detectPlayerInsert = detectPlayerInsert,
sendItemAwayMultiple = sendItemAwayMultiple, sendItemAwayMultiple = sendItemAwayMultiple,
sendItemToSelf = sendItemToSelf, sendItemToSelf = sendItemToSelf,
getTurtleInventory = getTurtleInventory, getTurtleInventory = getTurtleInventory,
sync = sync, listItemAmounts = listItemAmounts,
getAIL = getAIL, listNames = listNames,
getItem = getItem,
sync = sync,
run = run
} }

171
src/modules/inv_ra.lua Normal file
View file

@ -0,0 +1,171 @@
local previousInventory = {}
---@type ccTweaked.peripheral.WiredModem
local modem = peripheral.find("modem");
local turtleId = modem.getNameLocal()
local turtleMoveAllowed = true
local config = require("../../config") ---@type Config
local ra = require("tiny_ra_library")
local function sync()
print("inv_ra does not require syncing (it is already synced!)")
end
---@param itemName string
---@param perip ccTweaked.peripheral.Inventory|string|nil
---@param maxAmount number|nil
local function sendItemToSelf(itemName, perip, maxAmount)
local enderstorage = peripheral.wrap(config.remote.ender_storage);
if enderstorage == nil then return end
turtleMoveAllowed = false
local id = turtleId;
ra.withdraw(itemName, maxAmount, true) -- make sure to wait so we can push to turtle once waited
if type(perip) == "string" then
id = perip
end
for key, _ in pairs(enderstorage.list()) do
enderstorage.pushItems(id, key)
end
sleep(0.2)
turtleMoveAllowed = true
end
---@param slots ccTweaked.turtle.turtleSlot[]
---@param perip ccTweaked.peripheral.Inventory|nil
---@param id string|nil
---@param maxAmount number|nil
local function sendItemAwayMultiple(slots, perip, id, maxAmount)
if perip == nil then
perip = turtle
end
local srcId = id or turtleId
if srcId == nil then return end
local itemsInSlots = {}
for _, slot in ipairs(slots) do
local item = perip.getItemDetail(slot)
if item then
itemsInSlots[slot] = item
end
end
local enderstorage = peripheral.wrap(config.remote.ender_storage);
if enderstorage == nil then return end
local totalMoved = 0
local remaining = maxAmount or math.huge
if remaining <= 0 then
return 0
end
for _, slot in ipairs(slots) do
if remaining <= 0 then break end
local toSend = math.min(64, remaining)
local ok, pulled = pcall(function()
return enderstorage.pullItems(srcId, slot, toSend)
end)
if ok and pulled and pulled > 0 then
totalMoved = totalMoved + pulled
remaining = remaining - pulled
end
end
local numItems = 0
for _, _ in pairs(itemsInSlots) do
numItems = numItems + 1
end
if numItems > 0 then
os.queueEvent("update_ui")
end
local keys = {}
for key, _ in pairs(enderstorage.list()) do
if key ~= nil then
table.insert(keys, key)
end
end
ra.depositBySlots(keys, nil, false)
return totalMoved
end
local function getTurtleInventory()
local inventory = {}
for slot = 1, 16 do
local item = turtle.getItemDetail(slot, false)
inventory[slot] = item
end
return inventory
end
local function detectPlayerInsert()
if turtle then
previousInventory = getTurtleInventory()
while true do
os.pullEvent("turtle_inventory")
local currentInventory = getTurtleInventory()
if turtleMoveAllowed then
local newlyAddedSlots = {}
for slot = 1, 16 do
local prev = previousInventory[slot]
local curr = currentInventory[slot]
if not prev and curr then
table.insert(newlyAddedSlots, slot)
end
end
if #newlyAddedSlots > 0 then
sendItemAwayMultiple(newlyAddedSlots)
end
end
previousInventory = currentInventory
end
end
end
local function run()
return function()
ra.init(peripheral.wrap(config.remote.modem), config.remote.connection.port, config.remote.connection.password)
end
end
local function listNames()
return ra.listNames()
end
local function listItemAmounts()
return ra.listItemAmounts()
end
local function getItem(z)
return ra.getItem(z)
end
return {
detectPlayerInsert = detectPlayerInsert,
sendItemAwayMultiple = sendItemAwayMultiple,
sendItemToSelf = sendItemToSelf,
getTurtleInventory = getTurtleInventory,
listItemAmounts = listItemAmounts,
listNames = listNames,
getItem = getItem,
sync = sync,
run = run
}

View file

@ -1,6 +1,12 @@
local inv = require("modules.inv");
local config = require("../../config") ---@type Config local config = require("../../config") ---@type Config
local hmac = require("lib.hmac-pbkdf2-aead") if config.inventories == nil and config.remote.connection then
inv = require("modules.inv_ra")
else
inv = require("modules.inv")
end
local encryption = require("lib.encryption")
---@class RemoteAccess ---@class RemoteAccess
---@field type string ---@field type string
---@field data RemoteAccessData ---@field data RemoteAccessData
@ -10,13 +16,34 @@ local hmac = require("lib.hmac-pbkdf2-aead")
---@field amount number|nil ---@field amount number|nil
---@field slots number[]|nil ---@field slots number[]|nil
local function sendResponse(modem, port, hashedPassword, id, ok, data, err)
modem.transmit(
port,
port,
encryption.encrypt(hashedPassword,
textutils.serialiseJSON({
type = "response",
id = id,
ok = ok,
data = data,
error = err
})
)
)
end
local function run() local function run()
if config.remote == nil then return end if config.remote == nil or config.remote.remotes == nil then return end
local modem = peripheral.wrap(config.remote.modem) local modem = peripheral.wrap(config.remote.modem)
if not modem then return end if not modem then return end
for _, remote in pairs(config.remote.remotes) do local remotes = config.remote.remotes or { config.remote.connection }
for _, remote in pairs(remotes) do
if not modem.isOpen(remote.port) then if not modem.isOpen(remote.port) then
modem.open(remote.port) modem.open(remote.port)
end end
@ -28,10 +55,11 @@ local function run()
while true do while true do
local _, _, channel, _, message, _ = os.pullEvent("modem_message") local _, _, channel, _, message, _ = os.pullEvent("modem_message")
for color, remote in pairs(config.remote.remotes) do for color, remote in pairs(remotes) do
if remote.port == channel then if remote.port == channel then
local hashed = encryption.hashKey(remote.password)
local err, data = pcall(function() local err, data = pcall(function()
return hmac.decrypt_with_password(remote.password, message) return encryption.decrypt(hashed, message)
end) end)
if not data then return end if not data then return end
@ -51,23 +79,82 @@ local function run()
colors[parts[3]] colors[parts[3]]
) )
if json.type == "withdraw" then if json.type == "withdraw" then
inv.sendItemToSelf(json.data.itemName, config.remote.ender_storage, json.data.amount) local ok, err = pcall(function()
elseif json.type == "deposit" then inv.sendItemToSelf(
if json.data.itemName then json.data.itemName,
local move = {} config.remote.ender_storage,
for s, i in pairs(ender_storage.list()) do json.data.amount
if i.name == json.data.itemName then )
table.insert(move, s) end)
end
end
inv.sendItemAwayMultiple(move, ender_storage, config.remote.ender_storage, json.data.amount)
elseif json.data.slots then
if type(json.data.slots) ~= 'table' then
return
end
inv.sendItemAwayMultiple(json.data.slots, ender_storage, config.remote.ender_storage, json.data.amount) sendResponse(modem, remote.port, hashed, json.id, ok, nil, err)
end elseif json.type == "deposit" then
local ok, err = pcall(function()
if json.data.itemName then
local move = {}
for s, i in pairs(ender_storage.list()) do
if i.name == json.data.itemName then
table.insert(move, s)
end
end
inv.sendItemAwayMultiple(
move,
ender_storage,
config.remote.ender_storage,
json.data.amount
)
elseif json.data.slots then
inv.sendItemAwayMultiple(
json.data.slots,
ender_storage,
config.remote.ender_storage,
json.data.amount
)
end
end)
sendResponse(modem, remote.port, hashed, json.id, ok, nil, err)
elseif json.type == "list_names" then
local ok, result = pcall(function()
return inv.listNames()
end)
sendResponse(
modem,
remote.port,
hashed,
json.id,
ok,
ok and result or nil,
not ok and result or nil
)
elseif json.type == "list_item_amounts" then
local ok, result = pcall(function()
return inv.listItemAmounts()
end)
sendResponse(
modem,
remote.port,
hashed,
json.id,
ok,
ok and result or nil,
not ok and result or nil
)
elseif json.type == "get_item" then
local ok, result = pcall(function()
return inv.getItem(json.data.index)
end)
sendResponse(
modem,
remote.port,
hashed,
json.id,
ok,
ok and result or nil,
not ok and result or nil
)
end end
end end
end end

View file

@ -1,4 +1,11 @@
local inv = require("modules.inv"); local config = require("../../config") ---@type Config
local inv;
if config.inventories == nil and config.remote.connection then
inv = require("modules.inv_ra")
else
inv = require("modules.inv")
end
local PrimeUI = require("lib.primeui").PrimeUI; local PrimeUI = require("lib.primeui").PrimeUI;
local function is_beta(ver) local function is_beta(ver)
if ver:match("^[0-9a-f]+$") and #ver >= 8 then if ver:match("^[0-9a-f]+$") and #ver >= 8 then
@ -12,9 +19,18 @@ local function run()
local win = term.current(); local win = term.current();
local w, h = term.getSize() local w, h = term.getSize()
local items = inv.listItemAmounts()
local x = 0
local function getFiltered() local function getFiltered()
local filtered = {} local filtered = {}
for name, count in pairs(inv.getAIL().listItemAmounts()) do
x = x + 1
if x % 3 == 0 then
items = inv.listItemAmounts()
end
for name, count in pairs(items) do
if search == "" or string.find(name:lower(), search:lower(), 1, true) then if search == "" or string.find(name:lower(), search:lower(), 1, true) then
table.insert(filtered, { name = name, count = count }) table.insert(filtered, { name = name, count = count })
end end
@ -25,7 +41,7 @@ local function run()
for i, filter in ipairs(filtered) do for i, filter in ipairs(filtered) do
refiltered[i] = filter.name .. refiltered[i] = filter.name ..
string.rep(" ", w - (2 + #filter.name + #tostring(filter.count))) .. tostring(filter.count) string.rep(" ", w - (2 + #filter.name + #tostring(filter.count))) .. tostring(filter.count)
end end
return refiltered return refiltered
end end
@ -40,8 +56,7 @@ local function run()
end, end,
function(option) function(option)
local z = option:match("^(%S+)") local z = option:match("^(%S+)")
if inv.getItem(z) then
if inv.getAIL().getItem(z) then
inv.sendItemToSelf(z) inv.sendItemToSelf(z)
end end
end end

198
src/tiny_ra_library.lua Normal file
View file

@ -0,0 +1,198 @@
local encryption = require("lib.encryption")
---@type string
local password
---@type number
local port
---@type string
local hashedPassword
---@type ccTweaked.peripheral.Modem
local modem
local function sendRequest(reqType, data, wait)
if wait == nil then
wait = true
end
local id = tostring(math.random(1, 1e9))
modem.transmit(
port,
port,
encryption.encrypt(hashedPassword,
textutils.serialiseJSON({
type = reqType,
id = id,
data = data
})
)
)
if wait then
-- wait for response
while true do
local event, side, senderChannel, replyChannel, message = os.pullEvent("modem_message")
local ok, decrypted = pcall(encryption.decrypt, hashedPassword, message)
if not ok then goto continue end
local resp = textutils.unserialiseJSON(decrypted)
if resp and resp.type == "response" and resp.id == id then
return resp.ok, resp.data or resp.error
end
::continue::
end
end
end
local function init(modemI, portI, passwordI)
port = portI
password = passwordI
hashedPassword = encryption.hashKey(passwordI)
modem = modemI
modem.open(port)
end
---@param wait boolean|nil
local function listNames(wait)
if not port or not modem or not password then
error("tiny_ra_library: init was never ran")
end
if wait == nil then
wait = true
end
local ok, data = sendRequest("list_names", nil, wait)
if wait then
if not ok then
error("Failed to list names: " .. tostring(data))
end
return data
end
end
---@param itemName string
---@param count number|nil
---@param wait boolean|nil
local function withdraw(itemName, count, wait)
if not port or not modem or not password then
error("tiny_ra_library: init was never ran")
end
if wait == nil then
wait = true
end
local ok, data = sendRequest("withdraw", {
itemName = itemName,
count = count
}, wait)
if wait then
if not ok then
error("Failed to withdraw: " .. tostring(data))
end
return data
end
end
---@param slots number[]
---@param count number|nil
---@param wait boolean|nil
local function depositBySlots(slots, count, wait)
if not port or not modem or not password then
error("tiny_ra_library: init was never ran")
end
if wait == nil then
wait = true
end
local data = {
slots = slots
}
if count then
data["count"] = count
end
local ok, response = sendRequest("deposit", data, wait)
if wait then
if not ok then
error("Failed to deposit by slots: " .. tostring(response))
end
return response
end
end
---@param wait boolean|nil
local function listItemAmounts(wait)
if wait == nil then
wait = true
end
local ok, response = sendRequest("list_item_amounts", nil, wait)
if wait then
if not ok then
error("Failed to list names: " .. tostring(response))
end
return response
end
end
---@param itemName string
---@param wait boolean|nil
local function getItem(itemName, wait)
if wait == nil then
wait = true
end
local ok, data = sendRequest("get_item", { index = itemName }, wait)
if wait then
if not ok then
error("Failed to list names: " .. tostring(data))
end
return data
end
end
---@param itemName string
---@param count number|nil
---@param wait boolean|nil
local function depositByItemName(itemName, count, wait)
if not port or not modem or not password then
error("tiny_ra_library: init was never ran")
end
local data = {
itemName = itemName
}
if count then
data["count"] = count
end
local ok, response = sendRequest("deposit", data, wait)
if wait then
if not ok then
error("Failed to list names: " .. tostring(response))
end
return response
end
end
return {
init = init,
listItemAmounts = listItemAmounts,
getItem = getItem,
listNames = listNames,
withdraw = withdraw,
depositByItemName = depositByItemName,
depositBySlots = depositBySlots
}

File diff suppressed because one or more lines are too long