Skip to content

Instantly share code, notes, and snippets.

@Ryex
Last active February 23, 2024 00:09
Show Gist options
  • Save Ryex/20f35ef69539949a5b83b5da5387d57c to your computer and use it in GitHub Desktop.
Save Ryex/20f35ef69539949a5b83b5da5387d57c to your computer and use it in GitHub Desktop.
Create computercraft RadialQuary
local CWD = fs.getDir(shell.getRunningProgram())
local tArgs = {...}
local function usage()
print("Usage: config <rotation|level|receiver> <ID>")
print("Start the receiver first and then run rotation and level on respective computers")
print("Use a unique quarry ID and use the same ID on all three computers")
end
local SettingFile = fs.combine(CWD, "settings.cfg")
settings.load(SettingFile)
settings.define("local.quarryID", {
description = "local Quarry ID",
type = "string",
})
settings.define("local.receiverCompID", {
description = "receiver host ID",
type = "number"
})
settings.define("local.rotationCompID", {
description = "rotation client ID",
type = "number",
})
settings.define("local.levelCompID", {
description = "level client ID",
type = "number",
})
settings.save(SettingFile)
if #tArgs < 2 then
return usage()
end
local mode, quarryID = tArgs[1], tArgs[2]
local function openRednet()
peripheral.find("modem", rednet.open)
end
---@enum ConfigMsgType
local ConfigMsgType = {
SendID = 0,
SetupRot = 1,
SetupLevel = 2,
SendingID = 3,
SendingConfig = 4,
}
---@class ConfigMsg
---@field msg ConfigMsgType
---@field val number | table | nil
local function receiver()
settings.set("local.quarryID", quarryID)
settings.save(SettingFile)
openRednet()
rednet.host("ryry.quarry.radial.config", quarryID .. "_" .. os.getComputerID())
local foundRot, foundLevel = false, false
local config = { receiverID = os.getComputerID() }
print("Setting up config host")
while true do
---@type number | nil
local senderID
---@type ConfigMsg
local msg
senderID, msg = rednet.receive("ryry.quarry.radial.config", 60 * 5)
print("Got message", senderID, textutils.serialize(msg))
if senderID == nil then
print("Config Server timed out.")
break
end
if msg.msg == ConfigMsgType.SendID then
rednet.send(senderID, {msg = ConfigMsgType.SendingID, val = quarryID}, "ryry.quarry.radial.config")
elseif msg.msg == ConfigMsgType.SetupRot then
foundRot = true
settings.set("local.rotationCompID", msg.val)
settings.save(SettingFile)
rednet.send(senderID, {msg = ConfigMsgType.SendingConfig, val = config}, "ryry.quarry.radial.config")
elseif msg.msg == ConfigMsgType.SetupLevel then
foundLevel = true
settings.set("local.levelCompID", msg.val)
settings.save(SettingFile)
rednet.send(senderID, {msg = ConfigMsgType.SendingConfig, val = config}, "ryry.quarry.radial.config")
end
if foundRot and foundLevel then
print("Rotation and Level Client Found")
break
end
end
rednet.unhost("ryry.quarry.radial.config")
rednet.close()
end
local function rotationSetup()
settings.set("local.quarryID", quarryID)
settings.save(SettingFile)
openRednet()
local configHostID = nil
print("Looking for config host...")
local lookupCount = 0
while configHostID == nil do
configHostID = rednet.lookup("ryry.quarry.radial.config")
print("Found comp ID", configHostID)
lookupCount = lookupCount + 1
if lookupCount > 60 then
print("Failed to find config host")
return nil
end
sleep(0.2)
end
rednet.send(configHostID, {msg = ConfigMsgType.SendID}, "ryry.quarry.radial.config")
while true do
local senderID, msg = rednet.receive("ryry.quarry.radial.config", 60 * 5)
if senderID == nil then
print("Config attempt timed out")
return nil
elseif senderID == configHostID and msg.msg == ConfigMsgType.SendingID then
if msg.val == quarryID then
rednet.send(configHostID, {msg = ConfigMsgType.SetupRot, val = os.getComputerID()}, "ryry.quarry.radial.config")
end
elseif senderID == configHostID and msg.msg == ConfigMsgType.SendingConfig then
local config = msg.val
settings.set("local.receiverCompID", config.receiverID)
settings.save(SettingFile)
break
end
end
rednet.close()
end
local function levelSetup()
settings.set("local.quarryID", quarryID)
settings.save(SettingFile)
openRednet()
local configHostID = nil
print("Looking for config host...")
local lookupCount = 0
while configHostID == nil do
configHostID = rednet.lookup("ryry.quarry.radial.config")
print("Found comp ID", configHostID)
lookupCount = lookupCount + 1
if lookupCount > 60 then
print("Failed to find config host")
return nil
end
sleep(0.2)
end
rednet.send(configHostID, {msg = ConfigMsgType.SendID}, "ryry.quarry.radial.config")
while true do
local senderID, msg = rednet.receive("ryry.quarry.radial.config", 60 * 5)
if senderID == nil then
print("Config attempt timed out")
return nil
elseif senderID == configHostID and msg.msg == ConfigMsgType.SendingID then
if msg.val == quarryID then
rednet.send(configHostID, {msg = ConfigMsgType.SetupLevel, val = os.getComputerID()}, "ryry.quarry.radial.config")
end
elseif senderID == configHostID and msg.msg == ConfigMsgType.SendingConfig then
local config = msg.val
settings.set("local.receiverCompID", config.receiverID)
settings.save(SettingFile)
break
end
end
rednet.close()
end
if mode == "receiver" then
receiver()
elseif mode == "rotation" then
rotationSetup()
elseif mode == "level" then
levelSetup()
else
return usage()
end
-- install the code
--
--- GIST Fetch and Install
---
local CWD = fs.getDir(shell.getRunningProgram())
local function installFile(url, path)
print("Downloading", url, "to", path)
local req, reqErr = http.get(url)
if req == nil then
print("Failed to fetch " .. url .. ":", reqErr)
return nil
end
local res = req.readAll()
req.close()
local file, fileErr = fs.open(path, "w")
if file == nil then
print("Failed to open " .. path ..":", fileErr)
return nil
end
file.write(res)
file.close()
end
local function getLatestGist(gist)
assert( not not http, "HTTP not available")
local apiEndpoint = "https://api.github.com/gists/" .. gist .. "/commits"
print("Querying " .. apiEndpoint)
local req = http.get(apiEndpoint)
if req == nil then return nil end
local res = req.readAll()
req.close()
local versions = textutils.unserializeJSON(res)
local datePattern = "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z"
local printVers = {}
local max, maxTimestamp = nil, nil
for i, ver in ipairs(versions) do
local y, mon, d, h, min, s = ver.committed_at:match(datePattern)
local timestampt = {year = tonumber(y), month = tonumber(mon), day = tonumber(d), hour = tonumber(h), min = tonumber(min), sec = tonumber(s), isdst = false}
local timestamp = os.time(timestampt)
if max == nil or maxTimestamp < timestamp then
max = i
maxTimestamp = timestamp
end
-- printVers[i] = {i, ver.version:sub(1, 7), os.date("%Y/%m/%d - %T", timestamp)}
printVers[i] = {i, ver.version:sub(1, 7)}
end
textutils.pagedTabulate(colors.lightBlue, {"Version", "Hash"}, colors.orange, table.unpack(printVers))
if max ~= nil then
print("Latest Version:", max, "Hash:", versions[max].version:sub(1, 7), ", Released At:", os.date("%Y/%m/%d - %T", maxTimestamp))
print("Querying " .. versions[max].url)
local gistReq = http.get(versions[max].url)
if gistReq == nil then return nil end
local gistRes = gistReq.readAll()
gistReq.close()
local gist = textutils.unserializeJSON(gistRes)
return gist
end
return nil
end
local function installGistFiles(files)
for _, file in pairs(files) do
installFile(file.raw_url, fs.combine(CWD, file.filename))
end
end
local latest = getLatestGist("20f35ef69539949a5b83b5da5387d57c")
if latest ~= nil then
installGistFiles(latest.files)
else
print("Invalid Gist or no latest version!")
end
-- main controll
local CWD = fs.getDir(shell.getRunningProgram())
local SettingFile = fs.combine(CWD, "settings.cfg")
local QuarryMsgType = require("types")
settings.load(SettingFile)
settings.define("local.quarryID", {
description = "local Quarry ID",
type = "string",
})
settings.define("local.rotationCompID", {
description = "rotation client ID",
type = "number",
})
settings.define("local.levelCompID", {
description = "level client ID",
type = "number",
})
settings.define("local.clutchSide", {
description = "redsone side for clutch",
type = "string"
})
settings.define("local.gantrySide", {
description = "redstone side for gantry shaft",
type = "string"
})
settings.save(SettingFile)
peripheral.find("modem", rednet.open)
local quarryID = settings.get("local.quarryID")
local clutchSide = settings.get("local.clutchSide")
local gantrySide = settings.get("local.gantrySide")
if clutchSide == nil then return print("Set the redstone contact side with 'set local.clutchSide <side>'") end
if gantrySide == nil then return print("Set the redstone piston side with 'set local.gantrySide <side>'") end
local rotationCompID = settings.get("local.rotationCompID")
local levelCompID = settings.get("levelCompID")
if rotationCompID == nil or levelCompID == nil or quarryID == nil then
return print("Run the config with `config receiver <ID>`")
end
local rotationState
local rotationCount
local levelState
local function watchState()
while true do
---@type number
local senderID
---@type QuarryMsg
local msg
senderID, msg = rednet.receive("ryry.guarry.radial.state")
if senderID ~= nil and msg.id ~= nil and msg.id == quarryID then
if senderID == levelCompID and msg.msg == QuarryMsgType.LevelStateChange then
levelState = msg.state.new
elseif senderID == rotationCompID and msg.msg == QuarryMsgType.RotationStateChange then
if not msg.state.old and msg.state.new then
rotationCount = rotationCount + 1
end
rotationState = msg.state.new
end
end
end
end
local function moveDown()
redstone.setOutput(clutchSide, true)
sleep(0.1)
redstone.setOutput(gantrySide, true)
sleep(0.1)
redstone.setOutput(clutchSide, false)
while not levelState do
sleep(0.05)
end
redstone.setOutput(gantrySide, false)
end
local function controlLoop()
while true do
if rotationCount >=2 and levelState ~= nil and not levelState then
rotationCount = 0
moveDown()
end
sleep(0.05)
end
end
parallel.waitForAll(watchState, controlLoop)
-- level controll
local CWD = fs.getDir(shell.getRunningProgram())
local SettingFile = fs.combine(CWD, "settings.cfg")
local QuarryMsgType = require("types")
settings.load(SettingFile)
settings.define("local.quarryID", {
description = "local Quarry ID",
type = "string",
})
settings.define("local.receiverCompID", {
description = "receiver host ID",
type = "number"
})
settings.define("local.contactSide", {
description = "redstone contact side",
type = "string",
})
settings.save(SettingFile)
peripheral.find("modem", rednet.open)
local quarryID = settings.get("local.quarryID")
local contactSide = settings.get("local.contactSide")
if contactSide == nil then return print("Set the redstone contact side with 'set local.contactSide <side>'") end
local receiverCompID = settings.get("local.receiverCompID")
if receiverCompID == nil or receiverCompID < 0 or quarryID == nil then
return print("Run the config with `config level <ID>`")
end
local state = nil
while true do
local newState = redstone.getInput(contactSide)
if state ~= newState then
rednet.send(
receiverCompID,
{
msg = QuarryMsgType.LevelStateChange,
id = quarryID,
state = {
old = state,
new = newState
},
},
"ryry.quarry.radial.state"
)
state = newState
end
sleep(0.05)
end
-- rotation control
--
local CWD = fs.getDir(shell.getRunningProgram())
local SettingFile = fs.combine(CWD, "settings.cfg")
local QuarryMsgType = require("types")
settings.load(SettingFile)
settings.define("local.quarryID", {
description = "local Quarry ID",
type = "string",
})
settings.define("local.receiverCompID", {
description = "receiver host ID",
type = "number"
})
settings.define("local.pistonSide", {
description = "redstone piston side",
type = "string"
})
settings.define("local.levelSide", {
description = "redstone level contact side",
type = "string"
})
settings.define("local.contactSide", {
description = "redstone contact side",
type = "string",
})
settings.save(SettingFile)
peripheral.find("modem", rednet.open)
local quarryID = settings.get("local.quarryID")
local contactSide = settings.get("local.contactSide")
local pistonSide = settings.get("local.pistonSide")
local levelSide = settings.get("local.levelSide")
if contactSide == nil then return print("Set the redstone contact side with 'set local.contactSide <side>'") end
if pistonSide == nil then return print("Set the redstone piston side with 'set local.pistonSide <side>'") end
if levelSide == nil then return print("Set the level redstone contact side with 'set local.levelSide <side>'") end
local receiverCompID = settings.get("local.receiverCompID")
if receiverCompID == nil or receiverCompID < 0 or quarryID == nil then
return print("Run the config with `config level <ID>`")
end
local state = nil
local function watchRotation()
while true do
local newState = redstone.getInput(contactSide)
if state ~= newState then
rednet.send(
receiverCompID,
{
msg = QuarryMsgType.RotationStateChange,
id = quarryID,
state = {
old = state,
new = newState,
},
},
"ryry.quarry.radial.state"
)
state = newState
end
sleep(0.05)
end
end
local function watchLevel()
while true do
local levelState = redstone.getInput(levelSide)
if levelState then
redstone.setOutput(pistonSide, true)
sleep(3)
redstone.setOutput(pistonSide, false)
sleep(3)
end
sleep(0.05)
end
end
parallel.waitForAll(watchRotation, watchLevel)
---@enum QuarryMsgType
local QuarryMsgType = {
LevelStateChange = 0,
RotationStateChange = 1,
}
---@class RedstoneState
---@field old boolean
---@field new boolean
---@class QuarryMsg
---@field msg QuarryMsgType
---@field id string
---@field state RedstoneState
return QuarryMsgType
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment