Files
remoteturtle/pocketcontrol.lua

640 lines
18 KiB
Lua

-- Unified Pocket Control Center
-- Combines turtle control, GPS tracking, server management, and webbridge control
-- Communicates wirelessly with webbridge - NO direct HTTP calls
local CHANNEL_SEND = 100
local CHANNEL_RECEIVE = 101
local STATUS_CHANNEL = 102
local POCKET_CHANNEL = 103 -- Pocket <-> Webbridge communication
-- Find modem
local modem = peripheral.find("modem")
if not modem then
error("No wireless modem found!")
end
-- Equip modem if this is a pocket computer
if pocket then
pocket.equipBack()
modem = peripheral.find("modem")
end
modem.open(CHANNEL_RECEIVE)
modem.open(STATUS_CHANNEL)
modem.open(POCKET_CHANNEL)
local w, h = term.getSize()
-- State
local turtles = {}
local selectedTurtle = nil
local mode = "overview" -- overview, control, gps, webbridge, server
local myPosition = nil
local webbridgeStatus = nil
local serverStats = nil
local logMessages = {}
local maxLogs = 50
-- Button system
local buttons = {}
local function addLog(message, color)
table.insert(logMessages, 1, {
text = message,
time = os.epoch("utc"),
color = color or colors.white
})
if #logMessages > maxLogs then
table.remove(logMessages)
end
end
local function clearButtons()
buttons = {}
end
local function addButton(x, y, width, height, label, action, color)
table.insert(buttons, {
x = x, y = y, width = width, height = height,
label = label, action = action, color = color or colors.gray
})
end
local function drawButton(btn, highlight)
term.setCursorPos(btn.x, btn.y)
local bg = highlight and colors.white or btn.color
local fg = highlight and colors.black or colors.white
term.setBackgroundColor(bg)
term.setTextColor(fg)
local text = btn.label
local padding = math.floor((btn.width - #text) / 2)
for dy = 0, btn.height - 1 do
term.setCursorPos(btn.x, btn.y + dy)
if dy == math.floor(btn.height / 2) then
term.write(string.rep(" ", padding) .. text .. string.rep(" ", btn.width - padding - #text))
else
term.write(string.rep(" ", btn.width))
end
end
end
local function checkButton(x, y)
for _, btn in ipairs(buttons) do
if x >= btn.x and x < btn.x + btn.width and
y >= btn.y and y < btn.y + btn.height then
return btn
end
end
end
-- Update my GPS position and send to webbridge
local function updateMyPosition()
local x, y, z = gps.locate(2)
if x then
myPosition = {x = math.floor(x), y = math.floor(y), z = math.floor(z)}
-- Send to webbridge (which will forward to server)
modem.transmit(POCKET_CHANNEL, CHANNEL_RECEIVE, {
type = "player_position",
playerID = os.getComputerID(),
label = os.getComputerLabel() or ("Pocket #" .. os.getComputerID()),
position = myPosition,
timestamp = os.epoch("utc")
})
return true
else
addLog("GPS: Failed to locate", colors.red)
end
return false
end
-- Send command to turtle via webbridge
local function sendCommand(turtleID, command, param)
modem.transmit(POCKET_CHANNEL, CHANNEL_RECEIVE, {
type = "turtle_command",
turtleID = turtleID,
command = command,
param = param,
from = os.getComputerID()
})
addLog("Sent: " .. command .. " -> Turtle #" .. turtleID, colors.yellow)
end
-- Send webbridge control command
local function sendWebbridgeCommand(command)
modem.transmit(POCKET_CHANNEL, CHANNEL_RECEIVE, {
type = "webbridge_control",
command = command,
from = os.getComputerID()
})
addLog("Webbridge: " .. command, colors.cyan)
end
-- Request server stats via webbridge
local function fetchServerStats()
modem.transmit(POCKET_CHANNEL, CHANNEL_RECEIVE, {
type = "server_stats_request",
from = os.getComputerID()
})
end
-- Draw header
local function drawHeader()
term.setBackgroundColor(colors.blue)
term.setTextColor(colors.white)
term.setCursorPos(1, 1)
term.clearLine()
local title = "POCKET CTRL"
term.setCursorPos(2, 1)
term.write(title)
-- Show quick stats
term.setCursorPos(w - 10, 1)
term.write("T:" .. #turtles .. " ")
if myPosition then
term.write("GPS")
end
end
-- Draw mode tabs
local function drawTabs()
clearButtons()
local tabWidth = math.floor(w / 5)
local y = 2
-- Draw tab highlights
term.setCursorPos(1, y)
term.setBackgroundColor(colors.black)
term.clearLine()
local tabs = {
{name = "TRT", mode = "overview"},
{name = "CTL", mode = "control"},
{name = "GPS", mode = "gps"},
{name = "WEB", mode = "webbridge"},
{name = "SRV", mode = "server"}
}
for i, tab in ipairs(tabs) do
local x = (i - 1) * tabWidth + 1
addButton(x, y, tabWidth, 1, tab.name, function() mode = tab.mode end,
mode == tab.mode and colors.green or colors.gray)
term.setCursorPos(x, y)
if mode == tab.mode then
term.setBackgroundColor(colors.green)
term.setTextColor(colors.white)
else
term.setBackgroundColor(colors.gray)
term.setTextColor(colors.lightGray)
end
local padding = math.floor((tabWidth - #tab.name) / 2)
term.write(string.rep(" ", padding) .. tab.name .. string.rep(" ", tabWidth - padding - #tab.name))
end
end
-- Draw overview mode
local function drawOverview()
term.setBackgroundColor(colors.black)
local y = 4
term.setTextColor(colors.lime)
term.setCursorPos(2, y)
term.write("Online: " .. #turtles)
y = y + 1
if #turtles == 0 then
term.setTextColor(colors.gray)
term.setCursorPos(2, y + 1)
term.write("No turtles")
else
for i, turtle in ipairs(turtles) do
if y >= h - 3 then break end
term.setCursorPos(2, y)
term.setTextColor(colors.yellow)
term.write("#" .. turtle.turtleID)
term.setTextColor(colors.lightGray)
term.setCursorPos(8, y)
local stateText = (turtle.mode or "idle"):sub(1, 8)
term.write(stateText)
if turtle.position then
term.setCursorPos(17, y)
term.setTextColor(colors.gray)
term.write(string.format("%d,%d,%d", turtle.position.x, turtle.position.y, turtle.position.z))
end
y = y + 1
end
end
-- Add select button
local btnY = h - 1
addButton(2, btnY, 10, 1, "SELECT", function()
if #turtles > 0 then
selectedTurtle = (selectedTurtle or 0) % #turtles + 1
addLog("Selected #" .. turtles[selectedTurtle].turtleID, colors.lime)
end
end, colors.green)
drawButton(buttons[#buttons], false)
end
-- Draw control mode
local function drawControl()
term.setBackgroundColor(colors.black)
local y = 4
if not selectedTurtle or not turtles[selectedTurtle] then
term.setTextColor(colors.red)
term.setCursorPos(2, y)
term.write("No turtle selected")
term.setCursorPos(2, y + 1)
term.setTextColor(colors.gray)
term.write("SELECT from TURTLES")
return
end
local turtle = turtles[selectedTurtle]
term.setTextColor(colors.yellow)
term.setCursorPos(2, y)
term.write("Turtle #" .. turtle.turtleID)
y = y + 1
-- Movement buttons (compact layout)
local btnW = 4
local btnH = 1
local centerX = math.floor(w / 2) - 2
local centerY = 8
-- Movement controls
addButton(centerX, centerY - 2, btnW, btnH, "FWD", function()
sendCommand(turtle.turtleID, "forward")
end, colors.blue)
addButton(centerX, centerY + 2, btnW, btnH, "BCK", function()
sendCommand(turtle.turtleID, "back")
end, colors.blue)
addButton(centerX - btnW - 1, centerY, btnW, btnH, "LFT", function()
sendCommand(turtle.turtleID, "turnLeft")
end, colors.blue)
addButton(centerX + btnW + 1, centerY, btnW, btnH, "RGT", function()
sendCommand(turtle.turtleID, "turnRight")
end, colors.blue)
addButton(centerX, centerY, btnW, btnH, "UP", function()
sendCommand(turtle.turtleID, "up")
end, colors.green)
-- Action buttons (bottom rows)
local btnY = h - 5
addButton(2, btnY, 6, 1, "EXPLR", function()
sendCommand(turtle.turtleID, "explore")
end, colors.cyan)
addButton(9, btnY, 6, 1, "MINE", function()
sendCommand(turtle.turtleID, "mine")
end, colors.orange)
addButton(16, btnY, 6, 1, "HOME", function()
sendCommand(turtle.turtleID, "returnHome")
end, colors.yellow)
addButton(23, btnY, 6, 1, "STOP", function()
sendCommand(turtle.turtleID, "stop")
end, colors.red)
btnY = h - 3
addButton(2, btnY, 6, 1, "DOWN", function()
sendCommand(turtle.turtleID, "down")
end, colors.green)
addButton(9, btnY, 6, 1, "DIG", function()
sendCommand(turtle.turtleID, "dig")
end, colors.red)
for i = #buttons - 8, #buttons do
if buttons[i] then
drawButton(buttons[i], false)
end
end
end
-- Draw GPS mode
local function drawGPS()
term.setBackgroundColor(colors.black)
local y = 4
term.setTextColor(colors.lime)
term.setCursorPos(2, y)
term.write("GPS Tracking")
y = y + 1
-- My position (compact)
term.setTextColor(colors.white)
term.setCursorPos(2, y)
if myPosition then
term.write(string.format("You: %d,%d,%d", myPosition.x, myPosition.y, myPosition.z))
else
term.setTextColor(colors.red)
term.write("GPS: Not available")
end
y = y + 2
-- Distance to selected turtle
if selectedTurtle and turtles[selectedTurtle] and myPosition then
local turtle = turtles[selectedTurtle]
if turtle.position then
local dx = turtle.position.x - myPosition.x
local dy = turtle.position.y - myPosition.y
local dz = turtle.position.z - myPosition.z
local dist = math.sqrt(dx*dx + dy*dy + dz*dz)
term.setTextColor(colors.cyan)
term.setCursorPos(2, y)
term.write(string.format("Turtle #%d:", turtle.turtleID))
y = y + 1
term.setTextColor(colors.white)
term.setCursorPos(2, y)
term.write(string.format("Dist: %.1f blocks", dist))
y = y + 1
term.setTextColor(colors.gray)
term.setCursorPos(2, y)
term.write(string.format("Delta: %d,%d,%d", dx, dy, dz))
end
end
y = h - 2
addButton(2, y, 13, 1, "UPDATE GPS", function()
if updateMyPosition() then
addLog("GPS updated", colors.lime)
else
addLog("GPS failed", colors.red)
end
end, colors.green)
addButton(16, y, 13, 1, "GOTO TURTLE", function()
if selectedTurtle and turtles[selectedTurtle] and turtles[selectedTurtle].position then
local pos = turtles[selectedTurtle].position
addLog(string.format("At: %d,%d,%d", pos.x, pos.y, pos.z), colors.cyan)
end
end, colors.blue)
drawButton(buttons[#buttons-1], false)
drawButton(buttons[#buttons], false)
end
-- Draw webbridge mode
local function drawWebbridge()
term.setBackgroundColor(colors.black)
local y = 4
term.setTextColor(colors.lime)
term.setCursorPos(2, y)
term.write("Webbridge Control")
y = y + 1
if webbridgeStatus then
term.setTextColor(colors.white)
term.setCursorPos(2, y)
term.write("Status: ONLINE")
y = y + 1
term.setCursorPos(2, y)
term.write("MSG:" .. (webbridgeStatus.messages or 0) ..
" CMD:" .. (webbridgeStatus.commands or 0))
else
term.setTextColor(colors.gray)
term.setCursorPos(2, y)
term.write("Status: Unknown")
end
y = y + 2
-- Compact button layout
addButton(2, y, 13, 1, "PING", function()
sendWebbridgeCommand("ping")
end, colors.blue)
addButton(16, y, 13, 1, "RESTART", function()
sendWebbridgeCommand("restart")
end, colors.orange)
y = y + 2
addButton(2, y, 13, 1, "STATUS", function()
sendWebbridgeCommand("status")
end, colors.green)
addButton(16, y, 13, 1, "LOGS", function()
sendWebbridgeCommand("logs")
end, colors.cyan)
for i = #buttons - 3, #buttons do
if buttons[i] then
drawButton(buttons[i], false)
end
end
-- Show recent logs (compact)
y = y + 3
term.setTextColor(colors.gray)
term.setCursorPos(2, y)
term.write("Activity:")
y = y + 1
for i = 1, math.min(3, #logMessages) do
if y >= h - 1 then break end
local log = logMessages[i]
term.setTextColor(log.color)
term.setCursorPos(2, y)
local text = log.text
if #text > w - 3 then
text = text:sub(1, w - 6) .. "..."
end
term.write(text)
y = y + 1
end
end
-- Draw server mode
local function drawServer()
term.setBackgroundColor(colors.black)
local y = 4
term.setTextColor(colors.lime)
term.setCursorPos(2, y)
term.write("Server Interface")
y = y + 1
if serverStats then
term.setTextColor(colors.white)
term.setCursorPos(2, y)
term.write("Status: ONLINE")
y = y + 1
term.setCursorPos(2, y)
term.write("Turtles: " .. (serverStats.turtles or 0))
y = y + 1
term.setCursorPos(2, y)
term.write("Blocks: " .. (serverStats.blocks or 0))
else
term.setTextColor(colors.red)
term.setCursorPos(2, y)
term.write("Status: OFFLINE")
end
y = h - 2
addButton(2, y, 13, 1, "REFRESH", function()
fetchServerStats()
addLog("Stats refreshed", colors.lime)
end, colors.green)
addButton(16, y, 13, 1, "WEB INFO", function()
modem.transmit(POCKET_CHANNEL, CHANNEL_RECEIVE, {
type = "web_interface_request",
from = os.getComputerID()
})
addLog("Requesting info", colors.cyan)
end, colors.blue)
drawButton(buttons[#buttons-1], false)
drawButton(buttons[#buttons], false)
end
-- Main draw function
local function draw()
term.setBackgroundColor(colors.black)
term.clear()
drawHeader()
drawTabs()
if mode == "overview" then
drawOverview()
elseif mode == "control" then
drawControl()
elseif mode == "gps" then
drawGPS()
elseif mode == "webbridge" then
drawWebbridge()
elseif mode == "server" then
drawServer()
end
-- Status bar (compact)
term.setBackgroundColor(colors.gray)
term.setCursorPos(1, h)
term.clearLine()
term.setTextColor(colors.white)
term.setCursorPos(2, h)
term.write(string.format("T:%d", #turtles))
if selectedTurtle and turtles[selectedTurtle] then
term.setCursorPos(8, h)
term.write("#" .. turtles[selectedTurtle].turtleID)
end
if myPosition then
term.setCursorPos(w - 5, h)
term.write("GPS")
end
end
-- Main loop
print("Pocket Control Center")
print("Initializing...")
-- Initial GPS update
updateMyPosition()
-- Start background tasks
parallel.waitForAny(
function()
-- GPS update loop
while true do
sleep(2)
updateMyPosition()
end
end,
function()
-- Server stats update loop
while true do
sleep(10)
fetchServerStats()
end
end,
function()
-- Message listener
while true do
local event, side, channel, replyChannel, message = os.pullEvent("modem_message")
if channel == STATUS_CHANNEL and type(message) == "table" then
if message.type == "status" then
-- Update turtle list
local found = false
for i, t in ipairs(turtles) do
if t.turtleID == message.turtleID then
turtles[i] = message
found = true
break
end
end
if not found then
table.insert(turtles, message)
addLog("Turtle #" .. message.turtleID .. " connected", colors.lime)
end
end
elseif channel == POCKET_CHANNEL and type(message) == "table" then
-- Handle responses from webbridge
if message.type == "webbridge_status" then
webbridgeStatus = message.data
addLog("Webbridge status received", colors.cyan)
elseif message.type == "server_stats" then
serverStats = message.data
addLog("Server stats received", colors.lime)
elseif message.type == "webbridge_log" then
addLog("[WB] " .. (message.text or ""), colors.lightGray)
elseif message.type == "command_ack" then
addLog("Command acknowledged", colors.green)
elseif message.type == "error" then
addLog("Error: " .. (message.error or "unknown"), colors.red)
end
end
end
end,
function()
-- UI loop
draw()
while true do
local event, p1, p2, p3 = os.pullEvent()
if event == "mouse_click" or event == "mouse_up" then
local btn = checkButton(p2, p3)
if btn and btn.action then
btn.action()
end
draw()
elseif event == "char" then
if p1 == "q" then
break
elseif p1 == "r" then
draw()
end
end
end
end
)
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1, 1)
print("Pocket Control Center closed")