Skip to main content

Overview

Missions provide objectives for players and islands to complete, offering rewards and progression. The mission system supports both player-specific missions and island-wide missions with customizable requirements and tracking.

Mission Architecture

Missions in SuperiorSkyblock2 are built on an extensible abstract class:
public abstract class Mission<V> {
    // V is the data type stored per player/island
}
The generic type V represents the data structure used to track progress for this mission.

Core Components

Mission Properties

// Mission identification
String getName();
void setName(String name);

// Category
MissionCategory getMissionCategory();
void setMissionCategory(MissionCategory category);

Mission Types

Player Missions

Track individual player progress:
mission.setIslandMission(false);

// Progress is tracked per player
// Examples: Kill 100 zombies, mine 500 stone, craft 50 tools

Island Missions

Track collective island progress:
mission.setIslandMission(true);

// Progress is tracked per island (via owner)
// Examples: Reach island level 100, have 10 members, place 1000 spawners
public class KillMobsMission extends Mission<Integer> {
    private int requiredKills;
    
    @Override
    public void load(JavaPlugin plugin, ConfigurationSection section) {
        requiredKills = section.getInt("required-kills", 100);
        setIslandMission(false); // Player-specific
    }
    
    @Override
    public double getProgress(SuperiorPlayer player) {
        Integer kills = get(player);
        return kills == null ? 0.0 : Math.min(1.0, (double) kills / requiredKills);
    }
}

Mission Lifecycle

Loading Missions

@Override
public void load(JavaPlugin plugin, ConfigurationSection section) 
        throws MissionLoadException {
    // Load configuration
    // Set up requirements
    // Initialize data structures
    // Register listeners if needed
}
1

Parse Configuration

Read mission parameters from the config section
2

Validate Data

Check for required fields and valid values
3

Initialize Tracking

Set up data structures for progress tracking
4

Register Listeners

Register Bukkit event listeners if needed

Unloading Missions

@Override
public void unload() {
    // Unregister listeners
    // Clean up resources
    // Note: Progress data is saved automatically
}
Do not save progress data in unload(). SuperiorSkyblock handles this automatically.

Progress Tracking

Progress Calculation

@Override
public abstract double getProgress(SuperiorPlayer player);
Return a value between 0.0 and 1.0:
  • 0.0 = No progress
  • 0.5 = 50% complete
  • 1.0 = 100% complete (can complete)

Progress Value

@Override
public int getProgressValue(SuperiorPlayer player) {
    // Return the actual progress number
    // e.g., 47 out of 100 kills
    return 47;
}
This is used for displaying progress to players.

Completion Check

public boolean canComplete(SuperiorPlayer player) {
    return getProgress(player) >= 1.0;
}

Data Management

Storing Mission Data

// Store data for a player
public void insertData(SuperiorPlayer player, V value) {
    // Automatically handles player vs island missions
}

Data Key Resolution

@Nullable
protected SuperiorPlayer getDataKey(SuperiorPlayer player) {
    if (islandMission) {
        Island island = player.getIsland();
        return island == null ? null : island.getOwner();
    }
    return player;
}
Island missions use the island owner as the data key, so all island members share progress.

Completion Handlers

On Complete

@Override
public abstract void onComplete(SuperiorPlayer player);
Called when a player successfully completes the mission:
@Override
public void onComplete(SuperiorPlayer player) {
    // Give rewards
    player.sendMessage("Mission completed!");
    
    // Execute commands
    Bukkit.dispatchCommand(Bukkit.getConsoleSender(), 
        "eco give " + player.getName() + " 1000");
    
    // Grant items
    player.asPlayer().getInventory().addItem(new ItemStack(Material.DIAMOND, 10));
}

On Complete Fail

@Override
public abstract void onCompleteFail(SuperiorPlayer player);
Called when a player tries to complete but requirements aren’t met:
@Override
public void onCompleteFail(SuperiorPlayer player) {
    double progress = getProgress(player) * 100;
    player.sendMessage(String.format("Mission only %.1f%% complete!", progress));
}

Mission Categories

Missions are organized into categories:
public interface MissionCategory {
    String getName();
    int getSlot(); // GUI slot
    List<Mission<?>> getMissions();
}
Example Categories:
  • Farming Missions
  • Combat Missions
  • Building Missions
  • Economy Missions

Requirements System

Required Missions

Missions can depend on other missions:
@Override
public void load(JavaPlugin plugin, ConfigurationSection section) {
    // Require these missions first
    addRequiredMission("starter-mission", "intermediate-mission");
}

Required Checks

Placeholder-based requirements:
// Add checks with placeholder support
addRequiredCheck(
    "%island_level% >= 50",
    "%island_worth% >= 1000000"
);

Visibility Control

// Only show if required missions are completed
toggleOnlyShowIfRequiredCompleted();

Progress Persistence

Save Progress

@Override
public void saveProgress(ConfigurationSection section) {
    // Save mission data to config
    for (Map.Entry<SuperiorPlayer, V> entry : entrySet()) {
        section.set(entry.getKey().getUniqueId().toString(), 
                    serializeData(entry.getValue()));
    }
}

Load Progress

@Override
public void loadProgress(ConfigurationSection section) {
    // Load mission data from config
    for (String key : section.getKeys(false)) {
        UUID uuid = UUID.fromString(key);
        SuperiorPlayer player = SuperiorSkyblockAPI.getPlayer(uuid);
        V data = deserializeData(section.get(key));
        insertData(player, data);
    }
}
Progress is automatically saved when missions reload or the plugin disables.

GUI Integration

Format Mission Items

@Override
public void formatItem(SuperiorPlayer player, ItemStack itemStack) {
    // Called async before displaying in GUI
    // Inject custom placeholders into item
    
    ItemMeta meta = itemStack.getItemMeta();
    if (meta != null) {
        String name = meta.getDisplayName();
        name = name.replace("{progress}", 
            String.valueOf(getProgressValue(player)));
        meta.setDisplayName(name);
        itemStack.setItemMeta(meta);
    }
}

Data Transfer

Transfer on Island Ownership Change

@Override
public void transferData(SuperiorPlayer oldOwner, SuperiorPlayer newOwner) {
    // Called when island leadership transfers
    // Move mission progress to new owner
}
This is primarily for island missions when ownership changes.

Example: Complete Mission

Here’s a complete example of a mission implementation:
public class FarmingMission extends Mission<Integer> {
    private int requiredHarvests;
    private Set<Material> validCrops;
    
    @Override
    public void load(JavaPlugin plugin, ConfigurationSection section) 
            throws MissionLoadException {
        // Load config
        requiredHarvests = section.getInt("required-harvests", 1000);
        
        List<String> crops = section.getStringList("valid-crops");
        validCrops = crops.stream()
            .map(Material::valueOf)
            .collect(Collectors.toSet());
        
        if (validCrops.isEmpty()) {
            throw new MissionLoadException("No valid crops specified");
        }
        
        // Register listener
        Bukkit.getPluginManager().registerEvents(new Listener() {
            @EventHandler
            public void onHarvest(BlockBreakEvent event) {
                if (validCrops.contains(event.getBlock().getType())) {
                    SuperiorPlayer player = SuperiorSkyblockAPI
                        .getPlayer(event.getPlayer());
                    incrementProgress(player);
                }
            }
        }, plugin);
        
        // Set up data clearing
        setClearMethod(data -> {
            // Cleanup when resetting progress
        });
    }
    
    private void incrementProgress(SuperiorPlayer player) {
        Integer current = getOrCreate(player, p -> 0);
        insertData(player, current + 1);
        
        if (canComplete(player)) {
            player.sendMessage("Farming mission ready to complete!");
        }
    }
    
    @Override
    public double getProgress(SuperiorPlayer player) {
        Integer harvests = get(player);
        if (harvests == null) return 0.0;
        return Math.min(1.0, (double) harvests / requiredHarvests);
    }
    
    @Override
    public int getProgressValue(SuperiorPlayer player) {
        Integer harvests = get(player);
        return harvests == null ? 0 : harvests;
    }
    
    @Override
    public void onComplete(SuperiorPlayer player) {
        // Give rewards
        player.sendMessage("Farming mission completed!");
        
        Player bukkitPlayer = player.asPlayer();
        if (bukkitPlayer != null) {
            bukkitPlayer.getInventory().addItem(
                new ItemStack(Material.DIAMOND_HOE, 1)
            );
        }
        
        // Execute reward commands from config
        getMissionCategory(); // Access category if needed
    }
    
    @Override
    public void onCompleteFail(SuperiorPlayer player) {
        int current = getProgressValue(player);
        player.sendMessage(String.format(
            "Only %d/%d harvests complete!", 
            current, requiredHarvests
        ));
    }
    
    @Override
    public void formatItem(SuperiorPlayer player, ItemStack item) {
        ItemMeta meta = item.getItemMeta();
        if (meta == null) return;
        
        int current = getProgressValue(player);
        double percent = getProgress(player) * 100;
        
        List<String> lore = meta.getLore();
        if (lore != null) {
            lore = lore.stream()
                .map(line -> line
                    .replace("{current}", String.valueOf(current))
                    .replace("{required}", String.valueOf(requiredHarvests))
                    .replace("{percent}", String.format("%.1f", percent))
                )
                .collect(Collectors.toList());
            meta.setLore(lore);
        }
        
        item.setItemMeta(meta);
    }
}

Best Practices

Use Async-Safe Operations

formatItem() is called async. Don’t access Bukkit API unsafely.

Validate Configuration

Throw MissionLoadException if config is invalid.

Handle Null Data

get() can return null for players with no progress.

Clean Up Listeners

Unregister event listeners in unload().

IMissionsHolder Interface

Both SuperiorPlayer and Island implement this interface:
public interface IMissionsHolder {
    void completeMission(Mission<?> mission);
    void resetMission(Mission<?> mission);
    
    Map<Mission<?>, Integer> getCompletedMissions();
    int getAmountMissionCompleted(Mission<?> mission);
    boolean hasCompletedMission(Mission<?> mission);
    
    void resetAllMissions();
}

See Also

Players

Player mission tracking

Islands

Island mission tracking

Configuration

Configure missions

Events

Mission completion events