A Paper Minecraft plugin that generates a new daily quest for your server every day, delivered through a Citizens NPC. Quests are automatically generated and validated across many types — delivering items, herding mobs, exploring biomes, discovering structures, fighting mobs, crafting, trading with villagers, and biome-specific gathering — with optional timed BossBar challenges. Players earn points and build streaks with no manual quest writing required.
| Dependency | Version |
|---|---|
| Paper | 1.21.4+ |
| Citizens | 2.x |
| Java | 21+ |
| Plugin | Effect |
|---|---|
| ConsentPvP | Soft dependency. When installed, a player can only be teleported to (via /tscroll player) if they have consented (getPVPManager().hasConsent). Players who haven't opted in can't be teleported to. When absent, teleport consent checks are skipped. |
- Drop
QuestKeeper-1.0.0-SNAPSHOT.jarinto yourplugins/folder. - Ensure Citizens is installed and enabled.
- Start (or reload) the server — the plugin generates its first quest automatically.
- Create your quest-giver NPC with Citizens:
/npc create QuestKeeper - Note the NPC's ID from
/npc list. - Open
plugins/QuestKeeper/config.ymland set:quest-npc-id: <id>
- Reload the server or run
/questkeeper forcerefresh.
Players can now right-click the NPC to view and complete the daily quest.
- On startup, QuestKeeper loads the stored quest for today, or generates a fresh one if none exists.
- A background scheduler checks every 5 minutes whether the date has changed. When it does, a new quest is generated automatically — no cron jobs, no exact-midnight timing required.
- There is one active quest for the whole server per day. Every player can complete it once.
QuestKeeper uses a modular quest-type framework (registry pattern). Each type has its own generator and is validated automatically — adding a new type does not require touching the core generation logic.
| Type | Completion | Description |
|---|---|---|
ITEM_DELIVERY |
NPC turn-in | Collect a set number of an item and right-click the NPC. Items are removed on success. |
MOB_DELIVERY |
NPC turn-in | Bring passive mobs within 6 blocks of the NPC. Mobs are removed on success. |
EXPLORATION |
Automatic | Visit a target biome (e.g. Desert, Jungle). Completes the instant you arrive. |
STRUCTURE_DISCOVERY |
Automatic | Find a generated structure (Village, Stronghold, Monument…) located via Paper structure APIs. |
COMBAT |
Automatic | Kill N of a mob. Only direct player kills count. |
CRAFTING |
Automatic | Craft N of an item. Only items crafted after the quest started count. |
VILLAGER_TRADING |
Automatic | Complete N villager trades. |
BIOME_COLLECTION |
Automatic | Collect items from their natural biome. Player-placed blocks never count (anti-exploit). |
ESCORT |
(roadmap) | Experimental escort framework — architecture only, disabled by default. |
Timed quests: eligible quests may be generated with a countdown shown as a
BossBar (Quest Expires In: MM:SS). Failing to finish in time fails the quest
for the day. Configure via timed-quests in config.yml.
Quest selection is weighted and configurable under quest-generator.weights.
Generated quests target a 5–30 minute completion window for an average player
(no Nether Stars, Elytra, Ancient Debris, or dragon kills).
Quests are generated automatically from internal curated pools. No external config files are needed to define quests.
- Completing a quest awards a flat 1 Quest Point — every quest currently gives the same amount. Per-quest reward scaling is a planned future feature.
- Completing on consecutive days increments your streak. Missing a day resets it to 1.
- The streak is tracked and displayed but does not currently add bonus points — each completion is worth exactly 1 point. The
streak.bonus-per-day/streak.max-bonussettings are retained for a future re-enable.
Primary command: /questkeeper — Alias: /qk
| Command | Description |
|---|---|
/qk show |
Display today's active quest, target, and base reward. |
/qk points |
Show your own total points and current streak. |
/qk points <player> |
Show another player's points and streak. (requires questkeeper.admin) |
Requires the questkeeper.admin permission (default: op).
| Command | Description |
|---|---|
/qk reroll |
Generate a new quest for today. Players who already completed the old quest keep their completion — rerolls only affect players who haven't submitted yet. Broadcasts the new quest to the server. |
/qk resetplayer <player> |
Clear a player's daily completion flag so they can submit again. Useful for testing or correcting mistakes. |
/qk forcerefresh |
Manually trigger the daily reset check right now. Use after changing quest-npc-id or to test date-change behavior. |
/qk stats |
Show aggregate statistics: quests generated/completed/failed, average completion time, and per-type usage. |
/qk debug |
Print internal plugin state: active quest, server date, and (if run as a player) your own profile data. |
| Permission | Default | Description |
|---|---|---|
questkeeper.use |
true |
Use the daily quest system (NPC interaction, /qk show, /qk points). |
questkeeper.admin |
op |
Access admin commands: reroll, resetplayer, forcerefresh, stats, debug, and viewing other players' points. |
questkeeper.scroll.use |
true |
Use the Teleport Scroll system (/tscroll). |
plugins/QuestKeeper/config.yml
# NPC ID of the quest giver (run /npc list to find it)
quest-npc-id: -1
# Server timezone for daily reset
# Examples: "UTC", "America/New_York", "Europe/London", "Asia/Tokyo"
timezone: "UTC"
# Streak bonus settings
streak:
bonus-per-day: 2 # Points added per streak day beyond the first
max-bonus: 10 # Maximum streak bonus
# Radius around the NPC to search for mobs (mob delivery quests)
mob-search:
horizontal-radius: 6.0
vertical-radius: 4.0
# Broadcast the new daily quest to all players when it changes
broadcast-new-quest: true
# Quest generator selection weights (relative; a weight of 0 disables a type)
quest-generator:
weights:
item_delivery: 25
mob_delivery: 20
exploration: 15
structure_discovery: 10
combat: 15
crafting: 10
villager_trading: 3
biome_collection: 2
# Timed quest modifier (BossBar countdown)
timed-quests:
chance: 0.15 # 0.0–1.0 probability an eligible quest becomes timed
min-seconds: 600
max-seconds: 1200
bonus-reward: 6 # extra points for finishing in time (currently overridden by the flat 1-point reward)
# Exploration / structure discovery
exploration:
structure-discovery-radius: 24 # blocks from a located structure to "discover" it
structure-search-radius: 6400 # locateNearestStructure search radius
# Escort quests (experimental roadmap feature — leave disabled)
escort:
enabled: falsePersisted statistics live in stats.yml; per-player event-quest progress (and
timed-quest state) is stored alongside player data so it survives restarts.
Both files are created automatically in plugins/QuestKeeper/.
Stores the current active quest. Regenerated each day.
activeQuest:
date: "2026-03-11"
type: "ITEM_DELIVERY"
target: "OAK_LOG"
amount: 48
rewardPoints: 12
description: "Bring 48 Oak Logs to the Quest Keeper"Stores per-player state. Updated immediately on quest completion.
players:
123e4567-e89b-12d3-a456-426614174000:
lastCompletionDate: "2026-03-10" # used for streak logic
completedQuestDate: "2026-03-10" # used to block same-day re-submission
streak: 4
totalPoints: 87
scrollCharges: 2 # Teleport Scroll charges
questsCompleted: 130 # lifetime completions (leaderboard)
# Event-quest progress state (auto-completing quest types):
progressQuestDate: "2026-03-11" # quest day the progress applies to
progressCount: 3 # progress toward the active event quest
failedQuestDate: "" # set if a timed quest expired that day
timedStartMillis: 0 # epoch ms a timed quest started (0 = none)
progressMeta: {} # free-form (e.g. resolved structure coords)
# Shop perks:
streakShieldCharges: 1 # Streak Shields held (auto-spent on a missed day)
chatColorCode: "c" # active chat colour code ("" = none)
chatColorExpiryMillis: 0 # epoch ms the chat colour expires
unlockedEffects: [] # unlocked completion effects (enum names)
selectedEffect: "" # active completion effect ("" = none)Aggregate statistics surfaced by /qk stats.
questsGenerated: 42
questsCompleted: 130
questsFailed: 7
totalCompletionSeconds: 5400 # used to derive average completion time
questTypeUsage:
item_delivery: 12
combat: 9
exploration: 6Materials are drawn from an internal curated pool that covers common, farmable, and renewable items — things players are realistically expected to gather. The pool includes terrain blocks, logs, wool, farm produce, fish, mob drops, ores, and flowers.
Amount scales with rarity tier (the per-tier reward shown below is the generator's internal suggestion — it is currently overridden so every quest awards a flat 1 Quest Point):
| Tier | Example materials | Amount range |
|---|---|---|
| Very common | Dirt, Cobblestone, Sand | 48–64 |
| Common | Oak Log, Coal, Wheat, Feather | 24–48 |
| Uncommon | Iron Ingot, Bread, Slime Ball | 8–16 |
| Rare | Gold Ingot, Blaze Rod, Ender Pearl | 4–8 |
Only passive, herding-friendly mobs are included: Cow, Sheep, Pig, Chicken, Rabbit, Goat. Hostile mobs, bosses, water mobs, and villagers are excluded.
Mob amounts are intentionally low (2–5) since herding takes more effort than gathering items.
Event (auto-completing) quests draw from internal pools designed for a 5–30 minute completion window:
| Type | Pool / targets |
|---|---|
| Exploration | Desert, Jungle, Snowy Plains, Mushroom Fields, Badlands, Savanna, Taiga, Swamp, Plains, Forest |
| Structure Discovery | Village, Stronghold, Ocean Monument, Nether Fortress, Bastion Remnant, Pillager Outpost, Woodland Mansion, Desert Pyramid |
| Combat | Zombie, Skeleton, Creeper, Spider, Enderman (variants count: e.g. Stray/Bogged/Wither Skeleton → Skeleton; Husk/Drowned/Zombie Villager → Zombie; Cave Spider → Spider) |
| Crafting | Bread, Torch, Glass, Ladder, Stick, Chest, Furnace, Iron Chestplate, Cake |
| Villager Trading | Any completed trade, or trades involving a specific item drawn from across all professions (Wheat, Coal, Paper, Leather, Porkchop, Ink Sac, …). Item-specific trades are validated against the villager's live trade recipe |
| Biome Collection | Snowballs (Snowy Plains), Cocoa Beans (Jungle), Cactus (Desert), Mushrooms (Mushroom Fields), Warped Fungus (Warped Forest) |
Biome-collection only credits naturally generated blocks broken in the target biome; blocks the player placed are tracked and never count.
When the timed modifier applies (probability set by timed-quests.chance), an
eligible quest gains a countdown rendered as a BossBar (Quest Expires In: MM:SS).
The countdown starts the first time the player is seen with the quest that day.
Running out of time fails the quest for the day. (A configured timed bonus exists but is currently overridden while all quests award a flat 1 point.)
- Escort quests — modular
EscortQuestServicebehind theescort.enabledflag, disabled by default (architecture only). - Dynamic difficulty —
QuestDifficulty(EASY/NORMAL/HARD/ELITE) plus aDifficultyCalculatorstrategy interface, ready to weigh playtime, completions, streak, inventory value and XP level. The default calculator always returnsNORMAL, so balance is unchanged until a real implementation is wired in.
The generator will not produce the same type + target combination two days in a row (up to 20 retry attempts). The same logic applies when rerolling mid-day.
git clone https://github.com/ModularSoftAU/QuestKeeper.git
cd QuestKeeper
mvn packageThe built jar will be at target/QuestKeeper-1.0.0-SNAPSHOT.jar.
src/main/java/dev/anchorlight/questkeeper/
├── QuestKeeperPlugin.java # Plugin entry point + service wiring
├── quest/
│ ├── DailyQuest.java # Immutable quest object (+ metadata, time limit)
│ ├── QuestType.java # All quest types + DELIVERY/EVENT category
│ ├── QuestSubmissionResult.java # Typed result from submit()
│ ├── QuestGenerator.java # Weighted orchestration + timed modifier + retry
│ ├── QuestValidationService.java # Validates quests and detects similarity
│ ├── QuestManager.java # Core state: load, generate, submit, reroll, award
│ ├── QuestProgressService.java # Per-player event-quest progress + completion
│ ├── TimedQuestService.java # BossBar countdown + timed failure handling
│ ├── QuestLifecycleListener.java # Observer hook (quest changed / completed)
│ ├── ItemQuestGenerator.java # Item delivery pool (legacy, reused)
│ ├── MobQuestGenerator.java # Mob delivery pool (legacy, reused)
│ ├── framework/
│ │ ├── QuestTypeGenerator.java # Per-type generator interface
│ │ ├── QuestTypeRegistry.java # Registry pattern + weighted selection
│ │ └── TimedQuestModifier.java # Turns eligible quests into timed quests
│ └── generator/
│ ├── ItemDeliveryGenerator.java # Adapters + new event-quest generators
│ ├── MobDeliveryGenerator.java
│ ├── ExplorationQuestGenerator.java
│ ├── StructureDiscoveryGenerator.java
│ ├── CombatQuestGenerator.java
│ ├── CraftingQuestGenerator.java
│ ├── VillagerTradingQuestGenerator.java
│ └── BiomeCollectionQuestGenerator.java
├── listener/ # Event-driven quest validation
│ ├── QuestCombatListener.java # EntityDeathEvent (direct kills)
│ ├── QuestCraftingListener.java # CraftItemEvent
│ ├── QuestTradingListener.java # Merchant trade completion
│ ├── QuestCollectionListener.java # Block break/place (+ anti-exploit)
│ └── QuestExplorationListener.java # Biome arrival + structure discovery + join init
├── stats/
│ └── QuestStatisticsService.java # stats.yml counters (/qk stats)
├── difficulty/ # Dynamic difficulty framework (roadmap)
│ ├── QuestDifficulty.java
│ ├── DifficultyContext.java
│ ├── DifficultyCalculator.java
│ └── DefaultDifficultyCalculator.java
├── escort/
│ └── EscortQuestService.java # Escort framework (disabled by default)
├── integration/ # Optional plugin hooks
│ ├── PvpConsentService.java # Consent abstraction (soft dependency)
│ ├── AllowAllConsentService.java # Fallback when ConsentPvP is absent
│ └── ConsentPvpService.java # ConsentPvP-backed implementation
├── player/
│ ├── PlayerQuestProfile.java # Per-player streak, points, progress, timed state
│ ├── PlayerDataManager.java # In-memory cache backed by PlayerStorage
│ └── StreakService.java # Streak increment/reset logic
├── npc/
│ ├── CitizensHook.java # Citizens API wrapper
│ ├── NpcInteractListener.java # Handles NPCRightClickEvent
│ └── NpcDialogueService.java # Adventure API player messages
├── reward/
│ └── RewardService.java # Base + streak bonus calculation
├── scroll/ # Teleport Scroll system (warps, GUI, charges)
│ ├── ScrollConfig.java
│ ├── ScrollWarp.java
│ ├── TeleportScrollManager.java
│ ├── TeleportScrollGui.java
│ └── TeleportRequest.java
├── shop/
│ ├── QuestShopGui.java # Main shop GUI (supports sub-menu openers)
│ ├── ShopItem.java # Pluggable shop item interface
│ ├── ShopExtrasConfig.java # Config for the perk items below
│ ├── TeleportScrollShopItem.java
│ ├── StreakShieldShopItem.java # One-use streak protection
│ ├── TimedQuestExtensionShopItem.java# Adds time to an in-progress timed quest
│ ├── ChatColorShopItem.java # Opener → ChatColorGui
│ ├── ChatColorGui.java # Time-limited chat-colour picker
│ ├── ChatColorOption.java
│ ├── ChatColorService.java # Colours chat while a pass is active
│ ├── CompletionEffectShopItem.java # Opener → CompletionEffectGui
│ ├── CompletionEffectGui.java # Effect picker (unlock + select)
│ ├── CompletionEffect.java
│ └── CompletionEffectService.java # Plays the chosen effect on completion
├── command/
│ ├── QuestKeeperCommand.java # /questkeeper command handler
│ └── QuestKeeperTabCompleter.java
├── storage/
│ ├── QuestStorage.java # quest.yml read/write (+ metadata, time limit)
│ └── PlayerStorage.java # players.yml read/write (+ progress fields)
├── scheduler/
│ └── DailyResetScheduler.java # 5-minute polling for date changes
└── util/
├── MaterialUtil.java # Material pool + tier-based scaling
├── EntityUtil.java # Mob pool + reward scaling
├── BiomeUtil.java # Exploration biomes + biome lookup
├── StructureUtil.java # Structure registry + locate helpers
├── DateUtil.java # ZoneId-aware date helpers
└── TextUtil.java # Enum name formatting, pluralization