Developer API Guide
The API allows developers to access registered quests and to progress quests/quest pools for players. You can also create your own custom objectives and register Custom reward types, reward auto-correctors.
This guide assumes you are using 2.0+
You might also need to include Aikars ACF repo if you are using maven.
<repository>
<id>auroramc</id>
<url>https://repo.auroramc.gg/releases/</url>
</repository><dependency>
<groupId>gg.auroramc</groupId>
<artifactId>AuroraQuests</artifactId>
<version>{VERSION}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>gg.auroramc</groupId>
<artifactId>Aurora</artifactId>
<version>{VERSION}</version>
<scope>provided</scope>
</dependency>repositories {
maven {
url "https://repo.auroramc.gg/releases/"
}
}
dependencies {
compileOnly 'gg.auroramc:Aurora:{VERSION}'
compileOnly 'gg.auroramc:AuroraQuests:{VERSION}'
}repositories {
maven("https://repo.auroramc.gg/releases/")
}
dependencies {
compileOnly("gg.auroramc:Aurora:{VERSION}")
compileOnly("gg.auroramc:AuroraQuests:{VERSION}")
}The instance is only available after onLoad was called on AuroraQuests by your server software's plugin loader.
import gg.auroramc.quests.api.AuroraQuestsPlugin;
import gg.auroramc.quests.api.profile.ProfileManager;
import gg.auroramc.quests.api.questpool.QuestPool;
import gg.auroramc.quests.api.quest.Quest;
import gg.auroramc.quests.api.objective.Objective;
AuroraQuestsPlugin quests = AuroraQuestsPlugin.inst();
// Get the profile manager
ProfileManager manager = quests.getProfileManager();
// Get an online player's profile
Profile profile = manager.getProfile(player);
// From here you can get a specific quest pool or all of them from the online profile
QuestPool questPool = profile.getQuestPool(stringId);
Collection<QuestPool> pools = profile.getQuestPools();
// Get all the unlocked global quests or the rolled timed quests from the pool
List<Quest> activeQuests = questPool.getActiveQuests();
// Get all quests in the pool
Collection<Quest> quests = questPool.getQuests();
// Get quest by id
Quest quest = questPool.getQuest(stringId);
// Get the objective list from a quest
List<Objective> objectives = quest.getObjectives();
You can view how to add Custom rewards and correctors for them API. However, you will need to register them through the PoolManager. PoolManager is only initialized after onEnable was called. So you should properly declare you dependency on AuroraQuests and register rewards in your plugin onEnable .
A custom objective is a class you register under a task type name. Once registered, server owners can use it in any quest's tasks section exactly like a built-in one. You can also browse the built-in objectives for reference by clicking here.
Every objective:
Extends Objective (or one of the nicer subclasses, TypedObjective / StringTypedObjective).
Has a constructor with the exact signature (Quest, ObjectiveDefinition, Profile.TaskDataWrapper) that calls super(...). The factory instantiates your class reflectively using this signature, so it must match.
Overrides activate() — called once when the objective becomes active for a player (the quest is started and this task isn't completed yet). This is where you hook into events or start a scheduler.
Calls progress(amount, meta) whenever the player does the thing.
Useful members inherited from Objective:
this.target — the required amount, read from the task's args.amount (defaults to 1).
this.definition.getArgs() — the task's args as a configuration section, so you can read your own custom arguments (e.g. definition.getArgs().getInt("my-arg")).
onEvent(EventClass.class, this::handler, EventPriority.MONITOR) — subscribe to a Bukkit event through the plugin's optimized per-player event bus. If the event is a PlayerEvent, it is automatically filtered so your handler only runs for the player this objective belongs to. Subscriptions are cleaned up automatically when the objective is completed or disposed.
asyncInterval(runnable, delayTicks, intervalTicks) / syncInterval(runnable, delay, interval, global) — run a repeating task instead of (or alongside) listening to events. Useful for polling-style objectives (this is how the built-in PLAY_TIME works). These tasks are also cancelled automatically on dispose.
meta() / meta(location) — build the ObjectiveMeta that progress needs. Filters (worlds, regions, biomes, Y-level, …) are checked against this meta, so pass the relevant location when the action happens somewhere specific.
You never handle the {status}, {current} or {required} placeholders, filters, the aurora.quests.use permission check, or the WorldGuard progress flag yourself — progress(...) runs all of that for you before it counts anything.
An objective that counts how many times a player uses a riptide trident:
package com.example.quests;
import gg.auroramc.quests.api.objective.Objective;
import gg.auroramc.quests.api.objective.ObjectiveDefinition;
import gg.auroramc.quests.api.profile.Profile;
import gg.auroramc.quests.api.quest.Quest;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerRiptideEvent;
public class RiptideObjective extends Objective {
public RiptideObjective(Quest quest, ObjectiveDefinition definition, Profile.TaskDataWrapper data) {
super(quest, definition, data);
}
@Override
protected void activate() {
onEvent(PlayerRiptideEvent.class, this::handle, EventPriority.MONITOR);
}
private void handle(PlayerRiptideEvent event) {
// The event is a PlayerEvent, so this only fires for our player.
progress(1, meta());
}
}
Using it in a quest is now identical to a built-in objective:
tasks:
riptide:
task: RIPTIDE
display: "{status} &fUse riptide {required} times &b{current}&f/&b{required}"
args:
amount: 15
And register it in your plugin's onLoad (not onEnable — objectives must be registered before pools are parsed):
import gg.auroramc.quests.api.factory.ObjectiveFactory;
@Override
public void onLoad() {
ObjectiveFactory.registerObjective("RIPTIDE", RiptideObjective.class);
}
The name you register is the task: value players will use; it is matched case-insensitively.
types and multipliers)If your objective should match a list of types (and support the per-type multipliers feature), extend TypedObjective instead. It wires up the types filter and multipliers for you — you just resolve the type that triggered the event and pass it to meta(...).
This objective counts right-clicking with specific items:
package com.example.quests;
import gg.auroramc.aurora.api.AuroraAPI;
import gg.auroramc.quests.api.objective.ObjectiveDefinition;
import gg.auroramc.quests.api.objective.TypedObjective;
import gg.auroramc.quests.api.profile.Profile;
import gg.auroramc.quests.api.quest.Quest;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerInteractEvent;
public class UseItemObjective extends TypedObjective {
public UseItemObjective(Quest quest, ObjectiveDefinition definition, Profile.TaskDataWrapper data) {
super(quest, definition, data);
}
@Override
protected void activate() {
onEvent(PlayerInteractEvent.class, this::handle, EventPriority.MONITOR);
}
private void handle(PlayerInteractEvent event) {
if (!event.getAction().isRightClick() || event.getItem() == null) return;
// Resolve the id of the held item (vanilla or any supported custom-item plugin).
var type = AuroraAPI.getItemManager().resolveId(event.getItem());
// TypedObjective checks `type` against the configured `types` list and applies
// any matching `multipliers` automatically. Progress only counts if it matches.
progress(1, meta(type));
}
}
tasks:
use_wand:
task: USE_ITEM
display: "{status} &fUse your wand {required} times &b{current}&f/&b{required}"
args:
amount: 10
types:
- "minecraft:stick"
- "nexo:magic_wand"
# optional per-type multiplier, counts the nexo wand as 2
multipliers:
"nexo:magic_wand": 2
StringTypedObjective works the same way but matches plain strings instead of item/entity TypeIds — use it when the thing you match isn't an item or entity (the built-in UNLOCK_VAULT uses it to match vault loot-table keys).
By calling our provided API events you can progress all objectives in every quest pool for a player automatically. A bunch of built-in objectives support this.
BLOCK_BREAK, BUILD - PlayerBreakCustomBlockEvent
BLOCK_PLACE, BUILD - PlayerPlaceCustomBlockEvent
FISH - PlayerCaughtFishEvent
COMPLETE_DUNGEON - PlayerCompleteDungeonEvent
CRAFT - PlayerCraftedItemEvent
SELL_WORTH - PlayerEarnFromSellEvent
SELL - PlayerSellItemEvent
BUY - PlayerPurchaseItemEvent
BUY_WORTH - PlayerSpendOnPurchaseEvent
ENTER_REGION - PlayerEnterRegionEvent
INTERACT_NPC - PlayerInteractNpcEvent
KILL_MOB - PlayerKillMobEvent
BLOCK_LOOT, FARM, ENTITY_LOOT - PlayerLootEvent
TAKE_ITEM - PlayerTakeItemEvent
UPGRADE_ISLAND - PlayerUpgradeIslandEvent
JOIN_ISLAND - PlayerJoinIslandEvent
REACH_ISLAND_LEVEL - PlayerIslandLevelChangeEvent
REACH_ISLAND_WORTH - PlayerIslandWorthChangeEvent
You should always use the event based approach when you can.
I assume you read the previous sections, so you can get the list of objectives you want to progress.
Let's say, you want to progress all CONSUME objectives.
// Get the objective list from a quest
List<Objective> objectives = quest.getObjectives();
for (Objective objective : objectives) {
if (objective instanceof ConsumeObjective consumeObjective) {
int amount = 10;
// Since ConsumeObjective extends TypedObjective (like most of the objectives)
// we can call the helper method to create a meta object. Objective filters will
// check this meta. Meta is required.
ObjectiveMeta meta = consumeObjective.meta(new TypeId("minecraft", "chicken"));
consumeObjective.progress(10, meta);
}
}