883 lines
30 KiB
Lua
883 lines
30 KiB
Lua
--[[
|
|
Stargate Control System - Main Entry Point
|
|
For Stargate Journey Mod - AllTheMods 9
|
|
|
|
Comprehensive control program featuring:
|
|
- Full dialing system with touch screen interface
|
|
- Automatic iris control with security features
|
|
- Incoming/outgoing wormhole management
|
|
- Address book with categories
|
|
- Entity tracking and logging
|
|
- Whitelist/blacklist security
|
|
]]
|
|
|
|
---------------------------------------------
|
|
-- PROGRAM VERSION
|
|
---------------------------------------------
|
|
|
|
local PROGRAM_VERSION = "2.0"
|
|
|
|
---------------------------------------------
|
|
-- LOAD MODULES
|
|
---------------------------------------------
|
|
|
|
local config = require("config")
|
|
|
|
---------------------------------------------
|
|
-- AUTO-UPDATE SYSTEM
|
|
---------------------------------------------
|
|
|
|
local function autoUpdate()
|
|
if not (config.autoUpdate and http) then
|
|
return false
|
|
end
|
|
|
|
print("Checking for program updates...")
|
|
|
|
local filesToUpdate = {
|
|
"startup.lua",
|
|
"addresses.lua",
|
|
"utils.lua",
|
|
"display.lua",
|
|
"events.lua",
|
|
"handlers.lua"
|
|
}
|
|
|
|
local updated = false
|
|
|
|
for _, filename in ipairs(filesToUpdate) do
|
|
local url = config.repoBaseUrl .. filename
|
|
local response = http.get(url)
|
|
|
|
if response then
|
|
local newContent = response.readAll()
|
|
response.close()
|
|
|
|
-- Check if file exists and compare content
|
|
local needsUpdate = true
|
|
if fs.exists(filename) then
|
|
local file = fs.open(filename, "r")
|
|
if file then
|
|
local currentContent = file.readAll()
|
|
file.close()
|
|
|
|
-- Only update if content is different
|
|
if currentContent == newContent then
|
|
needsUpdate = false
|
|
print(" [SKIP] " .. filename .. " (unchanged)")
|
|
end
|
|
end
|
|
end
|
|
|
|
if needsUpdate then
|
|
-- Delete old file if it exists
|
|
if fs.exists(filename) then
|
|
fs.delete(filename)
|
|
end
|
|
|
|
local file = fs.open(filename, "w")
|
|
if file then
|
|
file.write(newContent)
|
|
file.close()
|
|
print(" [UPDATE] " .. filename)
|
|
updated = true
|
|
else
|
|
print(" [FAIL] Could not write " .. filename)
|
|
end
|
|
end
|
|
else
|
|
print(" [SKIP] Could not download " .. filename)
|
|
end
|
|
end
|
|
|
|
return updated
|
|
end
|
|
|
|
-- Perform auto-update
|
|
if autoUpdate() then
|
|
print("Program updated! Restarting...")
|
|
sleep(2)
|
|
os.reboot()
|
|
end
|
|
|
|
local addresses = require("addresses")
|
|
local utils = require("utils")
|
|
local display = require("display")
|
|
local events = require("events")
|
|
local handlers = require("handlers")
|
|
|
|
---------------------------------------------
|
|
-- INITIALIZATION
|
|
---------------------------------------------
|
|
|
|
-- Find and initialize peripherals
|
|
local mon = peripheral.find("monitor")
|
|
local gate = peripheral.find("advanced_crystal_interface")
|
|
or peripheral.find("crystal_interface")
|
|
or peripheral.find("basic_interface")
|
|
local transceiver = peripheral.find("transceiver")
|
|
local chatBox = peripheral.find("chatBox")
|
|
|
|
if gate == nil then
|
|
error("Stargate interface not found! Please connect an interface.")
|
|
end
|
|
|
|
if mon == nil then
|
|
error("Monitor not found! Please connect a monitor.")
|
|
end
|
|
|
|
if transceiver == nil then
|
|
print("WARNING: No transceiver found. Remote iris detection and GDO support disabled.")
|
|
end
|
|
|
|
if chatBox == nil and config.enableChatboxDebug then
|
|
print("WARNING: No chatbox found. Debug messages disabled.")
|
|
config.enableChatboxDebug = false
|
|
end
|
|
|
|
-- Check gate type and iris availability
|
|
local gateType = gate.getStargateType()
|
|
|
|
-- Tollan gates don't have iris support and will crash if checked
|
|
if gateType == "sgjourney:tollan_stargate" then
|
|
print("Tollan Stargate detected - iris functionality disabled")
|
|
config.irisEnabled = false
|
|
elseif gate.getIris() == nil then
|
|
print("Config has Iris enabled, but there is no iris! Disabling Iris")
|
|
config.irisEnabled = false
|
|
end
|
|
|
|
-- Get local gate address
|
|
local localGateAddress = gate.getLocalAddress()
|
|
if localGateAddress then
|
|
print("Local gate address: " .. gate.addressToString(localGateAddress))
|
|
else
|
|
print("WARNING: Could not read local gate address")
|
|
end
|
|
|
|
-- Initialize modules
|
|
utils.init(config, gate, chatBox)
|
|
display.init(mon, config, addresses, utils, localGateAddress)
|
|
handlers.init(config, gate, mon, utils, display, events, transceiver)
|
|
|
|
-- Configure transceiver IDC after utils is initialized (so we can use utils.log)
|
|
if transceiver and config.enableGDO and config.irisPassword then
|
|
local idcCode = config.irisPassword
|
|
if idcCode then
|
|
transceiver.setCurrentCode(idcCode)
|
|
utils.log("Transceiver IDC configured to: " .. idcCode)
|
|
else
|
|
print("WARNING: irisPassword must be a number for GDO support")
|
|
end
|
|
end
|
|
|
|
-- Ensure gate starts disconnected
|
|
gate.disconnectStargate()
|
|
|
|
-- Set iris to default state
|
|
if config.irisEnabled then
|
|
if config.irisClosedByDefault then
|
|
gate.closeIris()
|
|
else
|
|
gate.openIris()
|
|
end
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- GLOBAL STATE
|
|
---------------------------------------------
|
|
|
|
local dialing = false
|
|
local totalstate = nil
|
|
local disconnect = false
|
|
local selx, sely = 0, 0
|
|
local y = 0
|
|
|
|
-- Get shared state from handlers
|
|
local function getState()
|
|
return handlers.getState()
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- EVENT HANDLERS (Using centralized event system)
|
|
---------------------------------------------
|
|
|
|
local function GetClick()
|
|
mon.setTextScale(1)
|
|
local result, eventType, eventData = events.pullEvent("monitor_touch")
|
|
|
|
-- eventData is {eventType, side, x, y}
|
|
if result == "disconnect" then
|
|
return 0, 0
|
|
elseif type(result) == "table" and result.x and result.y then
|
|
return result.x, result.y
|
|
else
|
|
-- Fallback: extract from eventData
|
|
return eventData[3] or 0, eventData[4] or 0
|
|
end
|
|
end
|
|
|
|
local function GetActivation()
|
|
local result = events.pullEvent("stargate_incoming_wormhole")
|
|
return 1
|
|
end
|
|
|
|
local function ParaDisconnect()
|
|
-- Now handled by event system with priority
|
|
while true do
|
|
local result = events.pullEvent("monitor_touch")
|
|
if result == "disconnect" then
|
|
return 1
|
|
end
|
|
end
|
|
end
|
|
|
|
local function EntityRead()
|
|
sleep(0.1)
|
|
local result = events.pullEvent("stargate_reconstructing_entity")
|
|
return 1
|
|
end
|
|
|
|
local function DisconnectCheck()
|
|
local result = events.pullEvent("stargate_disconnected")
|
|
return 2
|
|
end
|
|
|
|
local function Paratimeout()
|
|
sleep(300)
|
|
return 2
|
|
end
|
|
|
|
local function GetMessage()
|
|
local result = events.pullEvent("stargate_message_received")
|
|
return 1
|
|
end
|
|
|
|
local function GetGDOTransmission()
|
|
local result = events.pullEvent("transceiver_transmission_received")
|
|
return 2
|
|
end
|
|
|
|
local function HandlePasswordEntry()
|
|
local result = handlers.handlePasswordInput()
|
|
return result
|
|
end
|
|
|
|
local function HandleIncomingPasswordRequest(password)
|
|
-- Check if password matches
|
|
if config.irisPassword and password == config.irisPassword then
|
|
utils.log("Correct password received, opening iris")
|
|
utils.openIris()
|
|
utils.sendPasswordResponse(true)
|
|
return true
|
|
else
|
|
utils.log("Incorrect password received")
|
|
utils.sendPasswordResponse(false)
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function MonitorRemoteIris()
|
|
-- Continuously monitor remote iris state while connected
|
|
-- Password handling is done during initial connection only
|
|
local lastIrisState = nil
|
|
local state = getState()
|
|
|
|
while gate.isStargateConnected() do
|
|
sleep(0.5) -- Check every half second
|
|
|
|
if transceiver then
|
|
local remoteIrisState = transceiver.checkConnectedShielding()
|
|
|
|
-- Only update display if state changed
|
|
if remoteIrisState ~= lastIrisState then
|
|
if remoteIrisState and remoteIrisState > 0 then
|
|
-- Remote iris closed or closing during active connection
|
|
if remoteIrisState == 100 then
|
|
utils.log("ALERT: Remote iris fully closed during connection!")
|
|
else
|
|
utils.log("ALERT: Remote iris moving: " .. remoteIrisState .. "%")
|
|
end
|
|
|
|
-- Show warning screen
|
|
mon.setBackgroundColor(colors.red)
|
|
mon.clear()
|
|
mon.setTextScale(1)
|
|
mon.setCursorPos(6, 5)
|
|
mon.write("REMOTE IRIS")
|
|
mon.setCursorPos(8, 7)
|
|
if remoteIrisState == 100 then
|
|
mon.write("CLOSED!")
|
|
else
|
|
mon.write(remoteIrisState .. "% CLOSED")
|
|
end
|
|
mon.setCursorPos(3, 10)
|
|
mon.write("Connection unsafe")
|
|
display.drawIrisStatus()
|
|
display.drawDisconnectButton()
|
|
else
|
|
-- Remote iris is open (0 or nil)
|
|
if lastIrisState and lastIrisState > 0 then
|
|
-- Iris just opened
|
|
utils.log("Remote iris opened - connection safe")
|
|
-- Open local iris now that connection is safe
|
|
if config.irisEnabled then
|
|
utils.openIris()
|
|
end
|
|
end
|
|
display.showConnected(state.destAddressname, state.destAddress)
|
|
end
|
|
|
|
lastIrisState = remoteIrisState
|
|
end
|
|
end
|
|
end
|
|
return 3 -- Return unique value to indicate iris monitoring ended
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- INCOMING WORMHOLE HANDLER
|
|
---------------------------------------------
|
|
|
|
local function findAddressName(address)
|
|
-- Search all address categories for matching address
|
|
local categories = { addresses.MainGates, addresses.playerGates, addresses.hazardGates }
|
|
|
|
for _, category in ipairs(categories) do
|
|
for _, entry in ipairs(category) do
|
|
local name, storedAddress = entry[1], entry[2]
|
|
-- Compare addresses (excluding point of origin)
|
|
if #address == #storedAddress then
|
|
local match = true
|
|
for i = 1, #address do
|
|
if address[i] ~= storedAddress[i] then
|
|
match = false
|
|
break
|
|
end
|
|
end
|
|
if match then
|
|
return name
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return nil -- Not found in address book
|
|
end
|
|
|
|
local function handleIncomingWormhole()
|
|
local state = getState()
|
|
|
|
-- Setup event handlers for this connection
|
|
handlers.setupConnectionHandlers()
|
|
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.red)
|
|
mon.setTextScale(1)
|
|
mon.setCursorPos(9, 4)
|
|
mon.write("INCOMING")
|
|
|
|
state.incomingAddress = utils.addressToTable(state.incomingAddress)
|
|
|
|
-- Check security
|
|
local allowed, reason = utils.isAddressAllowed(state.incomingAddress)
|
|
local addressString = gate.addressToString(state.incomingAddress) or "Unknown"
|
|
|
|
-- Look up address name in address book
|
|
local addressName = findAddressName(state.incomingAddress)
|
|
|
|
utils.log("Incoming wormhole from: " .. (addressName or addressString) .. " " .. reason)
|
|
|
|
-- Show incoming connection status
|
|
display.showIncoming(addressName, addressString, allowed, reason)
|
|
|
|
-- Send version message to remote gate
|
|
sleep(0.5) -- Brief delay to ensure connection is stable
|
|
utils.sendVersionMessage(PROGRAM_VERSION)
|
|
utils.debug("Sent: SGCS_V" .. PROGRAM_VERSION)
|
|
|
|
-- Handle iris
|
|
if config.autoCloseIrisOnIncoming then
|
|
sleep(config.irisCloseDelay)
|
|
if allowed then
|
|
-- Wait 2 seconds after connection established before opening iris
|
|
sleep(2)
|
|
utils.openIris()
|
|
else
|
|
utils.closeIris()
|
|
-- Send password request if iris is closed and password system is enabled
|
|
if config.irisPassword and config.enableMessaging then
|
|
utils.sendPasswordRequest()
|
|
utils.debug("Sent: IRIS_PASSWORD_REQUIRED")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Monitor for entities
|
|
disconnect = false
|
|
while (disconnect == false) do
|
|
-- parallel.waitForAny runs multiple functions at the same time and returns which one finished first
|
|
-- Here we're watching for things simultaneously:
|
|
-- 1 = EntityRead (someone/something came through the gate)
|
|
-- 2 = DisconnectCheck (gate disconnected on its own)
|
|
-- 3 = GetMessage (received a message from remote gate)
|
|
-- 4 = GetGDOTransmission (received GDO code)
|
|
-- ParaDisconnect (user clicked screen to manually disconnect)
|
|
-- Whichever happens first, that function returns and we handle it
|
|
local result = parallel.waitForAny(EntityRead, DisconnectCheck, GetMessage, GetGDOTransmission, ParaDisconnect)
|
|
if (result == 1) then
|
|
display.showEntity(state.incomingEntityType, state.incomingEntityName, allowed)
|
|
state.incomingEntityType = ""
|
|
state.incomingEntityName = ""
|
|
elseif (result == 3) then
|
|
-- Received a message
|
|
local message = state.lastReceivedMessage
|
|
utils.debug("Received: " .. tostring(message))
|
|
|
|
-- Check if it's a password attempt (don't log passwords in plaintext)
|
|
if message:sub(1, 14) == "IRIS_PASSWORD:" then
|
|
utils.log("Received password attempt")
|
|
local password = message:sub(15)
|
|
local passwordAccepted = HandleIncomingPasswordRequest(password)
|
|
if passwordAccepted then
|
|
-- Update allowed status so entities don't show as impacts
|
|
allowed = true
|
|
end
|
|
else
|
|
utils.log("Received message: " .. message)
|
|
end
|
|
state.lastReceivedMessage = nil
|
|
elseif (result == 4) then
|
|
-- GDO transmission received (already handled by event system)
|
|
utils.log("GDO transmission processed")
|
|
else
|
|
disconnect = true
|
|
end
|
|
end
|
|
disconnect = false
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- DIALING FUNCTIONS
|
|
---------------------------------------------
|
|
|
|
local function dialGate(address)
|
|
tmp = utils.deepcopy(address)
|
|
table.remove(tmp)
|
|
utils.log("Dialing: " .. gate.addressToString(tmp))
|
|
|
|
local gateType = gate.getStargateType()
|
|
|
|
-- Manual Milky Way dialing with ring rotation
|
|
if gateType == "sgjourney:milky_way_stargate" and config.manualDial == true then
|
|
local addressLength = #address
|
|
|
|
if addressLength == 8 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 6, 7, 8, 5 })
|
|
elseif addressLength == 9 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 5, 6, 7, 8 })
|
|
end
|
|
|
|
local start = gate.getChevronsEngaged() + 1
|
|
|
|
for chevron = start, addressLength, 1 do
|
|
local symbol = address[chevron]
|
|
|
|
if chevron % 2 == 0 then
|
|
gate.rotateClockwise(symbol)
|
|
else
|
|
gate.rotateAntiClockwise(symbol)
|
|
end
|
|
|
|
while (not gate.isCurrentSymbol(symbol)) do
|
|
sleep(0)
|
|
end
|
|
|
|
gate.openChevron()
|
|
sleep(1)
|
|
gate.closeChevron()
|
|
sleep(0.5)
|
|
|
|
display.showDialing(chevron, symbol, gateType)
|
|
end
|
|
else
|
|
-- Automatic dialing for other gate types
|
|
if gateType ~= "sgjourney:universe_stargate" then
|
|
local addressLength = #address
|
|
if addressLength == 8 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 6, 7, 8, 5 })
|
|
elseif addressLength == 9 then
|
|
gate.setChevronConfiguration({ 1, 2, 3, 4, 5, 6, 7, 8 })
|
|
end
|
|
end
|
|
|
|
local start = gate.getChevronsEngaged() + 1
|
|
|
|
for chevron = start, #address, 1 do
|
|
local symbol = address[chevron]
|
|
|
|
gate.engageSymbol(symbol)
|
|
sleep(config.gatespeed)
|
|
|
|
display.showDialing(chevron, symbol, gateType)
|
|
|
|
if (symbol) ~= 0 then
|
|
if (gateType == "sgjourney:universe_stargate") or (gateType == "sgjourney:pegasus_stargate") then
|
|
events.pullEvent("stargate_chevron_engaged")
|
|
end
|
|
else
|
|
if gateType == "sgjourney:universe_stargate" then
|
|
events.pullEvent("stargate_chevron_engaged")
|
|
redstone.setOutput("top", true)
|
|
elseif (gateType == "sgjourney:pegasus_stargate") then
|
|
events.pullEvent("stargate_chevron_engaged")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function selectGateFromList()
|
|
local state = getState()
|
|
local selecting = true
|
|
while dialing == false and selecting == true do
|
|
selx, sely = GetClick()
|
|
local buttonXY, computerAddresses, computerNames = display.getButtonData()
|
|
|
|
for i = 1, #buttonXY do
|
|
if (sely == buttonXY[i][3]) and ((selx >= buttonXY[i][1]) and (selx <= buttonXY[i][2])) then
|
|
dialGate(computerAddresses[i])
|
|
state.destAddressname = computerNames[i]
|
|
state.destAddress = computerAddresses[i]
|
|
dialing = true
|
|
sely = 0
|
|
selx = 0
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Check back button (x: 23-28, y: 17-19)
|
|
if not dialing and sely >= 17 and sely <= 19 and selx >= 23 and selx <= 28 then
|
|
selecting = false
|
|
sely = 0
|
|
selx = 0
|
|
end
|
|
end
|
|
|
|
display.clearButtonData()
|
|
return dialing
|
|
end
|
|
|
|
local function selectCategory()
|
|
local state = true
|
|
|
|
while state == true do
|
|
display.selectionTabs()
|
|
|
|
local tabx, taby = GetClick()
|
|
y = 2
|
|
local count = 0
|
|
|
|
if (taby >= 2) and (taby <= 6) and ((tabx >= 2) and (tabx <= 13)) then
|
|
if #addresses.MainGates ~= 0 then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.purple)
|
|
count, y = display.screenWrite(addresses.MainGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
elseif (taby >= 2) and (taby <= 6) and ((tabx >= 16) and (tabx <= 27)) then
|
|
if #addresses.playerGates ~= 0 then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.green)
|
|
count, y = display.screenWrite(addresses.playerGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
elseif (((taby >= 8) and (taby <= 12)) and ((tabx >= 2) and (tabx <= 13))) and (config.canAccessHazardGates == true) then
|
|
if (#addresses.hazardGates ~= 0) and (config.canAccessHazardGates == true) then
|
|
mon.setBackgroundColor(colors.black)
|
|
mon.clear()
|
|
mon.setBackgroundColor(colors.red)
|
|
count, y = display.screenWrite(addresses.hazardGates, count, y)
|
|
|
|
local returnstate = selectGateFromList()
|
|
if returnstate == true then
|
|
state = false
|
|
end
|
|
|
|
display.clearButtonData()
|
|
else
|
|
mon.setCursorPos(9, 7)
|
|
mon.write("no gates available")
|
|
sleep(5)
|
|
end
|
|
elseif (taby >= 17) and (tabx >= 23) then
|
|
state = false
|
|
totalstate = false
|
|
end
|
|
end
|
|
return 1
|
|
end
|
|
|
|
local function handleOutgoingDial()
|
|
local state = getState()
|
|
|
|
-- Setup event handlers for this connection
|
|
handlers.setupConnectionHandlers()
|
|
|
|
totalstate = true
|
|
local PDO = 0
|
|
PDO = parallel.waitForAny(selectCategory, Paratimeout)
|
|
|
|
if (PDO == 1) and totalstate == true then
|
|
sleep(1)
|
|
|
|
events.pullEvent("stargate_outgoing_wormhole")
|
|
|
|
-- Wait briefly for version message and password request from remote gate
|
|
state.remoteHasComputer = false
|
|
state.remotePasswordRequired = false
|
|
|
|
-- Collect messages for up to 2 seconds
|
|
local startTime = os.clock()
|
|
local lastMessageTime = startTime
|
|
while (os.clock() - startTime) < 2 and gate.isStargateConnected() do
|
|
local result = parallel.waitForAny(GetMessage, GetGDOTransmission, function()
|
|
sleep(0.3); return -1
|
|
end)
|
|
if result == 1 then
|
|
local message = state.lastReceivedMessage
|
|
state.lastReceivedMessage = nil
|
|
|
|
utils.debug("Received: " .. tostring(message))
|
|
|
|
if message and message:sub(1, 6) == "SGCS_V" then
|
|
state.remoteHasComputer = true
|
|
local version = message:sub(7)
|
|
utils.log("Remote gate has control system version " .. version)
|
|
lastMessageTime = os.clock()
|
|
elseif message == "IRIS_PASSWORD_REQUIRED" then
|
|
state.remotePasswordRequired = true
|
|
utils.log("Remote gate requires password for entry")
|
|
lastMessageTime = os.clock()
|
|
break -- Got password request, that's all we need
|
|
elseif message == "GDO_IRIS_OPEN" then
|
|
utils.log("Remote iris opened via GDO")
|
|
state.remotePasswordRequired = false
|
|
lastMessageTime = os.clock()
|
|
break -- Iris opened, no password needed
|
|
end
|
|
elseif result == 2 then
|
|
-- GDO transmission received (handled by event system)
|
|
utils.log("GDO transmission detected")
|
|
lastMessageTime = os.clock()
|
|
else
|
|
-- Timeout - if we got a version message, assume no password required
|
|
if state.remoteHasComputer and (os.clock() - lastMessageTime) > 0.5 then
|
|
break -- No more messages coming
|
|
elseif not state.remoteHasComputer and (os.clock() - startTime) > 1 then
|
|
break -- No computer at remote gate
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check if remote iris is closed (for safety, not password prompting)
|
|
local remoteIrisState = nil
|
|
if transceiver then
|
|
remoteIrisState = transceiver.checkConnectedShielding()
|
|
end
|
|
|
|
-- Close local iris if remote iris is closed (unsafe for travel)
|
|
if remoteIrisState and remoteIrisState > 0 then
|
|
if config.irisEnabled then
|
|
utils.closeIris()
|
|
end
|
|
if remoteIrisState == 100 then
|
|
utils.log("WARNING: Remote iris is fully closed!")
|
|
else
|
|
utils.log("WARNING: Remote iris is " .. remoteIrisState .. "% closed!")
|
|
end
|
|
end
|
|
|
|
local connectionSafe = false
|
|
|
|
-- If remote computer requested a password, show the prompt
|
|
if state.remotePasswordRequired then
|
|
utils.log("Password required by remote gate")
|
|
|
|
local result = HandlePasswordEntry()
|
|
|
|
-- Check if iris opened during password entry (via GDO)
|
|
if result == "IRIS_OPENED" then
|
|
connectionSafe = true
|
|
display.showPasswordResult(true)
|
|
sleep(1)
|
|
elseif result then
|
|
-- Password was entered, send it
|
|
utils.debug("Sent: IRIS_PASSWORD:" .. result)
|
|
|
|
-- Wait for response
|
|
local function WaitForResponse()
|
|
sleep(3)
|
|
return nil
|
|
end
|
|
|
|
local waitResult = parallel.waitForAny(GetMessage, WaitForResponse)
|
|
if waitResult == 1 then
|
|
local response = state.lastReceivedMessage
|
|
state.lastReceivedMessage = nil
|
|
if response == "IRIS_OPEN" or response == "GDO_IRIS_OPEN" then
|
|
display.showPasswordResult(true)
|
|
utils.log("Password accepted - iris opened")
|
|
sleep(1)
|
|
connectionSafe = true
|
|
elseif response == "IRIS_DENIED" then
|
|
display.showPasswordResult(false)
|
|
utils.log("Password rejected")
|
|
sleep(2)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Re-check remote iris state after password/GDO attempt
|
|
if transceiver then
|
|
remoteIrisState = transceiver.checkConnectedShielding()
|
|
if not remoteIrisState or remoteIrisState == 0 then
|
|
connectionSafe = true
|
|
end
|
|
end
|
|
|
|
-- If still not safe, show warning
|
|
if not connectionSafe then
|
|
mon.setBackgroundColor(colors.red)
|
|
mon.clear()
|
|
mon.setTextScale(1)
|
|
mon.setCursorPos(6, 5)
|
|
mon.write("REMOTE IRIS")
|
|
mon.setCursorPos(8, 7)
|
|
mon.write("CLOSED!")
|
|
mon.setCursorPos(3, 10)
|
|
mon.write("Connection unsafe")
|
|
display.drawIrisStatus()
|
|
display.drawDisconnectButton()
|
|
end
|
|
else
|
|
-- No password required - check if remote iris is open
|
|
if transceiver then
|
|
remoteIrisState = transceiver.checkConnectedShielding()
|
|
end
|
|
|
|
if not remoteIrisState or remoteIrisState == 0 then
|
|
-- Remote iris is open - connection is safe
|
|
connectionSafe = true
|
|
else
|
|
-- Remote iris is closed but no password system - show warning
|
|
utils.log("WARNING: Remote iris closed but no password system available")
|
|
mon.setBackgroundColor(colors.red)
|
|
mon.clear()
|
|
mon.setTextScale(1)
|
|
mon.setCursorPos(6, 5)
|
|
mon.write("REMOTE IRIS")
|
|
mon.setCursorPos(8, 7)
|
|
if remoteIrisState == 100 then
|
|
mon.write("CLOSED!")
|
|
else
|
|
mon.write(remoteIrisState .. "% CLOSED")
|
|
end
|
|
mon.setCursorPos(3, 10)
|
|
mon.write("Connection unsafe")
|
|
display.drawIrisStatus()
|
|
display.drawDisconnectButton()
|
|
end
|
|
end
|
|
|
|
utils.log("DEBUG: Before iris opening check - connectionSafe=" ..
|
|
tostring(connectionSafe) .. ", config.irisEnabled=" .. tostring(config.irisEnabled))
|
|
|
|
-- Only open local iris if connection is safe
|
|
if connectionSafe and config.irisEnabled then
|
|
utils.debug("Opening local iris NOW")
|
|
utils.log("Connection safe - opening local iris")
|
|
-- Wait 2 seconds to avoid voiding the iris
|
|
sleep(2)
|
|
utils.openIris()
|
|
display.showConnected(state.destAddressname, state.destAddress)
|
|
elseif connectionSafe then
|
|
utils.log("Connection safe - no iris to open")
|
|
display.showConnected(state.destAddressname, state.destAddress)
|
|
else
|
|
utils.log("Connection NOT safe - iris remains closed")
|
|
end
|
|
|
|
if (gate.isStargateConnected() == true) then
|
|
-- parallel.waitForAny runs functions at the same time, returns which finished first
|
|
-- While the wormhole is open, we wait for:
|
|
-- DisconnectCheck = gate disconnects naturally (timeout or remote disconnect)
|
|
-- ParaDisconnect = user manually clicks screen to disconnect
|
|
-- Paratimeout = safety timeout (5 minutes)
|
|
-- MonitorRemoteIris = continuously checks remote iris state
|
|
-- Whichever happens first ends the connection
|
|
PDO = parallel.waitForAny(DisconnectCheck, ParaDisconnect, Paratimeout, MonitorRemoteIris)
|
|
dialing = false
|
|
end
|
|
end
|
|
|
|
display.clearButtonData()
|
|
state.destAddress = {}
|
|
state.destAddressname = ""
|
|
state.remoteHasComputer = false
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- MAIN MENU
|
|
---------------------------------------------
|
|
|
|
local function mainMenu()
|
|
while true do
|
|
-- Setup basic handlers for main menu
|
|
handlers.setupConnectionHandlers()
|
|
|
|
display.showMainMenu()
|
|
local answer = parallel.waitForAny(GetClick, GetActivation)
|
|
|
|
if (answer == 1) then
|
|
handleOutgoingDial()
|
|
else
|
|
handleIncomingWormhole()
|
|
end
|
|
end
|
|
end
|
|
|
|
---------------------------------------------
|
|
-- STARTUP
|
|
---------------------------------------------
|
|
|
|
utils.log("=== Stargate Control System Starting ===")
|
|
utils.log("Gate Type: " .. gate.getStargateType())
|
|
utils.log("Iris Available: " .. tostring(config.irisEnabled))
|
|
|
|
-- Start the main menu
|
|
mainMenu()
|