-- ArcTurtleWatching PROGRAM
-- This program is intended to be used with my turtle programs (vertical mining and chunk loading)
-- The goal is to show the status of your turtles on a monitor and to send to players chat messages when a turtle has an error or has finished his work
-- The program uses multiple peripherals from Advanced Peripherals
-- - The chat box is used to send you chat messages
-- - The environment detector is used to show the dimension the turtle is in on the monitor
-- - The player detector is used to detect player connection and send them awaiting reports
-- You can connect every peripheral as you want, the program will detect them where they are
-- Every peripheral (except the wireless modem) can be absent, the program will ignore their specific uses
-- You can have up to 17 players to send message to
local ArcConst = require("ArcConstants")
local ArcTerm = require("ArcTerm")
local ArcMonitor = require("ArcMonitor")
local ArcUtilities = require("ArcUtilities")
-- GLOBAL VARIABLES
-- Peripherals
local gModem = nil -- Wrapped modem
local gMonitor = nil -- Wrapped monitor
local gChatBox = nil -- Wrapped chat box
local gEnvDetector = nil -- Wrapped environment detector
local gPlayerDetector = nil -- Wrapped player detector
-- Miscellaneous
local gsReportProtocol = "ATW" -- Report protocol code to receive reports
local gtabTurtleData = {} -- Contains all turtles data
local gnShowedTurtleIndex = 0 -- Index of the actual turtle showed on monitor
local gtabsPlayers = {} -- List of players to send reports to
local gsPlayersFileName = "Players.atw" -- Registered players file name
local gtabsFacing = {"north","east","south","west","unknown"}
-- FUNCTIONS
local function GetTurtleIndex(pnTurtleId)
for i=1,#gtabTurtleData,1 do
if gtabTurtleData[i][ArcConst.TDS_ID] == pnTurtleId then return i end
end
return nil
end
local function GetPlayerIndex(psPlayer)
for i=1,#gtabsPlayers,1 do
if gtabsPlayers[i] == psPlayer then return i end
end
return nil
end
local function SavePlayers()
local File = fs.open(gsPlayersFileName,"w+")
for i=1,#gtabsPlayers,1 do
File.writeLine(gtabsPlayers[i])
end
File.close()
end
local function LoadPlayers()
local File = nil
local sLine = nil
if not fs.exists(gsPlayersFileName) then return false end
File = fs.open(gsPlayersFileName,"r")
sLine = File.readLine()
while sLine ~= nil do
table.insert(gtabsPlayers,sLine)
sLine = File.readLine()
end
File.close()
end
local function RefreshMonitor()
local sDimension = nil
if gMonitor == nil then return false end
if gEnvDetector ~= nil then sDimension = ArcUtilities.StringExtract(gEnvDetector.getDimension(),2,":")
else sDimension = "unknown" end
ArcMonitor.Reset()
ArcMonitor.Write(1,1,"<========== TURTLE WATCHING ==========>")
if gnShowedTurtleIndex == 0 then
ArcMonitor.Write(15,9,"NO TURTLE")
ArcMonitor.Write(15,12,"DETECTED")
return true
end
ArcMonitor.Write(1,3,("Turtle type : %s"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_TYPE]))
ArcMonitor.Write(1,4,("Dimension : %s"):format(sDimension))
if gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_LOCATION] == 1 then
ArcMonitor.Write(1,5,("Position length : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSLX]))
ArcMonitor.Write(1,6,("Position width : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSWY]))
ArcMonitor.Write(1,7,("Position depth : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSDZ]))
ArcMonitor.Write(1,8,("Orientation : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSO]))
else
ArcMonitor.Write(1,5,("Position x : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSLX]))
ArcMonitor.Write(1,6,("Position y : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSWY]))
ArcMonitor.Write(1,7,("Position z : %d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSDZ]))
ArcMonitor.Write(1,8,("Facing : %s"):format(gtabsFacing[gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_POSO]]))
end
if gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_TYPE] == ArcConst.TT_VERTICALMINING then
ArcMonitor.Write(1,10,("Mine shaft : %d/%d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_ADVANCEMENT],gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_GOAL]))
else
ArcMonitor.Write(1,10,("Checkpoint : %d/%d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_ADVANCEMENT],gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_GOAL]))
end
ArcMonitor.Write(1,11,("Fuel level : %d/%d"):format(gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_FUELLEVEL],gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_FUELLIMIT]))
ArcMonitor.Write(1,13,"Current Status :")
ArcMonitor.Write(1,14,gtabTurtleData[gnShowedTurtleIndex][ArcConst.TDS_STATUS])
ArcMonitor.Write(20-string.len(gnShowedTurtleIndex),17,("%d/%d"):format(gnShowedTurtleIndex,#gtabTurtleData))
ArcMonitor.CreateButton(2,16,10,3,colors.lightGray)
ArcMonitor.Write(5,17,"Prev",colors.black,colors.lightGray)
ArcMonitor.CreateButton(29,16,10,3,colors.lightGray)
ArcMonitor.Write(32,17,"Next",colors.black,colors.lightGray)
end
local function GetPeripheralStatus(pPeripheral)
if pPeripheral == nil then return "not found" else return "running" end
end
local function Menu()
local nKey = nil
local bExit = nil
local sPlayer = nil
local nPlayerIndex = nil
while true do
while true do
ArcTerm.Reset()
ArcTerm.Write(1,1,"<================ TURTLE WATCHING ================>")
ArcTerm.Write(1,3,"-- Peripherals status --")
ArcTerm.Write(1,4,("Modem : %s"):format(GetPeripheralStatus(gModem)))
ArcTerm.Write(1,5,("Monitor : %s"):format(GetPeripheralStatus(gMonitor)))
ArcTerm.Write(1,6,("Chat box : %s"):format(GetPeripheralStatus(gChatBox)))
ArcTerm.Write(1,7,("Environment detector : %s"):format(GetPeripheralStatus(gEnvDetector)))
ArcTerm.Write(1,8,("Player detector : %s"):format(GetPeripheralStatus(gPlayerDetector)))
ArcTerm.Write(1,10,"Protocol code : ")
if gsReportProtocol == nil or gsReportProtocol == "" then gsReportProtocol = ArcTerm.Read(24,10);break
else ArcTerm.Write(24,10,gsReportProtocol) end
ArcTerm.Write(1,12,"- 1 | Manage players")
ArcTerm.Write(1,13,"- 2 | Change protocol code")
ArcTerm.Write(1,14,"- 3 | Reboot")
ArcTerm.Write(1,14,"- 4 | Update")
ArcTerm.Write(1,15,"- 5 | Shutdown")
bExit = false
nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
if nKey == 1 then
repeat
ArcTerm.Reset()
while true do
ArcTerm.Write(1,1,"<==================== PLAYERS ====================>")
for i=1,#gtabsPlayers,1 do
ArcTerm.Write(1,1+i,gtabsPlayers[i])
end
ArcTerm.Write(1,19,"1 | Add player - 2 | Remove player - 3 | Exit")
nKey = ArcUtilities.KeyToNumber(ArcTerm.ReadKey())
if nKey == 1 then
if #gtabsPlayers >= 17 then
ArcTerm.Write(1,19,ArcUtilities.Complete("Error : too many players"," ",51,"R",false))
os.sleep(2)
break
end
ArcTerm.Write(1,19,ArcUtilities.Complete("Player name : "," ",51,"R",false))
sPlayer = ArcTerm.Read(15,19)
if sPlayer == nil or sPlayer == "" then break end
nPlayerIndex = GetPlayerIndex(sPlayer)
if nPlayerIndex ~= nil then
ArcTerm.Write(1,19,ArcUtilities.Complete("Error : player already registered"," ",51,"R",false))
os.sleep(2)
break
end
table.insert(gtabsPlayers,sPlayer)
SavePlayers()
elseif nKey == 2 then
if #gtabsPlayers == 0 then
ArcTerm.Write(1,19,ArcUtilities.Complete("Error : no player registered"," ",51,"R",false))
os.sleep(2)
break
end
ArcTerm.Write(1,19,ArcUtilities.Complete("Player name : "," ",51,"R",false))
sPlayer = ArcTerm.Read(15,19)
if sPlayer == nil or sPlayer == "" then break end
nPlayerIndex = GetPlayerIndex(sPlayer)
if nPlayerIndex == nil then
ArcTerm.Write(1,19,ArcUtilities.Complete("Error : player not registered"," ",51,"R",false))
os.sleep(2)
break
end
table.remove(gtabsPlayers,nPlayerIndex)
SavePlayers()
elseif nKey == 3 then bExit = true end
break
end
until bExit
elseif nKey == 2 then
gsReportProtocol = nil
elseif nKey == 3 then
os.reboot()
elseif nKey == 4 then
os.reboot()
elseif nKey == 5 then
os.shutdown()
end
end
end
end
local function SendMessageToPlayer(pTurtleData,psPlayer)
if gChatBox == nil then return false end
gChatBox.sendMessageToPlayer(("Turtle #%d status update : %s"):format(pTurtleData[ArcConst.TDS_ID],string.lower(pTurtleData[ArcConst.TDS_STATUS])),psPlayer,gsReportProtocol.." report")
end
local function TurtleListen()
local nSenderId = nil
local sMessage = nil
local sProtocol = nil
local TurtleData = nil
local nTurtleIndex = nil
local bUpdate = nil
if gModem == nil then return false end
while true do
bUpdate = false
nSenderId,sMessage,sProtocol = rednet.receive(gsReportProtocol)
TurtleData = textutils.unserialise(sMessage.sTurtleData)
nTurtleIndex = GetTurtleIndex(TurtleData[ArcConst.TDS_ID])
if nTurtleIndex == nil then
if TurtleData[ArcConst.TDS_STATUSTYPE] ~= ArcConst.TST_SHUTDOWN then
bUpdate = true
table.insert(gtabTurtleData,TurtleData)
if #gtabTurtleData == 1 then gnShowedTurtleIndex = 1 end
RefreshMonitor()
end
else
if TurtleData[ArcConst.TDS_STATUSTYPE] == ArcConst.TST_SHUTDOWN then
table.remove(gtabTurtleData,nTurtleIndex)
if gnShowedTurtleIndex > #gtabTurtleData then
gnShowedTurtleIndex = #gtabTurtleData
end
RefreshMonitor()
else
for i=1,#TurtleData,1 do
if gtabTurtleData[nTurtleIndex][i] ~= TurtleData[i] then bUpdate = true end
end
if bUpdate then
for i=1,#TurtleData,1 do
gtabTurtleData[nTurtleIndex][i] = TurtleData[i]
end
if gnShowedTurtleIndex == nTurtleIndex then RefreshMonitor() end
end
end
end
if bUpdate and TurtleData[ArcConst.TDS_STATUSTYPE] == ArcConst.TST_NEEDPLAYER then
for i=1,#gtabsPlayers,1 do
SendMessageToPlayer(TurtleData,gtabsPlayers[i])
end
end
end
end
local function MonitorTouch()
local sEvent = nil
local sSide = nil
local nX = nil
local nY = nil
local nButtonIndex = nil
if gMonitor == nil then return false end
while true do
while true do
sEvent,sSide,nX,nY = os.pullEvent("monitor_touch")
if #gtabTurtleData <= 1 then break end
nButtonIndex = ArcMonitor.GetButtonIndex(nX,nY)
if nButtonIndex == nil then break end
if nButtonIndex == 1 then
gnShowedTurtleIndex = gnShowedTurtleIndex-1
if gnShowedTurtleIndex <= 0 then gnShowedTurtleIndex = #gtabTurtleData end
elseif nButtonIndex == 2 then
gnShowedTurtleIndex = gnShowedTurtleIndex+1
if gnShowedTurtleIndex > #gtabTurtleData then gnShowedTurtleIndex = 1 end
end
RefreshMonitor()
end
end
end
local function DetectPlayersConnexion()
local sEvent = nil
local sPlayer = nil
local sDimension = nil
if gPlayerDetector == nil or gChatBox == nil then return false end
while true do
sEvent,sPlayer,sDimension = os.pullEvent("playerJoin")
for i=1,#gtabsPlayers[i],1 do
if sPlayer == #gtabsPlayers[i] then
for j=1,#gtabTurtleData,1 do
if gtabTurtleData[j][ArcConst.TDS_STATUSTYPE] == ArcConst.TST_NEEDPLAYER then SendMessageToPlayer(gtabTurtleData[j],sPlayer) end
end
end
end
end
end
local function CheckPeripherals()
local tabPeripherals = nil
local sPeripheralType1 = nil
local sPeripheralType2 = nil
tabPeripherals = peripheral.getNames()
for i=1,#tabPeripherals,1 do
sPeripheralType1,sPeripheralType2 = peripheral.getType(tabPeripherals[i])
if sPeripheralType1 == "modem" and sPeripheralType2 == nil then
gModem = tabPeripherals[i]
rednet.open(gModem)
elseif sPeripheralType1 == "monitor" then
gMonitor = peripheral.wrap(tabPeripherals[i])
elseif sPeripheralType1 == "chat_box" then
gChatBox = peripheral.wrap(tabPeripherals[i])
elseif sPeripheralType1 == "environment_detector" then
gEnvDetector = peripheral.wrap(tabPeripherals[i])
elseif sPeripheralType1 == "player_detector" then
gPlayerDetector = peripheral.wrap(tabPeripherals[i])
end
end
end
CheckPeripherals()
ArcMonitor.SetMonitor(gMonitor)
RefreshMonitor()
LoadPlayers()
os.sleep(0.1)
parallel.waitForAll(Menu,TurtleListen,MonitorTouch,DetectPlayersConnexion)