Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

QuestKeeper

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.


Requirements

Dependency Version
Paper 1.21.4+
Citizens 2.x
Java 21+

Optional integrations

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.

Installation

  1. Drop QuestKeeper-1.0.0-SNAPSHOT.jar into your plugins/ folder.
  2. Ensure Citizens is installed and enabled.
  3. Start (or reload) the server — the plugin generates its first quest automatically.
  4. Create your quest-giver NPC with Citizens:
    /npc create QuestKeeper
    
  5. Note the NPC's ID from /npc list.
  6. Open plugins/QuestKeeper/config.yml and set:
    quest-npc-id: <id>
  7. Reload the server or run /questkeeper forcerefresh.

Players can now right-click the NPC to view and complete the daily quest.


How It Works

Daily quest lifecycle

  • 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.

Quest types

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.

Points and streaks

  • 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-bonus settings are retained for a future re-enable.

Commands

Primary command: /questkeeper — Alias: /qk

Player commands

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)

Admin commands

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.

Permissions

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).

Configuration

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: false

Persisted statistics live in stats.yml; per-player event-quest progress (and timed-quest state) is stored alongside player data so it survives restarts.


Data Files

Both files are created automatically in plugins/QuestKeeper/.

quest.yml

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"

players.yml

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)

stats.yml

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: 6

Quest Generation Details

Item delivery pool

Materials 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

Mob delivery pool

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 quest pools

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.

Timed quests

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.)

Roadmap frameworks

  • Escort quests — modular EscortQuestService behind the escort.enabled flag, disabled by default (architecture only).
  • Dynamic difficultyQuestDifficulty (EASY/NORMAL/HARD/ELITE) plus a DifficultyCalculator strategy interface, ready to weigh playtime, completions, streak, inventory value and XP level. The default calculator always returns NORMAL, so balance is unchanged until a real implementation is wired in.

Repeat prevention

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.


Building from Source

git clone https://github.com/ModularSoftAU/QuestKeeper.git
cd QuestKeeper
mvn package

The built jar will be at target/QuestKeeper-1.0.0-SNAPSHOT.jar.


Module Layout

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