Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Participant Pocket Client
- -- ComputerCraft / CC:Tweaked
- local CHANNEL = 44551
- local SERVER_PROTOCOL = "cc_quiz_scoreboard"
- local modemSide
- local username = nil
- local disqualified = false
- local currentState = {
- mode = "hidden", -- hidden | reveal | disqualified | waiting
- message = "Not registered yet.",
- payload = nil
- }
- local function formatPoints(value)
- local points = tonumber(value) or 0
- if points % 1 == 0 then
- return tostring(math.floor(points))
- end
- return string.format("%.1f", points)
- end
- local function findModemSide()
- for _, side in ipairs(peripheral.getNames()) do
- if peripheral.getType(side) == "modem" then
- return side
- end
- end
- return nil
- end
- local function centerText(y, text, color)
- local w, _ = term.getSize()
- term.setCursorPos(1, y)
- term.clearLine()
- term.setTextColor(color or colors.white)
- local x = math.max(1, math.floor((w - #text) / 2) + 1)
- term.setCursorPos(x, y)
- term.write(text)
- end
- local function initNetwork()
- modemSide = findModemSide()
- if not modemSide then
- error("No modem found. This pocket computer needs an ender modem.")
- end
- if not rednet.isOpen(modemSide) then
- rednet.open(modemSide)
- end
- end
- local function sendToServer(payload)
- payload.channel = CHANNEL
- rednet.broadcast(payload, SERVER_PROTOCOL)
- end
- local function askUsername()
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.clear()
- centerText(2, "CC Quiz Participant", colors.cyan)
- centerText(4, "Enter username and press Enter", colors.lightGray)
- term.setCursorPos(2, 6)
- term.setTextColor(colors.white)
- write("> ")
- local input = read()
- input = tostring(input or ""):gsub("^%s+", ""):gsub("%s+$", "")
- if input == "" then
- centerText(8, "Username cannot be empty.", colors.red)
- sleep(1.2)
- return askUsername()
- end
- return input
- end
- local function draw()
- term.setBackgroundColor(colors.black)
- term.clear()
- centerText(1, "Participant View", colors.cyan)
- if username then
- centerText(2, username, colors.white)
- end
- if currentState.mode == "waiting" then
- centerText(4, currentState.message or "Waiting for server...", colors.lightGray)
- elseif currentState.mode == "hidden" then
- centerText(4, "Scoreboard Hidden", colors.yellow)
- centerText(6, "Wait for administration reveal.", colors.lightGray)
- elseif currentState.mode == "disqualified" then
- centerText(4, "DISQUALIFIED", colors.red)
- centerText(6, currentState.message or "Removed by administration.", colors.lightGray)
- elseif currentState.mode == "reveal" then
- local data = currentState.payload or {}
- local row = 4
- centerText(row, string.format("Your Rank: #%d / %d", data.self.rank or 0, data.total or 0), colors.lime)
- row = row + 1
- centerText(row, string.format("Your Points: %s", formatPoints(data.self.points or 0)), colors.white)
- row = row + 2
- centerText(row, "Top 3", colors.yellow)
- row = row + 1
- if data.top3 and #data.top3 > 0 then
- local rendered = 0
- for i = 1, #data.top3 do
- local t = data.top3[i]
- if not (data.self and t.id == data.self.id) then
- centerText(row, string.format("%d. %s (%s)", t.rank, t.username, formatPoints(t.points)), colors.white)
- row = row + 1
- rendered = rendered + 1
- end
- end
- if rendered == 0 then
- centerText(row, "(You are in Top 3)", colors.lightGray)
- row = row + 1
- end
- else
- centerText(row, "No ranking data", colors.lightGray)
- row = row + 1
- end
- row = row + 1
- if data.above then
- centerText(row, string.format("Above: %d. %s (%s)", data.above.rank, data.above.username, formatPoints(data.above.points)), colors.lightGray)
- row = row + 1
- else
- centerText(row, "Above: nobody", colors.lightGray)
- row = row + 1
- end
- if data.below then
- centerText(row, string.format("Below: %d. %s (%s)", data.below.rank, data.below.username, formatPoints(data.below.points)), colors.lightGray)
- else
- centerText(row, "Below: nobody", colors.lightGray)
- end
- end
- local _, h = term.getSize()
- centerText(h, "Q: re-register", colors.gray)
- end
- local function handleMessage(senderId, msg, protocol)
- if protocol ~= SERVER_PROTOCOL then
- return
- end
- if type(msg) ~= "table" or not msg.type then
- return
- end
- if msg.type == "register_ok" then
- currentState.mode = "waiting"
- currentState.message = "Registered. Waiting for scoreboard state..."
- elseif msg.type == "register_error" then
- currentState.mode = "waiting"
- currentState.message = msg.message or "Registration failed."
- elseif msg.type == "score_hidden" then
- currentState.mode = "hidden"
- currentState.message = msg.message
- currentState.payload = nil
- elseif msg.type == "score_reveal" then
- currentState.mode = "reveal"
- currentState.payload = msg
- elseif msg.type == "disqualified" then
- disqualified = true
- currentState.mode = "disqualified"
- currentState.message = msg.reason or "You were disqualified."
- currentState.payload = nil
- elseif msg.type == "force_reset" then
- currentState.mode = "waiting"
- currentState.message = msg.reason or "Reset requested by administration."
- currentState.payload = nil
- draw()
- sleep(1)
- os.reboot()
- end
- end
- local function register()
- sendToServer({
- type = "register",
- username = username
- })
- sendToServer({
- type = "request_state"
- })
- end
- local function heartbeatLoop()
- while true do
- sleep(5)
- sendToServer({ type = "heartbeat" })
- if not disqualified then
- sendToServer({ type = "request_state" })
- end
- end
- end
- local function receiverLoop()
- while true do
- local senderId, msg, protocol = rednet.receive(SERVER_PROTOCOL, 1)
- if senderId then
- handleMessage(senderId, msg, protocol)
- draw()
- end
- end
- end
- local function inputLoop()
- while true do
- local event, key = os.pullEvent("key")
- if key == keys.q then
- disqualified = false
- username = askUsername()
- currentState.mode = "waiting"
- currentState.message = "Registering..."
- currentState.payload = nil
- register()
- draw()
- end
- end
- end
- local function main()
- initNetwork()
- username = askUsername()
- currentState.mode = "waiting"
- currentState.message = "Registering..."
- register()
- draw()
- parallel.waitForAny(receiverLoop, heartbeatLoop, inputLoop)
- end
- main()
Advertisement
Add Comment
Please, Sign In to add comment