Astral Realms Documentation Help

Developer API

AstralFarmPass exposes its services as getters on the main plugin instance, keeps per-player state in an AstralSync snapshot, fires one custom event, and has one real extension point — the quest-goal registry.

Maven coordinates

<dependency> <groupId>com.astralrealms</groupId> <artifactId>farmpass</artifactId> <version>2.0-SNAPSHOT</version> <scope>provided</scope> </dependency>

Repository: https://maven.astralrealms.fr/repository/maven-public/.

Plugin services

AstralFarmPass plugin = JavaPlugin.getPlugin(AstralFarmPass.class); BlueprintService blueprints = plugin.blueprints(); QuestService quests = plugin.quests(); EventService events = plugin.events(); MultiKillService multiKills = plugin.multiKills(); MenuContainer menus = plugin.menus(); FarmPassConfiguration configuration = plugin.configuration(); RewardsConfiguration rewards = plugin.rewards();

Getter

Type

Use

plugin.blueprints()

BlueprintService

Look up loaded blueprints, and the load fingerprint.

plugin.quests()

QuestService

The month's quests, the current season id, and recording progress.

plugin.events()

EventService

Internal — owns the generated listeners; unloaded on disable.

plugin.multiKills()

MultiKillService

Internal — batches same-tick kills into PlayerMultiKillEvent.

plugin.menus()

MenuContainer

The plugin's menus (farmpass-main, pass-weekly_quest).

plugin.configuration()

FarmPassConfiguration

Zone, salt, value-step, premium permission, categories.

plugin.rewards()

RewardsConfiguration

The reward ladder; findById(String).

There is no database and no repository layer — see Seasons & Generation.

QuestService

Method

Returns

Use

currentSeason()

String

The current season id (2026-08), re-checking the month boundary first.

quests(QuestType)

Collection<QuestInstance>

Every non-expired quest of that type, including the still-locked ones.

activeQuests(QuestType)

Collection<QuestInstance>

Only the unlocked, non-expired ones.

generate(QuestType, YearMonth)

List<QuestInstance>

Derives an arbitrary month's quests without touching the cached one — useful to preview next month. Returns an empty list if the type has no category or no available blueprint.

progress(QuestGoalContext)

void

Runs a context against every active quest, recording progress and awarding points.

The three read methods and progress are synchronized and re-check the month boundary on entry, so a rollover is picked up by the first call that crosses it.

BlueprintService

Method

Returns

Use

findById(String)

Optional<QuestBlueprint>

Look a blueprint up by id.

findByType(QuestType)

Collection<QuestBlueprint>

Every blueprint of a type, in sorted id order.

all()

Collection<QuestBlueprint>

Every loaded blueprint, in sorted id order.

fingerprint()

String

12-character hash of everything that influences generation — see Fingerprint.

Iteration order is deliberately sorted: generation walks these collections and must do so identically on every server.

PlayerMultiKillEvent

Fully qualified name: com.astralrealms.farmpass.model.event.PlayerMultiKillEvent (extends AstralCore's AbstractEvent). Not cancellable — it is a notification.

Fired by MultiKillService once a player has killed several mobs of the same kind within a single tick, which in practice means a single swing: a sweeping attack, an explosion, a piercing arrow. Deaths are batched per player per tick and flushed on the next one, when the whole swing has resolved; a batch that ends up with a single kill fires nothing. Accessors are fluent (Lombok @Accessors(fluent = true)).

Fields

Field

Type

Description

player()

Player

The killer, resolved from the damage source's causing entity.

type()

EntityType

The species killed — one event per species in the batch.

kills()

int

How many of them died in that tick. Always > 1.

weapon()

ItemStack

A clone of what the player held in their main hand when the batch opened, so a quest can ask for a specific weapon.

Example listener

@EventHandler public void onMultiKill(PlayerMultiKillEvent event) { if (event.kills() >= 10) event.player().sendMessage("Nice swing!"); }

It is consumed internally by the entity-multi-kill goal.

Extending the goal framework

A goal is a @ConfigSerializable class implementing QuestGoal:

public interface QuestGoal { QuestGoalResult handle(QuestGoalContext context); default Collection<?> values() { return List.of(); } }
  • handle receives the adapted event and returns what it made of it.

  • values() is what the quest tooltip lists — the blocks, mobs or biomes the goal watches. An empty collection means "anything counts", which a description says better than a list would. Vanilla Keyed/Translatable values render client-side translated; see %value_…%.

QuestGoalContext

Component

Type

Populated when

player()

Player

Always — an adapter that cannot find a player drops the event instead.

event()

Event

Always: the original Bukkit event, which goals pattern-match on.

block()

@Nullable Block

Block break/place/harvest, interact, enchant (the table), vault.

itemStack()

@Nullable ItemStack

Block place (the held item), craft (the result), enchant (the item), interact, scroll.

entity()

@Nullable Entity

Kill, breed, fertilize-egg (the mother), leash, potion (the player), fish (the catch), loot.

QuestGoalResult

Factory

Effect

notHandled()

The goal does not recognise this event.

handled(int delta)

Adds delta, capped at the target.

distinct(String key)

Adds 1, only the first time key is seen for that quest.

best(int value)

Raises progress to value when it beats the current one, instead of accumulating.

Registering

@ConfigSerializable public record BlockBrushQuestGoal(Set<Material> materials) implements QuestGoal { @Override public QuestGoalResult handle(QuestGoalContext context) { // ... } }

QuestGoalRegistry and EventAdapterRegistry are both static, immutable maps compiled into the plugin: a new goal type, and the event adapter it needs if that event is not listened to yet, are added by editing those two classes and rebuilding. There is no runtime registration hook — an event missing from EventAdapterRegistry is an event no goal will ever see.

QuestGoalTypeSerializer resolves goal.type through QuestGoalRegistry and then deserializes the rest of the goal node into that class, so a record's components become the block's kebab-cased YAML keys. Serialization back to YAML is deliberately unsupported.

Player data: FarmPassPlayerData

The AstralSync snapshot behind every player's pass, keyed farmpass:data (FarmPassSnapshotAdapter).

Optional<FarmPassPlayerData> data = SyncAPI.findData(player.getUniqueId(), FarmPassPlayerData.class);

Member

Type

Meaning

season()

String

The month this data belongs to.

progress()

Map<UUID, Integer>

Progress per quest id.

discoveries()

Map<UUID, Set<String>>

Distinct keys already counted per quest.

claimedRewards()

Set<String>

"<reward id>_<free\|premium>" entries.

points()

int

Season points.

progress(QuestInstance)

int

Current progress on a quest.

isCompleted(QuestInstance)

boolean

Whether it reached its target.

progress(QuestInstance, int)

int

Adds progress, capped at the target; returns the new total.

best(QuestInstance, int)

int

Raises progress to a value if it beats the current one.

discover(QuestInstance, String)

boolean

Records a distinct key; true if it was new.

points(IntUnaryOperator)

void

Updates the points.

reset(String season)

void

Re-stamps the season and clears everything else.

Quests themselves are not stored: every server re-generates them identically, so only what the player did with them travels. That is also why nothing has to be reconciled on a server switch — the adapter's apply only compares the stamped season with the current one, and resets the data when they differ.

Consumed upstream events

EventAdapterRegistry maps 20 event classes to the adapter that pulls a player out of them. Listeners are registered from that map alone, at MONITOR priority, through ReflectionUtils.forceRegisterEvent — a lightweight RegisteredListener per event rather than a reflective @EventHandler per type. Cancelled events never reach a goal.

Source

Events

Bukkit — blocks

BlockBreakEvent, BlockPlaceEvent, PlayerHarvestBlockEvent

Bukkit — entities

EntityDeathEvent, EntityBreedEvent, EntityFertilizeEggEvent (Paper), PlayerLeashEntityEvent, EntityPotionEffectEvent

Bukkit — items

CraftItemEvent, EnchantItemEvent, PlayerFishEvent

Bukkit — players

PlayerInteractEvent, PlayerMoveEvent, LootGenerateEvent, VaultChangeStateEvent (Paper)

AstralFarmPass

PlayerMultiKillEvent

AstralVote

PlayerVoteEvent

AstralScrolls

ScrollCompletedEvent

AstralTower-Bridge

TowerFinishedEvent

AstralShop

ShopSellEvent

Registered action

claim-farmpass-reward — see Rewards. Registered with registerAction, i.e. on the plugin's own registry, so it resolves in AstralFarmPass's configuration only.

Last modified: 25 September 2026