CCCPaste

ArcTurtleWatching PROGRAM

-- 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)