-- 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(), position = myPosition, timestamp = os.epoch("utc") }) addLog("GPS: " .. x .. "," .. y .. "," .. z, colors.lime) 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 row) local 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) addButton(16, btnY, 6, 1, "MINE", function() sendCommand(turtle.turtleID, "mineStart") end, colors.orange) addButton(23, btnY, 6, 1, "HOME", function() sendCommand(turtle.turtleID, "returnHome") end, colors.yellow) 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(5) 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")