# Daily and monthly quests

> Each player draws their own daily and monthly quests from your pools, swaps one a day, builds a streak, and gets what you set as the reward. New in 0.3, off by default.

Source: https://stygion.eu/docs/keystone/quests
Updated: 2026-10-25

New in 0.3, module `quests`, **off by default** (it hands things out). Everything is set in `quests.toml` (and the editor's Settings); nothing is tied to one server: the default pools are plain vanilla.

## How it works

- Each player draws their own quests at random: `daily.count` from `daily.quests` (default 3) and `monthly.count` from `monthly.quests` (default 3). A quest is drawn as often as its `weight` says; yesterday's quests come back only when the pool has too few others.
- A new quest day starts at `reset-at` (default `00:00`) in `time-zone` (empty = the server's); a new month at that hour on the 1st.
- `/quests reroll <id>` swaps one unfinished quest for another, `rerolls-per-day` times a quest day (default 1, 0 turns it off).
- Finishing every quest of a day or a month pays `daily.all-done` / `monthly.all-done` on top.
- **Streak:** a day counts when every daily quest is done (`streak.counts = all`) or one is (`any`). Missing a day starts it again. `streak.goals` pays at a number of days; with `streak.repeat` (default on) again at every multiple (7, 14, 21 …).
- Progress and done quests are kept in the server's database and survive a restart; nothing is paid twice.

## Writing a quest

One line per quest in `daily.quests` or `monthly.quests`, parts separated by ` | `:

```
id=ores | title=[en]Mine 16 ores[cs]Vytěž 16 rud | goal=mine #c:ores 16 | reward=item minecraft:diamond 2; keys 1 | reward-text=2 diamonds and a key | icon=minecraft:iron_ore | weight=2
```

Only `id` (one word) and `goal` are required. `title` and `reward-text` may carry `[en]…[cs]…` parts; without `title` the goal is shown as written, without `icon` one is chosen from the goal, without `reward-text` Keystone describes the rewards itself. A line that does not read is named in the log and skipped.

**Goals**

| Goal | Counts | How |
|---|---|---|
| `mine <id\|#tag\|*> <n>`, `kill …` | blocks broken, mobs killed | at once, from Keystone's own events |
| `place <id\|#tag\|*> <n>` | blocks put down | once the block has stood for `anti-farm.window` (see below) |
| `vote [n]` | votes (module `votes`) | at once; a vote while away counts and is paid at the next join |
| `biome <id\|#tag\|*> [n]` | different biomes stood in | where the player stands, every `look-every` (10 s) |
| `dimension <id>` | having been there | the same |
| `craft`, `use`, `eat`, `pickup`, `break <id\|#tag\|*> <n>` | the game's own statistics | the difference since the quest was dealt |
| `walk`, `swim`, `boat`, `minecart`, `horse`, `fly <blocks>` | distance travelled | the same |
| `fish <n>`, `trade <n>`, `play <minutes\|2h>` | fish caught, villager trades, time played | the same |
| `stat <category>/<id> <n>` | any statistic the game keeps | the same |

Statistics are read from the game's own file every `stats-every` (30 s) off the server thread; the game writes that file when it saves, so these trail an online player by up to one autosave.

**Anti-farm.** A placed block counts for a quest only once it has stood for `anti-farm.window` (default 30 minutes); broken again sooner, it never counts. Breaking a block of the same kind that the player put down in that chunk or the next one within the window is not mining, so sand that fell or a block a piston pushed is still their own; blocks that fall or turn into another (`anti-farm.loose`: sand, gravel, concrete powder and concrete, anvils …; ids, `#tags` or `*_suffix`) count as one kind. Nothing is forgotten to make room: a player with more than `anti-farm.most` (4096) blocks waiting gets nothing for placing or mining until the window has passed. `0` as the window turns all of it off.

**Rewards**, joined with `;` (the same words in `daily.all-done`, `monthly.all-done` and `streak.goals`, there one reward a line):

| Reward | What it does |
|---|---|
| `item <id> <n>` | items, written as `/give` takes them |
| `keys <n>` | crate keys (module `crates`) |
| `xp <n>` / `xp <n>L` | experience points / levels |
| `rank <group> for 7d` | a group for a time (without `for`, for good) |
| `perk <perk> for 1d` | a perk for a time |
| `badge <id>` | a badge (module `badges`) |
| `message <text>` | a line in their chat |
| `/<command>` | any command, run as the console; `{player}` is their name |

A streak line: `days=7 | reward=keys 2; xp 500 | reward-text=2 crate keys`.

## Commands and permissions

- `/quests` lists your quests in chat; with Keystone's client half it opens the quest screen. `/quests reroll <id>`. Permission `keystone.quests`, given to everybody (`default` group).
- `/keystone quests reset <player>` deals somebody fresh quests and gives their re-rolls back. Permission `keystone.quests.admin`.

## With the client half

The client half draws the quest screen and a HUD element and shows a notice when a quest is done. It gets the state under `quests.state` (when progress moves, at most every 2 seconds, and after joining) and sends `quests.reroll`; the HUD reads `quests` and `quests.resets` ([HUD values](/docs/keystone/hud-values)). All of it is held to `keystone.quests`. A player without the client half loses nothing: chat says the same.

## API and events

`GET /v1/quests` (scope `players.read`): the pools, the reset time, re-rolls and the streak rewards; with `?player=` that player's quests, progress and streak. Addons hear `QuestCompleted` (player, quest id, `daily` or `monthly`) after the reward was handed over.

Texts are in the language files under `quests.*`; change them in the editor's Languages screen.
