๐ฏ 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