Created
January 25, 2024 18:50
-
-
Save Unixkitty/de7bdfa57fdcb32c7e5c825e1620148a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
1. getStargateType | |
2. setChevronConfiguration | |
3. getRotation | |
4. setRestrictNetwork | |
5. getRecentFeedback | |
6. endRotation | |
7. getLocalAddress | |
8. getEnergy | |
9. disconnectStargate | |
10. getNetwork | |
11. raiseChevron | |
12. engageSymbol | |
13. isCurrentSymbol | |
14. getRestrictNetwork | |
15. getEnergyTarget | |
16. getCurrentSymbol | |
17. setEnergyTarget | |
18. encodeChevron | |
19. getStargateEnergy | |
20. getOpenTime | |
21. isStargateConnected | |
22. getConnectedAddress | |
23. getChevronsEngaged | |
24. setNetwork | |
25. getStargateGeneration | |
26. rotateClockwise | |
27. getDialedAddress | |
28. rotateAntiClockwise | |
29. lowerChevron | |
------------------------------------- | |
TODO | |
- Begin on commands: | |
- help | |
- help <command> | |
]]-- | |
-- Init | |
program_name = "StargateOS" | |
program_version = "0.5.2" | |
program_name_text = program_name .. " v" .. program_version | |
stargate = peripheral.find("advanced_crystal_interface") or error("Stargate interface not found!") | |
monitor = peripheral.wrap("top") or error("Place a monitor on top of this computer") | |
chevronMonitor = peripheral.find("monitor") or error("Connect an additional monitor via network cable") | |
chevronWindows = {} | |
gateMaxOpenTimeSeconds = 2280 | |
knownAddresses = { | |
ABYDOS = { 26, 6, 14, 31, 11, 29 }, | |
XEN = { 13, 24, 2, 19, 3, 30 }, | |
XEN_PEGASUS = { 14, 30, 6, 13, 17, 23 }, | |
CHULAK = { 8, 1, 22, 14, 36, 19 }, | |
P3W_451 = { 18, 7, 3, 36, 25, 15 } | |
} | |
monitor.setTextScale(0.5) | |
chevronMonitor.setTextScale(0.5) | |
termWidth, termHeight = term.getSize() | |
monitorWidth, monitorHeight = monitor.getSize() | |
chevronMonitorWidth, chevronMonitorHeight = chevronMonitor.getSize() | |
statusWindow = window.create(term.current(), 1, 1, termWidth, 1, true) | |
mainWindow = window.create(term.current(), 1, 2, termWidth, termHeight - 1, true) | |
monitorStatusWindow = window.create(monitor, (monitorWidth / 2) - 4, 2, 11, 1, true) | |
monitorEnergyWindow = window.create(monitor, 1, 4, monitorWidth, 1, true) | |
stargateEnergy = 0 | |
stargateEnergyRate = 0 | |
stargateStatuses = { IDLE = "IDLE", DIALING = "DIALING", CONNECTED = "CONNECTED" } | |
stargateStatus = stargateStatuses.IDLE | |
lastStargateStatus = stargateStatus | |
incomingWormhole = false | |
stargateRotation = stargate.getRotation() | |
speedDialActive = false | |
-- Log file functions | |
function getFreshLogCreatedEntry() | |
return "Creating log file at: " .. os.date() | |
end | |
-- [2024-01-19 15:54:54] [INFO/ERROR] [function name]: <message> | |
function writeLogEntry(fileObject, level, functionName, message) | |
fileObject.writeLine("[" .. os.date("%F %T") .. "] [" .. functionName .. "] [" .. level .. "]: " .. message) | |
end | |
function _log(logLevel, functionName, messageText) | |
local logfile_name = "/" .. program_name .. ".log" | |
local logfile_enabled = true | |
local error_only = false | |
if logfile_enabled == true and not fs.exists(logfile_name) then | |
local file = fs.open(logfile_name, "w") | |
file.writeLine(getFreshLogCreatedEntry()) | |
file.close() | |
end | |
if logLevel == "ERROR" then | |
if logfile_enabled == true then | |
local file = fs.open(logfile_name, "a") | |
writeLogEntry(file, logLevel, functionName, messageText) | |
file.close() | |
end | |
else | |
if logfile_enabled == true and error_only == false then | |
if not fs.exists(logfile_name) then | |
local file = fs.open(logfile_name, "w") | |
file.writeLine(getFreshLogCreatedEntry()) | |
file.close() | |
end | |
local file = fs.open(logfile_name, "a") | |
writeLogEntry(file, logLevel, functionName, messageText) | |
file.close() | |
end | |
end | |
end | |
function log(functionName, messageText) | |
_log("INFO", functionName, messageText) | |
end | |
function logError(functionName, messageText) | |
_log("ERROR", functionName, messageText) | |
end | |
--Functions | |
function cleanMainWindow() | |
mainWindow.clear() | |
mainWindow.setCursorPos(1, termHeight - 1) | |
end | |
function makeChevronWindow(number) | |
local w = window.create(chevronMonitor, chevronMonitorWidth - 3, number, 4, 1, true) | |
w.setBackgroundColor(colors.gray) | |
w.clear() | |
return w | |
end | |
function updateStatus(status) | |
lastStargateStatus = stargateStatus | |
stargateStatus = status | |
if lastStargateStatus ~= stargateStatus then | |
log("updateStatus", "Status change: " .. lastStargateStatus .. " > " .. stargateStatus) | |
-- Clean lower half of the main monitor on status change | |
for i = 6, monitorHeight, 1 | |
do | |
monitor.setCursorPos(1, i) | |
monitor.clearLine() | |
end | |
end | |
end | |
function updateChevronDisplay(chevron, symbol) | |
chevronWindows[chevron].setCursorPos(4 - string.len(symbol), 1) | |
chevronWindows[chevron].clear() | |
chevronWindows[chevron].write(symbol) | |
end | |
function resetChevronDisplay(address) | |
for chevron = 1, 9, 1 | |
do | |
if address[chevron] == nil then | |
updateChevronDisplay(chevron, "-") | |
else | |
updateChevronDisplay(chevron, address[chevron]) | |
end | |
end | |
end | |
function getCenterTextStart(width, textLength) | |
return ((width / 2) - (textLength / 2)) + 1 | |
end | |
function updateMonitorLine(line, text, centered) | |
local x = 1 | |
if centered then | |
x = getCenterTextStart(monitorWidth, #text) | |
end | |
monitor.setCursorPos(x, line) | |
monitor.clearLine() | |
monitor.write(text) | |
end | |
function setConnectedToText(text) | |
chevronWindows[10].clear() | |
chevronWindows[10].setCursorPos(getCenterTextStart(chevronMonitorWidth, #text), 1) | |
chevronWindows[10].write(text) | |
end | |
-- Continue init | |
log("init", "Starting up " .. program_name_text) | |
term.clear() | |
term.setCursorPos(1, 1) | |
monitor.setPaletteColor(colors.lightBlue, 0x0A1E36) | |
monitor.setBackgroundColor(colors.lightBlue) | |
monitor.clear() | |
monitor.setCursorPos(1, 1) | |
chevronMonitor.setPaletteColor(colors.lightBlue, 0x0A1E36) | |
chevronMonitor.setBackgroundColor(colors.lightBlue) | |
chevronMonitor.clear() | |
chevronMonitor.setCursorPos(1, 1) | |
statusWindow.setBackgroundColor(colors.blue) | |
statusWindow.clear() | |
statusWindow.setCursorPos(1, 1) | |
rootWindow = term.redirect(statusWindow) | |
--TODO switch back | |
textutils.slowWrite(program_name_text .. " initializing...") | |
--statusWindow.write(program_name_text .. " initializing...") | |
term.redirect(rootWindow) | |
for chevron = 1, 9, 1 | |
do | |
chevronMonitor.setCursorPos(1, chevron) | |
chevronMonitor.write("Chevron " .. chevron .. ": ") | |
table.insert(chevronWindows, makeChevronWindow(chevron)) | |
end | |
resetChevronDisplay(stargate.getConnectedAddress()) | |
table.insert(chevronWindows, window.create(chevronMonitor, 1, 10, chevronMonitorWidth, 1, true)) | |
chevronWindows[10].setBackgroundColor(colors.gray) | |
setConnectedToText("-") | |
statusWindow.clear() | |
statusWindow.setCursorPos(1, 1) | |
statusWindow.write(program_name_text) | |
mainWindow.setPaletteColor(colors.lightBlue, 0x0A1E36) | |
mainWindow.setBackgroundColor(colors.lightBlue) | |
cleanMainWindow() | |
--Begin program | |
function updateMonitor() | |
updateMonitorLine(1, "Status:", true) | |
updateMonitorLine(3, "Energy:", true) | |
for i = 1, monitorWidth, 1 | |
do | |
monitor.setCursorPos(i, 5) | |
monitor.write("=") | |
end | |
monitorStatusWindow.setBackgroundColor(colors.gray) | |
monitorStatusWindow.clear() | |
monitorEnergyWindow.setBackgroundColor(colors.gray) | |
monitorEnergyWindow.clear() | |
while (true) | |
do | |
monitorStatusWindow.setCursorPos(((monitorWidth / 2) - (#stargateStatus / 2)) - 1, 1) | |
monitorStatusWindow.clear() | |
if stargateStatus == stargateStatuses.IDLE then | |
monitorStatusWindow.setTextColor(colors.green) | |
elseif stargateStatus == stargateStatuses.DIALING then | |
monitorStatusWindow.setTextColor(colors.yellow) | |
elseif stargateStatus == stargateStatuses.CONNECTED then | |
monitorStatusWindow.setTextColor(colors.red) | |
else | |
monitorStatusWindow.setTextColor(colors.purple) | |
end | |
monitorStatusWindow.write(stargateStatus) | |
monitorEnergyWindow.setCursorPos(1, 1) | |
monitorEnergyWindow.clear() | |
monitorEnergyWindow.write(stargateEnergy .. " RF") | |
sleep(1) | |
end | |
end | |
function updateMonitorSecondary() | |
while (true) | |
do | |
if stargateStatus == stargateStatuses.DIALING then | |
updateMonitorLine(6, "Rotation: " .. stargateRotation .. "°", false) | |
elseif stargateStatus == stargateStatuses.CONNECTED then | |
updateMonitorLine(7, math.ceil(stargate.getOpenTime() / 20) .. "s / " .. gateMaxOpenTimeSeconds .. "s", false) | |
updateMonitorLine(8, stargateEnergyRate .. " RF/t", false) | |
end | |
sleep(1) | |
end | |
end | |
function updateChevronMonitor() | |
while (true) | |
do | |
-- Pinging the display to avoid it being black on chunk reload | |
chevronMonitor.setCursorPos(1, 1) | |
chevronMonitor.write("C") | |
sleep(3) | |
end | |
end | |
function updateEnergy() | |
local lastStargateEnergy = stargateEnergy | |
while (true) | |
do | |
stargateEnergy = stargate.getStargateEnergy() | |
stargateEnergyRate = stargateEnergy - lastStargateEnergy | |
lastStargateEnergy = stargateEnergy | |
sleep(0.05) | |
end | |
end | |
-- EVENTS | |
function watchChevrons() | |
while (true) | |
do | |
local _, chevron, incoming, symbol = os.pullEvent("stargate_chevron_engaged") | |
incomingWormhole = incoming | |
local incomingText = "outgoing" | |
if incoming then | |
incomingText = "incoming" | |
end | |
log("watchChevrons", "Chevron " .. chevron .. " encoded with symbol " .. symbol .. " by an " .. incomingText .. " connection") | |
updateChevronDisplay(chevron, symbol) | |
updateStatus(stargateStatuses.DIALING) | |
end | |
end | |
function watchDisconnects() | |
while (true) | |
do | |
local _, feedback = os.pullEvent("stargate_disconnected") | |
local result = "in an unknown manner" | |
if feedback == 7 then | |
result = "manually" | |
elseif feedback == 8 then | |
result = "by point of origin" | |
elseif feedback == 9 then | |
result = "by network" | |
elseif feedback == 10 then | |
result = "automatically" | |
end | |
log("watchDisconnects", "Existing connection was closed " .. result .. " (code " .. feedback .. ")") | |
updateStatus(stargateStatuses.IDLE) | |
end | |
end | |
function lookupAddress(searchAddress) | |
for name, address in pairs(knownAddresses) | |
do | |
local isMatch = true | |
for i, value in ipairs(address) | |
do | |
if value ~= searchAddress[i] then | |
isMatch = false | |
break | |
end | |
end | |
if isMatch then | |
return name | |
end | |
end | |
return "UNKNOWN ADDRESS" | |
end | |
function setConnectedStatus(address, incoming) | |
local typeText = "OUTGOING" | |
if incoming then | |
typeText = "INCOMING" | |
end | |
updateStatus(stargateStatuses.CONNECTED) | |
updateMonitorLine(6, "Type: " .. typeText, false) | |
local connectedTo = lookupAddress(address) | |
setConnectedToText(connectedTo) | |
log("setConnectionStatus", typeText .. " connection established with: -" .. table.concat(address, "-") .. "- " .. connectedTo) | |
end | |
function watchIncoming() | |
while (true) | |
do | |
local _, address = os.pullEvent("stargate_incoming_wormhole") | |
setConnectedStatus(address, true) | |
end | |
end | |
function watchOutgoing() | |
while (true) | |
do | |
local _, address = os.pullEvent("stargate_outgoing_wormhole") | |
setConnectedStatus(address, false) | |
end | |
end | |
function cleanUp() | |
local e = os.pullEventRaw("terminate") | |
if e == "terminate" then | |
term.redirect(mainWindow) | |
print("Shutting down " .. program_name_text) | |
log("cleanUp", "Shutting down " .. program_name) | |
monitor.setBackgroundColor(colors.black) | |
monitor.clear() | |
chevronMonitor.setBackgroundColor(colors.black) | |
chevronMonitor.clear() | |
term.redirect(rootWindow) | |
term.clear() | |
error("Goodbye") | |
else | |
error("How did we get here?") | |
end | |
end | |
function handleEvents() | |
parallel.waitForAll(watchChevrons, watchDisconnects, watchIncoming, watchOutgoing) | |
end | |
function updateDial() | |
local lastAddress = stargate.getConnectedAddress() | |
local lastRotation = stargate.getRotation() | |
resetStargate() | |
while (true) | |
do | |
local address = stargate.getConnectedAddress() | |
stargateRotation = stargate.getRotation() | |
if #address == 0 and #lastAddress > 0 then | |
resetChevronDisplay(address) | |
updateStatus(stargateStatuses.IDLE) | |
setConnectedToText("-") | |
end | |
if stargateRotation ~= lastRotation then | |
updateStatus(stargateStatuses.DIALING) | |
end | |
lastAddress = address | |
lastRotation = stargateRotation | |
sleep(1) | |
end | |
end | |
function updateClock() | |
local timeWindow = window.create(statusWindow, termWidth - 9, 1, 9, 1, true) | |
timeWindow.setBackgroundColor(colors.blue) | |
while (true) | |
do | |
timeWindow.setCursorPos(1, 1) | |
timeWindow.clear() | |
timeWindow.write(textutils.formatTime(os.time())) | |
sleep(1) | |
end | |
end | |
-- Gate operation functions | |
function resetStargate() | |
stargate.disconnectStargate() | |
end | |
function fastEncodeSymbol(symbol) | |
end | |
function slowEncodeSymbol(chevron, symbol) | |
if chevron % 2 == 0 then | |
print("Rotating the ring clockwise") | |
stargate.rotateClockwise(symbol) | |
else | |
print("Rotating the ring counter-clockwise") | |
stargate.rotateAntiClockwise(symbol) | |
end | |
--Wait for the current rotation to complete | |
while(not stargate.isCurrentSymbol(symbol)) | |
do | |
sleep(0) | |
end | |
print("Rotation complete") | |
--Grace delays when controlling chevrons | |
sleep(1) | |
print("Encoding chevron " .. chevron) | |
stargate.raiseChevron() | |
sleep(1) | |
stargate.lowerChevron() | |
sleep(1) | |
print("Chevron " .. chevron .. " encoded with: " .. symbol) | |
sleep(0.05) | |
end | |
function dialGate(address) | |
-- TODO dialing sequence code | |
print("Dialing -" .. table.concat(address, "-") .. "- " .. lookupAddress(address)) | |
log("dialGate", "Initiating dialing sequence with address: " .. table.concat(address, ",")) | |
resetStargate() | |
for chevron = 1,#address,1 do | |
local symbol = address[chevron] | |
if symbol == 0 then | |
print("Unexpected point of origin in address. Aborting.") | |
logError("dialGate", "Unexpected point of origin in address. Aborting.") | |
resetStargate() | |
return | |
end | |
if speedDialActive then | |
print("Encoding symbol: " .. symbol) | |
stargate.engageSymbol(symbol) | |
sleep(0.5) | |
else | |
slowEncodeSymbol(chevron, symbol) | |
end | |
end | |
if stargate.getChevronsEngaged() > 5 then | |
if speedDialActive then | |
print("Engaging Point of Origin") | |
stargate.engageSymbol(0) | |
else | |
slowEncodeSymbol(stargate.getChevronsEngaged() + 1, 0) | |
end | |
end | |
if speedDialActive then | |
speedDialActive = false | |
print("Speed dial: " .. tostring(speedDialActive)) | |
end | |
end | |
function dialGateByName(name) | |
for key, address in pairs(knownAddresses) do | |
if key == name then | |
dialGate(address) | |
return | |
end | |
end | |
print("Gate address not found for: " .. name .. "") | |
end | |
function dialGateAddress(address) | |
-- Check if the input matches the comma-separated format | |
local addressTable = {} | |
for numberStr in address:gmatch("%-?%d+") do | |
table.insert(addressTable, tonumber(numberStr)) | |
end | |
-- Check if the input matches the dash-surrounded format | |
if #addressTable == 0 then | |
for numberStr in address:gmatch("%-(%d+)%-") do | |
table.insert(addressTable, tonumber(numberStr)) | |
end | |
end | |
-- Validate the size of the address (between 6 and 8) | |
if #addressTable < 6 or #addressTable > 8 then | |
print("Invalid address format. Address must have between 6 and 8 glyphs.") | |
return | |
end | |
-- Remove any dashes from the addressTable | |
for i, value in ipairs(addressTable) do | |
addressTable[i] = math.abs(value) | |
end | |
-- Call dialGate with the parsed address | |
dialGate(addressTable) | |
end | |
-- Interactive console | |
function runConsole() | |
term.redirect(mainWindow) | |
mainWindow.setPaletteColor(colors.lightBlue, 0x0A1E36) | |
mainWindow.setBackgroundColor(colors.lightBlue) | |
cleanMainWindow() | |
log("runConsole", "Startup complete") | |
while (true) | |
do | |
mainWindow.write("> ") | |
mainWindow.setCursorBlink(true) | |
local input = io.read() | |
local command, args = string.match(input, "^(%S+)%s*(.*)") | |
sleep(0) | |
mainWindow.setCursorBlink(false) | |
log("runConsole", "\"" .. input .. "\" sent to command terminal") | |
if command == "help" then | |
if args == "" then | |
print("Available commands:") | |
--print("help <command>") | |
print("dial <NAME>") | |
print("dialAddress | dial_address <full address>") | |
--print("iris [ open | close ]") | |
print("speedDial [ true | false ]") | |
print("disconnect | stop | abort") | |
else | |
print("Help for '" .. args .. "'") | |
if args == "dial" then | |
print("\nExample:") | |
print("\t'dial ABYDOS'") | |
elseif args == "dialAddress" or args == "dial_address" then | |
print("Example:") | |
print("\tdialAddress 26,6,14,31,11,29") | |
print("\ndialAddress -26-6-14-31-11-29-") | |
elseif args == "speedDial" or args == "fastDial" then | |
print("Example:") | |
print("\tspeedDial true") | |
end | |
end | |
elseif command == "dial" then | |
if args == "" then | |
print("Known addresses for dialing by name:") | |
local keys = {} | |
for key in pairs(knownAddresses) do | |
table.insert(keys, key) | |
end | |
print(table.concat(keys, ", ")) | |
print("\nExample:") | |
print("\t'dial ABYDOS'") | |
else | |
dialGateByName(args) | |
end | |
elseif command == "dialAddress" or command == "dial_address" then | |
if args == "" then | |
print("Example:") | |
print("\tdialAddress 26,6,14,31,11,29") | |
print("\ndialAddress -26-6-14-31-11-29-") | |
else | |
dialGateAddress(args) | |
end | |
elseif command == "speedDial" or command == "fastDial" then | |
if args == "" then | |
speedDialActive = not speedDialActive | |
print("Speed dial: " .. tostring(speedDialActive)) | |
elseif args == "true" then | |
speedDialActive = true | |
print("Speed dial: " .. tostring(speedDialActive)) | |
elseif args == "false" then | |
speedDialActive = false | |
print("Speed dial: " .. tostring(speedDialActive)) | |
else | |
print("Example:") | |
print("\tspeedDial true") | |
end | |
elseif command == "disconnect" or command == "stop" or command == "abort" then | |
print("Resetting stargate...") | |
log("runConsole", "Resetting stargate") | |
resetStargate() | |
print("Stargate reset.") | |
else | |
print("Unknown command. Type 'help' for a list of available commands.") | |
end | |
end | |
end | |
parallel.waitForAll(cleanUp, updateMonitor, updateMonitorSecondary, updateChevronMonitor, updateClock, updateEnergy, updateDial, handleEvents, runConsole) |
Author
Unixkitty
commented
Jan 26, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment