Skip to main content

๐ŸŽฏ EventObjectiveHandler API

An objective handler controls a custom objective type.

It tells EventForge:

what the objective type is called
how to load its config
what to do when the event starts
what to do when the event stops
how to react during the event

Basic handler

public final class RelicHuntObjectiveHandler implements EventObjectiveHandler<RelicHuntObjectiveData> {

@Override
public String getType() {
return "RELIC_HUNT";
}

@Override
public ObjectiveLoadResult<RelicHuntObjectiveData> load(EventObjectiveLoadContext context) {
RelicHuntObjectiveData data = new RelicHuntObjectiveData();

return ObjectiveLoadResult.success(data);
}

@Override
public void onStart(EventObjectiveSession session, RelicHuntObjectiveData data) {
// Event started.
}

@Override
public void onStop(EventObjectiveSession session, RelicHuntObjectiveData data) {
// Event stopped.
}
}

Objective type

The objective type is the ID server owners use in event files.

@Override
public String getType() {
return "RELIC_HUNT";
}

Config example:

objective:
type: RELIC_HUNT

Use a clear uppercase ID.

Good examples:

RELIC_HUNT
SUPPLY_DROP
PARKOUR_RACE
BOSS_DAMAGE

Loading config

The load(...) method reads the event config and returns objective data.

@Override
public ObjectiveLoadResult<RelicHuntObjectiveData> load(EventObjectiveLoadContext context) {
String path = context.getObjectivePath();

int hintInterval = context.getConfig().getInt(path + ".hint-interval", 30);

RelicHuntObjectiveData data = new RelicHuntObjectiveData(hintInterval);

return ObjectiveLoadResult.success(data);
}

Load context

EventObjectiveLoadContext gives your handler access to useful loading tools.

Common methods:

context.getConfig();
context.getEventId();
context.getObjectivePath();
context.warning("Message");
context.error("Message");
context.loadActions("path.to.actions");

Use warnings for soft issues.

Use errors or failed load results when the objective cannot work.


Returning success

Return success when your objective loaded correctly.

return ObjectiveLoadResult.success(data);

Returning failure

Return failure when the objective cannot run.

return ObjectiveLoadResult.failure("No relic locations were configured.");

This helps /eventforge validate show useful information to server owners.


Loading actions

Custom objectives can load EventForge actions from config.

List<EventAction> actions = context.loadActions(path + ".actions");

Store those actions in your objective data.

Later, run them with ActionService.

EventForgeAPI.getActionService().executeAll(
session.getEventId(),
player,
actions,
placeholders
);

Event start

onStart(...) runs when the event starts.

@Override
public void onStart(EventObjectiveSession session, RelicHuntObjectiveData data) {
Bukkit.broadcastMessage("Relic Hunt started!");
}

Use this to:

prepare runtime state
spawn temporary objects
start timers
send start messages
cache event data

Event stop

onStop(...) runs when the objective stops.

@Override
public void onStop(EventObjectiveSession session, RelicHuntObjectiveData data) {
// Clean up runtime state.
}

Use this to clean up:

spawned entities
temporary blocks
timers
cooldowns
cached player state

Awarding score

Use EventObjectiveSession#addScore(...).

session.addScore(player, 5);

EventForge handles score updates, leaderboards, PlaceholderAPI data, score events, and milestone checks.


Using variables and text effects

Custom objectives should use the public services instead of duplicating parsing.

String message = "{var:event_color}{event_display} &7custom objective message.";

String parsedVariables = EventForgeAPI.getVariableService().parse(
session.getEventId(),
player,
message
);

String parsedText = EventForgeAPI.getTextEffectService().parse(parsedVariables);

player.sendMessage(parsedText);

Using custom placeholders

When running actions, provide your own placeholders.

Map<String, String> placeholders = new HashMap<>();
placeholders.put("relic", "ancient_relic");
placeholders.put("relic_display", "Ancient Relic");
placeholders.put("score_change", "5");

EventForgeAPI.getActionService().executeAll(
session.getEventId(),
player,
actions,
placeholders
);

Then server owners can use:

actions:
- type: MESSAGE
message: "&aYou found &f{relic_display}&a! &7(+{score_change})"

Bukkit listeners

Objective handlers can also listen to normal Bukkit events.

@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();

// Check if the player is part of an active custom objective.
}

Register the listener in your addon plugin.

getServer().getPluginManager().registerEvents(objectiveHandler, this);

Runtime state

Custom objectives often need runtime state.

Examples:

visited locations
interaction cooldowns
active spawned mobs
temporary blocks
player progress

Keep runtime state scoped to active events and clear it when the event stops.


Validation tips

Good validation makes custom objectives much easier to use.

Useful checks:

missing sections
empty location lists
invalid worlds
invalid materials
invalid numbers
missing actions
bad cooldowns

Example:

if (locations.isEmpty()) {
return ObjectiveLoadResult.failure("No relic locations were configured.");
}

Summary

An objective handler is the main class behind a custom objective.

It should:

load config safely
return useful validation messages
award score through EventForge
clean up on stop
use public services for variables, text effects, and actions