๐งช Custom Objectives
Custom objectives let addons add completely new ways for players to score during EventForge events.
Use this when the built-in objective types are not enough for your event idea.
What custom objectives can do
A custom objective can:
load its own config section
track player actions
award score
run EventForge actions
use EventForge variables
use text effects
listen to Bukkit events
start dialogues
store runtime state
Example ideas:
RELIC_HUNT
SUPPLY_DROP
PARKOUR_RACE
BOSS_DAMAGE
CUSTOM_DUNGEON
Basic structure
A custom objective usually needs:
objective data class
objective handler class
plugin registration
event config example
The data class stores loaded config and runtime state.
The handler controls how the objective loads, starts, stops, and reacts to gameplay.
Registering an objective
Register your objective when your addon enables.
@Override
public void onEnable() {
if (!EventForgeAPI.isAvailable()) {
getLogger().severe("EventForge API is not available.");
getServer().getPluginManager().disablePlugin(this);
return;
}
EventForgeAPI.getObjectiveRegistry().register(new RelicHuntObjectiveHandler(this));
}
Unregister on disable:
@Override
public void onDisable() {
if (EventForgeAPI.isAvailable()) {
EventForgeAPI.getObjectiveRegistry().unregister("RELIC_HUNT");
}
}
Objective ID
Your objective handler must use a unique objective ID.
Good IDs:
RELIC_HUNT
SUPPLY_DROP
PARKOUR_RACE
BOSS_DAMAGE
Avoid using EventForge built-in IDs.
Built-in objective IDs:
MINE_BLOCKS
KILL_MOBS
FISH_ITEMS
COLLECT_ITEMS
MOB_INVASION
CAPTURE_ZONE
VISIT_REGIONS
INTERACT_BLOCKS
Built-in objectives are protected and cannot be unregistered by addons.
Example config
Once registered, server owners can use your objective in an event file.
objective:
type: RELIC_HUNT
hint-interval: 30s
announce-leaderboard-on-stop: true
relics:
ancient_relic:
clue: "Look near the old fountain."
world: world
x: 0
y: 70
z: 0
radius: 4
discovery-points: 5
Objective data class
Create a data class that implements EventObjectiveData.
public final class RelicHuntObjectiveData implements EventObjectiveData {
private final List<RelicLocation> relics;
private final int hintIntervalSeconds;
public RelicHuntObjectiveData(List<RelicLocation> relics, int hintIntervalSeconds) {
this.relics = List.copyOf(relics == null ? List.of() : relics);
this.hintIntervalSeconds = Math.max(1, hintIntervalSeconds);
}
public List<RelicLocation> getRelics() {
return relics;
}
public int getHintIntervalSeconds() {
return hintIntervalSeconds;
}
}
Objective handler
Your handler loads the config and reacts to objective lifecycle methods.
public final class RelicHuntObjectiveHandler implements EventObjectiveHandler<RelicHuntObjectiveData> {
private final JavaPlugin plugin;
public RelicHuntObjectiveHandler(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public String getType() {
return "RELIC_HUNT";
}
@Override
public ObjectiveLoadResult<RelicHuntObjectiveData> load(EventObjectiveLoadContext context) {
int hintInterval = context.getConfig().getInt(context.getObjectivePath() + ".hint-interval", 30);
RelicHuntObjectiveData data = new RelicHuntObjectiveData(
List.of(),
hintInterval
);
return ObjectiveLoadResult.success(data);
}
@Override
public void onStart(EventObjectiveSession session, RelicHuntObjectiveData data) {
plugin.getLogger().info("Relic Hunt started for event " + session.getEventId());
}
@Override
public void onStop(EventObjectiveSession session, RelicHuntObjectiveData data) {
plugin.getLogger().info("Relic Hunt stopped for event " + session.getEventId());
}
}
Awarding score
Use the objective session to award score.
session.addScore(player, points);
Example:
int points = 5;
session.addScore(player, points);
EventForge handles the event score update and fires score change events.
Using variables and text effects
Custom objectives can use the public services added in v1.0.2.
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);
Running EventForge actions
Custom objectives can run configured EventForge actions through ActionService.
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
);
This lets your custom objective support normal EventForge action config.
Loading actions
Use the objective load context to load actions from your custom config section.
List<EventAction> actions = context.loadActions(
context.getObjectivePath() + ".actions"
);
Then store the actions in your objective data class.
Listening to Bukkit events
Custom objectives can listen to normal Bukkit events.
Example:
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
// Check active EventForge sessions handled by your objective.
}
Make sure you clean up runtime state when events stop or the plugin disables.
Runtime state
Custom objectives often need temporary runtime state.
Examples:
players who already discovered something
cooldowns
active timers
spawned entities
temporary blocks
region progress
Keep runtime state inside your objective handler or objective data object, and clear it when the event stops.
Validation
Use warnings and errors during config loading.
if (relics.isEmpty()) {
context.warning("No relics configured.");
}
If the objective cannot load, return a failed result.
return ObjectiveLoadResult.failure("No relics were configured.");
Good validation makes server setup much easier.
Addon example
The Advanced Relic Hunt example addon shows:
custom objective registration
custom action registration
custom objective config loading
VariableService usage
TextEffectService usage
milestones in the event config
dialogue integration
custom objective scoring
Use it as a reference when building more advanced addons.
Summary
Use custom objectives when you want EventForge to support a new scoring system.
A good custom objective should:
load config safely
validate useful mistakes
award score through EventForge
clean up runtime state
support EventForge actions where useful
use public services instead of duplicating parsing