Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/BG-Software-LLC/SuperiorSkyblock2/llms.txt

Use this file to discover all available pages before exploring further.

Island Calculation Algorithms

SuperiorSkyblock2 uses algorithms to handle island calculations, block tracking, and entity tracking. You can customize these algorithms to change how islands are processed and calculated.

Algorithm Types

There are three main algorithm interfaces:
  • IslandCalculationAlgorithm - Calculates island worth and block counts
  • IslandBlocksTrackerAlgorithm - Tracks block placement and removal
  • IslandEntitiesTrackerAlgorithm - Tracks entity spawning and removal

Island Calculation Algorithm

Calculate island blocks and worth asynchronously:
import com.bgsoftware.superiorskyblock.api.island.algorithms.IslandCalculationAlgorithm;
import com.bgsoftware.superiorskyblock.api.island.Island;
import com.bgsoftware.superiorskyblock.api.key.Key;

import java.math.BigInteger;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class CustomCalculationAlgorithm implements IslandCalculationAlgorithm {
    
    @Override
    public CompletableFuture<IslandCalculationResult> calculateIsland(Island island) {
        return CompletableFuture.supplyAsync(() -> {
            Map<Key, BigInteger> blockCounts = new HashMap<>();
            
            // Scan island chunks and count blocks
            island.getAllChunks().forEach(chunk -> {
                // Custom scanning logic
                scanChunk(chunk, blockCounts);
            });
            
            return new IslandCalculationResult() {
                @Override
                public Map<Key, BigInteger> getBlockCounts() {
                    return blockCounts;
                }
            };
        });
    }
    
    private void scanChunk(ChunkPosition chunk, Map<Key, BigInteger> blockCounts) {
        // Implement chunk scanning logic
        World world = Bukkit.getWorld(chunk.getWorld());
        Chunk bukkitChunk = world.getChunkAt(chunk.getX(), chunk.getZ());
        
        // Scan blocks in chunk
        for (int x = 0; x < 16; x++) {
            for (int z = 0; z < 16; z++) {
                for (int y = world.getMinHeight(); y < world.getMaxHeight(); y++) {
                    Block block = bukkitChunk.getBlock(x, y, z);
                    if (!block.getType().isAir()) {
                        Key key = Key.of(block);
                        blockCounts.merge(key, BigInteger.ONE, BigInteger::add);
                    }
                }
            }
        }
    }
}

Calculation Result

Return block counts from calculations:
public interface IslandCalculationResult {
    /**
     * Get all block-counts that were calculated.
     */
    Map<Key, BigInteger> getBlockCounts();
}
Calculations run asynchronously. Use CompletableFuture to handle results without blocking the main thread.

Island Blocks Tracker Algorithm

Track block changes on islands in real-time:
import com.bgsoftware.superiorskyblock.api.island.algorithms.IslandBlocksTrackerAlgorithm;
import com.bgsoftware.superiorskyblock.api.key.Key;

import java.math.BigInteger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CustomBlocksTrackerAlgorithm implements IslandBlocksTrackerAlgorithm {
    
    private final Map<Key, BigInteger> blockCounts = new ConcurrentHashMap<>();
    private boolean loadingDataMode = false;
    
    @Override
    public boolean trackBlock(Key key, BigInteger amount) {
        if (amount.compareTo(BigInteger.ZERO) <= 0) {
            return false;
        }
        
        blockCounts.merge(key, amount, BigInteger::add);
        
        if (!loadingDataMode) {
            // Track variants and global blocks
            trackVariants(key, amount);
        }
        
        return true;
    }
    
    @Override
    public boolean untrackBlock(Key key, BigInteger amount) {
        if (amount.compareTo(BigInteger.ZERO) <= 0) {
            return false;
        }
        
        BigInteger currentAmount = blockCounts.getOrDefault(key, BigInteger.ZERO);
        BigInteger newAmount = currentAmount.subtract(amount);
        
        if (newAmount.compareTo(BigInteger.ZERO) <= 0) {
            blockCounts.remove(key);
        } else {
            blockCounts.put(key, newAmount);
        }
        
        if (!loadingDataMode) {
            untrackVariants(key, amount);
        }
        
        return true;
    }
    
    @Override
    public BigInteger getBlockCount(Key key) {
        // Return count including variants
        return blockCounts.entrySet().stream()
            .filter(entry -> entry.getKey().getGlobalKey().equals(key.getGlobalKey()))
            .map(Map.Entry::getValue)
            .reduce(BigInteger.ZERO, BigInteger::add);
    }
    
    @Override
    public BigInteger getExactBlockCount(Key key) {
        // Return exact count for this specific key
        return blockCounts.getOrDefault(key, BigInteger.ZERO);
    }
    
    @Override
    public Map<Key, BigInteger> getBlockCounts() {
        return new HashMap<>(blockCounts);
    }
    
    @Override
    public void clearBlockCounts() {
        blockCounts.clear();
    }
    
    @Override
    public void setLoadingDataMode(boolean loadingDataMode) {
        this.loadingDataMode = loadingDataMode;
    }
    
    private void trackVariants(Key key, BigInteger amount) {
        // Track global key variants
        // Implementation depends on your needs
    }
    
    private void untrackVariants(Key key, BigInteger amount) {
        // Untrack global key variants
    }
}

Loading Data Mode

The setLoadingDataMode() method controls variant tracking:
// During data loading, only track exact blocks
tracker.setLoadingDataMode(true);

// Load blocks from database
loadBlocksFromDatabase();

// After loading, enable variant tracking
tracker.setLoadingDataMode(false);
Always set loading data mode to true when loading from database to prevent incorrect variant calculations.

Island Entities Tracker Algorithm

Track entity spawning and removal:
import com.bgsoftware.superiorskyblock.api.island.algorithms.IslandEntitiesTrackerAlgorithm;
import com.bgsoftware.superiorskyblock.api.key.Key;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CustomEntitiesTrackerAlgorithm implements IslandEntitiesTrackerAlgorithm {
    
    private final Map<Key, Integer> entityCounts = new ConcurrentHashMap<>();
    private final Island island;
    
    public CustomEntitiesTrackerAlgorithm(Island island) {
        this.island = island;
    }
    
    @Override
    public boolean trackEntity(Key key, int amount) {
        if (amount <= 0) {
            return false;
        }
        
        entityCounts.merge(key, amount, Integer::sum);
        return true;
    }
    
    @Override
    public boolean untrackEntity(Key key, int amount) {
        if (amount <= 0) {
            return false;
        }
        
        int currentAmount = entityCounts.getOrDefault(key, 0);
        int newAmount = currentAmount - amount;
        
        if (newAmount <= 0) {
            entityCounts.remove(key);
        } else {
            entityCounts.put(key, newAmount);
        }
        
        return true;
    }
    
    @Override
    public int getEntityCount(Key key) {
        return entityCounts.getOrDefault(key, 0);
    }
    
    @Override
    public Map<Key, Integer> getEntitiesCounts() {
        return new HashMap<>(entityCounts);
    }
    
    @Override
    public void clearEntityCounts() {
        entityCounts.clear();
    }
    
    @Override
    public void recalculateEntityCounts() {
        if (!canRecalculateEntityCounts()) {
            return;
        }
        
        clearEntityCounts();
        
        // Scan all entities on the island
        island.getAllChunks().forEach(chunkPos -> {
            World world = Bukkit.getWorld(chunkPos.getWorld());
            if (world != null && world.isChunkLoaded(chunkPos.getX(), chunkPos.getZ())) {
                Chunk chunk = world.getChunkAt(chunkPos.getX(), chunkPos.getZ());
                for (Entity entity : chunk.getEntities()) {
                    if (island.isInside(entity.getLocation())) {
                        Key key = Key.of(entity);
                        trackEntity(key, 1);
                    }
                }
            }
        });
    }
    
    @Override
    public boolean canRecalculateEntityCounts() {
        // Check if island chunks are loaded
        return island.isSpawn() || island.getAllPlayersInside().size() > 0;
    }
}

Delegate Algorithms

Extend existing algorithms using delegate pattern:
import com.bgsoftware.superiorskyblock.api.island.algorithms.DelegateIslandCalculationAlgorithm;
import com.bgsoftware.superiorskyblock.api.island.Island;

import java.util.concurrent.CompletableFuture;

public class EnhancedCalculationAlgorithm extends DelegateIslandCalculationAlgorithm {
    
    public EnhancedCalculationAlgorithm(IslandCalculationAlgorithm original) {
        super(original);
    }
    
    @Override
    public CompletableFuture<IslandCalculationResult> calculateIsland(Island island) {
        // Pre-processing
        long startTime = System.currentTimeMillis();
        
        return super.calculateIsland(island).thenApply(result -> {
            // Post-processing
            long duration = System.currentTimeMillis() - startTime;
            island.getOwner().asPlayer().sendMessage(
                "Island calculated in " + duration + "ms"
            );
            
            return result;
        });
    }
}
1

Extend Delegate Class

Use the appropriate delegate class for your algorithm type:
  • DelegateIslandCalculationAlgorithm
  • DelegateIslandBlocksTrackerAlgorithm
  • DelegateIslandEntitiesTrackerAlgorithm
2

Override Methods

Override only the methods you want to customize.
3

Call Super Methods

Call super.method() to use the original implementation.

Registering Custom Algorithms

Replace default algorithms with custom implementations:
import com.bgsoftware.superiorskyblock.api.SuperiorSkyblock;
import com.bgsoftware.superiorskyblock.api.modules.PluginModule;
import com.bgsoftware.superiorskyblock.api.events.IslandCreateEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class AlgorithmModule extends PluginModule {
    
    public AlgorithmModule() {
        super("AlgorithmModule", "Author");
    }
    
    @Override
    public void onEnable(SuperiorSkyblock plugin) {
        // Algorithms are set per-island
        // Listen to island creation to set custom algorithms
    }
    
    @Override
    public Listener[] getModuleListeners(SuperiorSkyblock plugin) {
        return new Listener[] { new AlgorithmListener() };
    }
    
    private class AlgorithmListener implements Listener {
        
        @EventHandler
        public void onIslandCreate(IslandCreateEvent event) {
            Island island = event.getIsland();
            
            // Set custom calculation algorithm
            island.setCalculationAlgorithm(new CustomCalculationAlgorithm());
            
            // Set custom blocks tracker
            island.setBlocksTrackerAlgorithm(new CustomBlocksTrackerAlgorithm());
            
            // Set custom entities tracker
            island.setEntitiesTrackerAlgorithm(new CustomEntitiesTrackerAlgorithm(island));
        }
    }
    
    // Other module methods...
}

Practical Example: Fast Calculation Algorithm

public class FastCalculationAlgorithm implements IslandCalculationAlgorithm {
    
    private final SuperiorSkyblock plugin;
    
    public FastCalculationAlgorithm(SuperiorSkyblock plugin) {
        this.plugin = plugin;
    }
    
    @Override
    public CompletableFuture<IslandCalculationResult> calculateIsland(Island island) {
        return CompletableFuture.supplyAsync(() -> {
            Map<Key, BigInteger> blockCounts = new HashMap<>();
            
            // Use block tracker data if available
            IslandBlocksTrackerAlgorithm tracker = island.getBlocksTracker();
            if (tracker != null) {
                blockCounts.putAll(tracker.getBlockCounts());
            } else {
                // Fallback to chunk scanning
                scanIsland(island, blockCounts);
            }
            
            return new IslandCalculationResult() {
                @Override
                public Map<Key, BigInteger> getBlockCounts() {
                    return blockCounts;
                }
            };
        });
    }
    
    private void scanIsland(Island island, Map<Key, BigInteger> blockCounts) {
        // Only scan loaded chunks for faster calculation
        island.getLoadedChunks(World.Environment.NORMAL, true, true)
            .forEach(chunk -> scanChunk(chunk, blockCounts));
    }
    
    private void scanChunk(Chunk chunk, Map<Key, BigInteger> blockCounts) {
        ChunkSnapshot snapshot = chunk.getChunkSnapshot();
        
        for (int x = 0; x < 16; x++) {
            for (int z = 0; z < 16; z++) {
                for (int y = snapshot.getMinHeight(); y < snapshot.getMaxHeight(); y++) {
                    Material type = snapshot.getBlockType(x, y, z);
                    if (!type.isAir()) {
                        Key key = Key.of(type.name());
                        blockCounts.merge(key, BigInteger.ONE, BigInteger::add);
                    }
                }
            }
        }
    }
}

Best Practices

  • Use CompletableFuture for calculation algorithms to avoid blocking
  • Use thread-safe collections (ConcurrentHashMap) for trackers
  • Validate amounts before tracking/untracking (check for negative values)
  • Clear caches when clearBlockCounts() or clearEntityCounts() is called
  • Respect loadingDataMode flag in block trackers
  • Use ChunkSnapshot for async chunk scanning
  • Check if chunks are loaded before accessing them
  • Implement canRecalculateEntityCounts() properly
  • Use delegate algorithms to extend default behavior
  • Set algorithms early (during island creation)
  • Consider performance impact of real-time tracking vs periodic calculations

Build docs developers (and LLMs) love