Settings, metrics and boards
Let players choose, count what they do and rank them.
A setting is a value a player chooses, a metric is a count of what players do, and a board is a ranking of players. You declare each in the manifest and the host stores them, so your mod needs no database of its own.
Settings
Declare a setting in mod.json, then read it.
Every value is text, and a bot reads each setting's default.
import { settingNumber, settingOn } from 'modlock'
const layout = player.setting('layout') // 'full' or 'compact'
const sounds = settingOn(player, 'sounds') // a switch, as a boolean
const volume = settingNumber(player, 'volume') // a number
player.setSetting('layout', 'full')local layout = player:setting("layout") -- "full" or "compact"
local sounds = player:setting("sounds") == "true"
local volume = tonumber(player:setting("volume"))
player:setSetting("layout", "full")layout = player.setting("layout") # "full" or "compact"
sounds = player.setting("sounds") == "true"
volume = float(player.setting("volume") or 0)
player.set_setting("layout", "full")layout, _ := p.Setting("layout") // "full" or "compact"
sounds, _ := p.SettingOn("sounds") // a switch, as a bool
volume, _ := p.SettingNumber("volume") // a number
_ = p.SetSetting("layout", "full")A player may change a setting outside the mod, such as on a profile page. The
setting-changed event reports the change:
import { onSettingChanged } from 'modlock'
onSettingChanged((player, key, value) => {
if (key === 'layout') {
player.chat(`Layout is now ${value}.`)
}
})modlock.onSettingChanged(function(player, key, value)
if key == "layout" then
player:chat(`Layout is now {value}.`)
end
end)@modlock.on_setting_changed
def changed(player: modlock.Player, key: str, value: str) -> None:
if key == "layout":
player.chat(f"Layout is now {value}.")mod.OnSettingChanged(func(p mod.Player, key, value string) {
if key == "layout" {
_ = p.Chat("Layout is now " + value + ".")
}
})Metrics
A metric is a running total for each player: a count, a sum or a maximum.
Declare it in mod.json, then add to it. The
optional label splits one metric into several totals.
player.addMetric('round.wins', 1)
player.addMetric('hud.opened', 1, 'compact')player:addMetric("round.wins", 1)
player:addMetric("hud.opened", 1, "compact")player.add_metric("round.wins", 1)
player.add_metric("hud.opened", 1, "compact")_ = p.AddMetric("round.wins", 1, nil)
label := "compact"
_ = p.AddMetric("hud.opened", 1, &label)When no host stores metrics, modlock dev logs each session's totals when the
session ends.
Boards
A board ranks players against each other. Declare it in
mod.json. Report each score with a value for
each of the board's partitions.
import { command, readBoard, reportScore } from 'modlock'
reportScore(player, 'aim.score', 4210, {
partition: { scenario: 'tracking', difficulty: 'hard' },
})
// Show the top five.
command('top', (player) => {
for (const row of readBoard('aim.score', { limit: 5 })?.rows ?? []) {
player.chat(`${row.rank}. ${row.name} ${row.value}`)
}
})modlock.reportScore(player, "aim.score", 4210, {
partition = { scenario = "tracking", difficulty = "hard" },
})
-- Show the top five.
modlock.command("top", function(player)
local page = modlock.readBoard("aim.score", { limit = 5 })
for _, row in if page then page.rows else {} do
player:chat(`{row.rank}. {row.name} {row.value}`)
end
end)modlock.report_score(
player, "aim.score", 4210, partition={"scenario": "tracking", "difficulty": "hard"}
)
# Show the top five.
@modlock.command("top")
def top(player: modlock.Player, args: str) -> None:
page = modlock.read_board("aim.score", limit=5)
for row in page.rows if page is not None else []:
player.chat(f"{row.rank}. {row.name} {row.value}")_ = p.ReportScore(&mod.ScoreReport{
Board: "aim.score",
Value: 4210,
Partition: map[string]string{"scenario": "tracking", "difficulty": "hard"},
})
// Show the top five.
mod.Command("top", func(p mod.Player, args string) {
page, err := mod.ReadBoard(&mod.BoardQuery{Board: "aim.score", Limit: 5})
if err != nil {
return
}
for _, row := range page.GetRows() {
_ = p.Chat(fmt.Sprintf("%d. %s %d", row.GetRank(), row.GetName(), row.GetValue()))
}
})| Call | Does |
|---|---|
reportScore(player, board, value, options?) | Reports one score. Returns whether the host accepted it. A host without boards accepts none, and the score goes to the mod's log instead. |
reportMatch(board, places, options?) | Reports a whole match's places to a rated board. Players who tie share a place. |
readBoard(board, read?) | Reads a page of a board, ten rows by default, as the host last read it. A page the host has not read yet comes back loading. |
Python spells them in snake_case with keyword options. Go spells them
Player.ReportScore, ReportMatch and ReadBoard, and returns the host's
error where the others return false.