All pastes #4c45AdjCXW Raw Edit

text paste

public text v1 · immutable
#4c45AdjCXW ·published 2026-09-20 08:49 UTC
rendered paste body
-- ============================================================================-- Online Player Viewer-- Requires: monitor + playerDetector peripherals-- Dependencies: CC:Tweaked + Advanced Peripherals-- ============================================================================-- ============================================================================-- Configuration-- ============================================================================local REFRESH_INTERVAL = 1                 -- Refresh interval in secondslocal TEXT_SCALE = 0.5                     -- Text scale (0.5 = smallest / most room)-- ============================================================================-- Color Theme-- ============================================================================local CLR = {    bg = colors.black,    title = colors.yellow,    header = colors.cyan,    text = colors.white,    white = colors.white,    dim = colors.lightGray,    online = colors.lime,    offline = colors.red,    warn = colors.orange,    highlight = colors.green,    accent = colors.pink,    border = colors.gray,}-- Dimension short nameslocal DIM_NAMES = {    ["minecraft:overworld"] = "Overworld",    ["minecraft:the_nether"] = "Nether",    ["minecraft:the_end"] = "End",}-- ============================================================================-- Utility Functions-- ============================================================================--- Format dimension namelocal function formatDimension(dim)    return DIM_NAMES[dim] or dim or "???"end--- Format coordinateslocal function formatPos(x, y, z)    if not x then return "---, ---, ---" end    return string.format("%d, %d, %d", math.floor(x), math.floor(y), math.floor(z))end--- Format healthlocal function formatHealth(health, maxHealth)    if not health then return "?/?" end    return string.format("%.0f/%.0f", health, maxHealth)end--- Draw centered textlocal function drawCentered(mon, y, text, color)    local w = mon.getSize()    mon.setTextColor(color or CLR.text)    local x = math.floor((w - #text) / 2) + 1    if x < 1 then x = 1 end    mon.setCursorPos(x, y)    mon.write(text)end--- Draw horizontal separatorlocal function drawSeparator(mon, y, char, color)    local w = mon.getSize()    char = char or "-"    mon.setTextColor(color or CLR.border)    mon.setCursorPos(1, y)    mon.write(string.rep(char, w))end--- Truncate text to fit widthlocal function truncate(text, maxLen)    if #text <= maxLen then return text end    return text:sub(1, maxLen - 1) .. "."end-- ============================================================================-- Peripheral Setup-- ============================================================================local function findPeripherals()    local monitor = nil    local detector = nil    local names = peripheral.getNames()    for _, name in ipairs(names) do        local ptype = peripheral.getType(name)        if ptype == "monitor" and not monitor then            monitor = peripheral.wrap(name)        elseif ptype == "player_detector" and not detector then            detector = peripheral.wrap(name)        end    end    return monitor, detectorend-- ============================================================================-- Data Fetching-- ============================================================================local function getPlayerData(detector)    local players = {}    local ok, onlineList = pcall(detector.getOnlinePlayers, detector)    if not ok then        return nil, "Failed to get player list: " .. tostring(onlineList)    end    if not onlineList or #onlineList == 0 then        return {}, nil    end    for _, name in ipairs(onlineList) do        local info = detector.getPlayer(name)        if info then            table.insert(players, {                name = name,                x = info.x,                y = info.y,                z = info.z,                dimension = info.dimension,                health = info.health,                maxHealth = info.maxHealth,            })        else            -- Still show player even if detail fetch fails            table.insert(players, {                name = name,                x = nil, y = nil, z = nil,                dimension = nil,                health = nil, maxHealth = nil,            })        end    end    -- Sort alphabetically    table.sort(players, function(a, b) return a.name:lower() < b.name:lower() end)    return players, nilend-- ============================================================================-- Rendering-- ============================================================================-- Fixed row layout (the static frame is drawn once; only dynamic rows update)local ROW_STATS   = 4   -- player count / refresh rate / timestamplocal ROW_HEADER  = 6   -- column headerslocal ROW_FIRST   = 8   -- first player row--- Blank a vertical region with the background color. Used instead of--- clearing the whole monitor so a refresh never flashes the entire screen.local function clearRows(mon, y1, y2)    local w = mon.getSize()    for y = y1, y2 do        mon.setBackgroundColor(CLR.bg)        mon.setCursorPos(1, y)        mon.write(string.rep(" ", w))    endend--- Draw the static frame (title bar, separators, column header) once.--- It stays on screen; only the dynamic rows are rewritten afterwards.local function drawStaticFrame(mon)    local w = mon.getSize()    mon.setBackgroundColor(CLR.bg)    mon.clear()    -- Title bar    mon.setTextColor(CLR.title)    drawSeparator(mon, 1, "=", CLR.title)    drawCentered(mon, 2, "** Online Player Viewer **", CLR.title)    drawSeparator(mon, 3, "=", CLR.title)    -- Column header    local nameW = math.min(18, math.floor(w * 0.22))    local dimW  = math.min(10, math.floor(w * 0.12))    local posW  = math.min(24, math.floor(w * 0.35))    local hpW   = math.min(10, math.floor(w * 0.12))    mon.setTextColor(CLR.header)    mon.setCursorPos(2, ROW_HEADER)    local headerFmt = "%-" .. nameW .. "s  %-" .. dimW .. "s  %-" .. posW .. "s  %" .. hpW .. "s"    mon.write(string.format(headerFmt, "Name", "Dimension", "Position (X, Y, Z)", "Health"))    drawSeparator(mon, ROW_HEADER + 1, "-", CLR.border)end--- Redraw only the stats row (no full clear, so no flicker)local function renderStats(mon, playerCount, timestamp)    local w = mon.getSize()    mon.setBackgroundColor(CLR.bg)    mon.setCursorPos(1, ROW_STATS)    mon.write(string.rep(" ", w))    mon.setTextColor(CLR.text)    mon.setCursorPos(2, ROW_STATS)    mon.write("Online: ")    mon.setTextColor(CLR.online)    mon.write(tostring(playerCount))    mon.setTextColor(CLR.dim)    mon.setCursorPos(math.floor(w / 2) + 1, ROW_STATS)    mon.write("Refresh: " .. REFRESH_INTERVAL .. "s")    -- Timestamp on the right    local timeStr = "Updated: " .. timestamp    mon.setCursorPos(w - #timeStr, ROW_STATS)    mon.write(timeStr)endlocal function renderPlayerTable(mon, players)    local w, h = mon.getSize()    local lastRow = h - 2    if #players == 0 then        clearRows(mon, ROW_FIRST, lastRow)        drawCentered(mon, ROW_FIRST, "(No players online)", CLR.dim)        return    end    -- Column widths    local nameW = math.min(18, math.floor(w * 0.22))    local dimW  = math.min(10, math.floor(w * 0.12))    local posW  = math.min(24, math.floor(w * 0.35))    local hpW   = math.min(10, math.floor(w * 0.12))    local row = ROW_FIRST    for i, p in ipairs(players) do        if row > lastRow then            mon.setTextColor(CLR.dim)            mon.setCursorPos(2, lastRow)            mon.write("... +" .. (#players - i + 1) .. " more players")            break        end        -- Alternating row colors        if i % 2 == 0 then            mon.setTextColor(CLR.text)        else            mon.setTextColor(CLR.white)        end        mon.setCursorPos(2, row)        -- Name        local name = truncate(p.name, nameW)        mon.write(string.format("%-" .. nameW .. "s", name))        mon.setCursorPos(2 + nameW + 2, row)        -- Dimension        local dim = truncate(formatDimension(p.dimension), dimW)        if p.dimension == "minecraft:the_nether" then            mon.setTextColor(CLR.warn)        elseif p.dimension == "minecraft:the_end" then            mon.setTextColor(CLR.accent)        end        mon.write(string.format("%-" .. dimW .. "s", dim))        -- Position        if i % 2 == 0 then            mon.setTextColor(CLR.text)        else            mon.setTextColor(CLR.white)        end        mon.setCursorPos(2 + nameW + 2 + dimW + 2, row)        local pos = formatPos(p.x, p.y, p.z)        mon.write(string.format("%-" .. posW .. "s", pos))        -- Health        mon.setCursorPos(2 + nameW + 2 + dimW + 2 + posW + 2, row)        local hp = formatHealth(p.health, p.maxHealth)        if p.health and p.maxHealth then            local ratio = p.health / p.maxHealth            if ratio < 0.25 then                mon.setTextColor(CLR.offline)            elseif ratio < 0.5 then                mon.setTextColor(CLR.warn)            else                mon.setTextColor(CLR.highlight)            end        end        mon.write(string.format("%" .. hpW .. "s", hp))        row = row + 1    end    -- Blank the rows left over when the new list is shorter    clearRows(mon, row, lastRow)endlocal function renderFooter(mon, errorMsg)    local w, h = mon.getSize()    mon.setBackgroundColor(CLR.bg)    mon.setCursorPos(1, h - 1)    mon.write(string.rep(" ", w))    mon.setCursorPos(1, h)    mon.write(string.rep(" ", w))    if errorMsg then        drawSeparator(mon, h - 1, "-", CLR.warn)        mon.setTextColor(CLR.warn)        mon.setCursorPos(2, h)        mon.write("! " .. truncate(errorMsg, w - 4))    else        drawSeparator(mon, h - 1, "-", CLR.border)        mon.setTextColor(CLR.dim)        mon.setCursorPos(2, h)        mon.write("CC:Tweaked + Advanced Peripherals")    endend-- ============================================================================-- Main Loop-- ============================================================================local function main()    local monitor, detector = findPeripherals()    if not monitor then        print("Error: Monitor peripheral not found!")        print("Available: " .. table.concat(peripheral.getNames(), ", "))        return    end    if not detector then        print("Error: Player detector peripheral not found!")        print("Available: " .. table.concat(peripheral.getNames(), ", "))        -- Show error on monitor too        monitor.setTextScale(TEXT_SCALE)        monitor.setBackgroundColor(CLR.bg)        monitor.clear()        monitor.setTextColor(CLR.warn)        drawCentered(monitor, math.floor(monitor.getSize() / 2), "! Player detector not found !", CLR.warn)        return    end    -- Init monitor    monitor.setTextScale(TEXT_SCALE)    print("Online Player Viewer started!")    print("Monitor size: " .. monitor.getSize())    print("Press Ctrl+T to stop")    -- Draw the static frame once; only dynamic rows are redrawn each tick    drawStaticFrame(monitor)    -- Main loop    while true do        local timestamp = os.date("%H:%M:%S")        local players, err = getPlayerData(detector)        renderStats(monitor, players and #players or 0, timestamp)        if players then            renderPlayerTable(monitor, players)        else            local _, h = monitor.getSize()            clearRows(monitor, ROW_FIRST, h - 2)        end        renderFooter(monitor, err)        sleep(REFRESH_INTERVAL)    endend-- ============================================================================-- Entry Point-- ============================================================================local ok, err = pcall(main)if not ok then    local monitor = peripheral.find("monitor")    if monitor then        monitor.setTextScale(TEXT_SCALE)        monitor.setBackgroundColor(colors.black)        monitor.clear()        monitor.setTextColor(colors.red)        monitor.setCursorPos(1, 1)        monitor.write("Crash:\n" .. tostring(err))    end    print("Error: " .. tostring(err))end