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;
});
}
}
Extend Delegate Class
Use the appropriate delegate class for your algorithm type:
DelegateIslandCalculationAlgorithm
DelegateIslandBlocksTrackerAlgorithm
DelegateIslandEntitiesTrackerAlgorithm
Override Methods
Override only the methods you want to customize.
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