Advertisement
zamoth

player detector

Jul 7th, 2025 (edited)
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 4.84 KB | None | 0 0
  1. -- Player tracker for ComputerCraft (America/Sao_Paulo)
  2. --   • Requires HTTP
  3. --   • Smooth play-time updates via os.clock()
  4. --   • Auto-sync login/logout times when HTTP recovers
  5. --   • Persists data across restarts
  6. --   • Color-codes online (green) / offline (gray/red)
  7.  
  8. local detector = peripheral.find("playerDetector")
  9. if not detector then error("Player Detector not found") end
  10. local monitor  = peripheral.find("monitor")
  11. if not monitor  then error("Monitor not found") end
  12.  
  13. -- storage
  14. local dataFile = "player_data"
  15. local data = {}
  16. if fs.exists(dataFile) then
  17.   local f = fs.open(dataFile, "r")
  18.   data = textutils.unserialize(f.readAll()) or {}
  19.   f.close()
  20. end
  21.  
  22. local function saveData()
  23.   local f = fs.open(dataFile, "w")
  24.   f.write(textutils.serialize(data))
  25.   f.close()
  26. end
  27.  
  28. -- parse unixtime and datetime from the Sao_Paulo endpoint
  29. local function fetchTime()
  30.   local resp = http.get("http://worldtimeapi.org/api/timezone/America/Sao_Paulo.txt")
  31.   if not resp then return nil, nil end
  32.   local unixtime, datetime
  33.   for line in resp.readAll():gmatch("[^\r\n]+") do
  34.     local k,v = line:match("^(%w+):%s*(.+)$")
  35.     if k == "unixtime" then
  36.       unixtime = tonumber(v)
  37.     elseif k == "datetime" then
  38.       datetime = v
  39.     end
  40.   end
  41.   resp.close()
  42.   if not unixtime or not datetime then return nil, nil end
  43.   return unixtime, datetime
  44. end
  45.  
  46. -- format "2025-07-07T16:00:00.123456-03:00" → "16:00 - 07/07/25"
  47. local function formatTimestamp(dt)
  48.   local Y,M,D,h,m = dt:match("^(%d+)%-(%d+)%-(%d+)T(%d+):(%d+)")
  49.   return h .. ":" .. m .. " - " .. D .. "/" .. M .. "/" .. Y:sub(3)
  50. end
  51.  
  52. -- format seconds → "Xh Ym Zs"
  53. local function formatDuration(s)
  54.   s = math.floor(s)
  55.   local h = math.floor(s/3600)
  56.   local m = math.floor((s%3600)/60)
  57.   local sec = s%60
  58.   return string.format("%dh %dm %ds", h, m, sec)
  59. end
  60.  
  61. -- track real‐time clock deltas
  62. local lastClock = os.clock()
  63. local loopDelay = 1  -- seconds per cycle (updated to 1s)
  64.  
  65. while true do
  66.   local nowClock = os.clock()
  67.   local dt = nowClock - lastClock
  68.   lastClock = nowClock
  69.  
  70.   -- 1) Tick up every online player's play time
  71.   for _, info in pairs(data) do
  72.     if info.lastState == "online" then
  73.       info.totalPlayTime = info.totalPlayTime + dt
  74.     end
  75.   end
  76.  
  77.   -- 2) Try to fetch real UTC now
  78.   local nowEpoch, nowDT = fetchTime()
  79.  
  80.   -- 3) Build current online set
  81.   local onlineSet = {}
  82.   for _, name in ipairs(detector.getOnlinePlayers()) do
  83.     onlineSet[name] = true
  84.   end
  85.  
  86.   -- 4) Detect logouts
  87.   for name, info in pairs(data) do
  88.     if info.lastState == "online" and not onlineSet[name] then
  89.       info.lastState = "offline"
  90.       -- buffer the clock-time of this change
  91.       info.pendingClock = nowClock
  92.       info.pendingType  = "logout"
  93.     end
  94.   end
  95.  
  96.   -- 5) Detect logins
  97.   for name, info in pairs(data) do
  98.     if info.lastState == "offline" and onlineSet[name] then
  99.       info.lastState = "online"
  100.       info.pendingClock = nowClock
  101.       info.pendingType  = "login"
  102.     end
  103.   end
  104.  
  105.   -- 6) New players
  106.   for name in pairs(onlineSet) do
  107.     if not data[name] then
  108.       data[name] = {
  109.         totalPlayTime = 0,
  110.         lastState     = "online",
  111.         -- treat initial login as pending until we sync
  112.         pendingClock  = nowClock,
  113.         pendingType   = "login"
  114.       }
  115.     end
  116.   end
  117.  
  118.   -- 7) If we got a valid epoch, replay any pending events
  119.   if nowEpoch then
  120.     for _, info in pairs(data) do
  121.       if info.pendingType then
  122.         -- how long ago (in real seconds) the event happened?
  123.         local age = nowClock - info.pendingClock
  124.         -- compute the true unixtime of that event
  125.         info.lastChangeTime  = nowEpoch - age
  126.         info.lastChangeLabel = formatTimestamp(nowDT)
  127.         info.pendingClock    = nil
  128.         info.pendingType     = nil
  129.       end
  130.     end
  131.   end
  132.  
  133.   saveData()
  134.  
  135.   -- 8) Redraw monitor
  136.   monitor.setTextScale(1)
  137.   monitor.clear()
  138.   monitor.setCursorPos(1,1)
  139.  
  140.   local names = {}
  141.   for n in pairs(data) do table.insert(names, n) end
  142.   table.sort(names)
  143.  
  144.   local y = 1
  145.   for _, name in ipairs(names) do
  146.     local info = data[name]
  147.  
  148.     -- name
  149.     monitor.setCursorPos(1, y)
  150.     monitor.setTextColor(info.lastState == "online" and colors.green or colors.gray)
  151.     monitor.write(name)
  152.     y = y + 1
  153.  
  154.     -- status & since
  155.     monitor.setCursorPos(3, y)
  156.     if info.lastState == "online" then
  157.       monitor.setTextColor(colors.green)
  158.       monitor.write("Online since " .. (info.lastChangeLabel or "…"))
  159.     else
  160.       monitor.setTextColor(colors.red)
  161.       monitor.write("Offline since " .. (info.lastChangeLabel or "…"))
  162.     end
  163.     y = y + 1
  164.  
  165.     -- total played
  166.     monitor.setCursorPos(3, y)
  167.     monitor.setTextColor(colors.white)
  168.     monitor.write("Total played " .. formatDuration(info.totalPlayTime))
  169.     y = y + 2
  170.   end
  171.  
  172.   sleep(loopDelay)
  173. end
  174.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement