Scores and rewards#
A leaderboard everybody in the world shares, a reward that comes once a day, and a round that runs on a clock. Three things nearly every game wants, built from world.store, players.store and task.
A leaderboard#
Keep one key per player under a common prefix, and let top do the ranking.
local world = require("world")local players = require("players") -- Anything in the world can call this: a goal, a coin, a finish line.local function add_points(player_id: string, points: number) world.store:update("score." .. player_id, function(current) return tostring((tonumber(current) or 0) + points) end)end self:on_touch(function(player) add_points(player.id, 10) print("— top 5 —") for i, row in world.store:top("score.", 5) do local who = players:get(row.key) print(i, who and who.name or "someone who left", row.value) endend)row.key comes back without the prefix, so here it is the player's id. Somebody who has left still has a score; players:get answers nil for them, which is why the name has a fallback.
Name keys like paths
score.<id>, shop.bread, quest.<id>.step. A prefix then works as a folder: keys("quest.") lists every quest row, and a different prefix never mixes into your leaderboard.
A reward once a day#
os.time() is the real clock: seconds since 1970, the same on every server. Store when somebody last collected, in their drawer, so it follows them to whichever machine hosts the world next time.
local players = require("players") local DAY = 24 * 60 * 60 self:on_touch(function(player) local last, why = players.store:get(player.id, "daily.last") if why then return end -- not ready, or not allowed local now = os.time() local since = now - (tonumber(last) or 0) if since < DAY then local hours = math.ceil((DAY - since) / 3600) print(player.name .. ": come back in " .. hours .. " h") return end players.store:set(player.id, "daily.last", tostring(now)) players.store:update(player.id, "gold", function(current) return tostring((tonumber(current) or 0) + 50) end) print(player.name .. " collected 50 gold")end)"Once a day" here means 24 hours after the last one. For "once per calendar day", compare dates instead: os.date("!%Y-%m-%d") is today in UTC.
A round on a clock#
A round is a sequence: open, wait, warn, wait, close, rest, again. With task it is written in that order.
local world = require("world") local function announce(text: string) world.store:set("round.state", text) print(text)end task.spawn(function() while true do announce("round open") task.wait(120) announce("30 seconds left") task.wait(30) announce("round over") local best = world.store:top("score.", 1)[1] if best then print("winner:", best.key, best.value) end for _, key in world.store:keys("score.") do world.store:set(key, nil) -- a fresh board for the next round end task.wait(20) endend)Every object in the world can read round.state with world.store:get, so a gate or a scoreboard reacts to the round without talking to the controller.
See also#
- Saving data: which drawer survives what
- Shops and currency: spending what they earned
- task: routines that pause