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.

Block events handle the custom block stacking system in SuperiorSkyblock2, allowing blocks to stack beyond the normal one block per location limit.

Block Stacking System

SuperiorSkyblock2 includes a built-in block stacking system that allows multiple blocks to occupy the same location with a stack count. This is particularly useful for farming and resource management.
Block stacking reduces lag by representing multiple identical blocks as a single block with metadata tracking the count.

Block Stack Event

BlockStackEvent

Called when blocks are being stacked (added to an existing stack).
import com.bgsoftware.superiorskyblock.api.events.BlockStackEvent;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class BlockStackListener implements Listener {
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Block block = event.getBlock();
        Player player = event.getPlayer();
        int originalCount = event.getOriginalCount();
        int newCount = event.getNewCount();
        int increaseAmount = event.getIncreaseAmount();
        
        // Log stacking activity
        plugin.getLogger().info(
            player.getName() + " stacked " + block.getType() + 
            " from " + originalCount + " to " + newCount
        );
        
        // Limit stack size for certain blocks
        if (block.getType() == Material.DIAMOND_ORE && newCount > 1000) {
            event.setCancelled(true);
            player.sendMessage("Diamond ore can only stack up to 1000!");
        }
    }
}
Event Properties:
  • getBlock() - The block being stacked
  • getPlayer() - The player performing the stack action
  • getOriginalCount() - Stack count before this action
  • getNewCount() - Stack count after this action
  • getIncreaseAmount() - Number of blocks being added (newCount - originalCount)
  • isCancelled() / setCancelled(boolean) - Cancel the stacking operation

Block Unstack Event

BlockUnstackEvent

Called when blocks are being unstacked (removed from an existing stack).
import com.bgsoftware.superiorskyblock.api.events.BlockUnstackEvent;

@EventHandler
public void onBlockUnstack(BlockUnstackEvent event) {
    Block block = event.getBlock();
    Player player = event.getPlayer();
    int originalCount = event.getOriginalCount();
    int newCount = event.getNewCount();
    int decreaseAmount = event.getDecreaseAmount();
    
    // Track resource harvesting
    trackHarvest(player, block.getType(), decreaseAmount);
    
    // Give bonus items for large stack breaks
    if (decreaseAmount >= 64 && player.hasPermission("bonus.break")) {
        giveBonus(player, block.getType(), decreaseAmount / 64);
        player.sendMessage("Bonus items received!");
    }
}
Event Properties:
  • getBlock() - The block being unstacked
  • getPlayer() - The player performing the unstack action
  • getOriginalCount() - Stack count before this action
  • getNewCount() - Stack count after this action
  • getDecreaseAmount() - Number of blocks being removed (originalCount - newCount)
  • isCancelled() / setCancelled(boolean) - Cancel the unstacking operation
When newCount reaches 0, the block is completely removed. You can prevent this by setting setCancelled(true).

Practical Examples

Stack Limit Manager

Enforce different stack limits for different block types:
public class StackLimitManager implements Listener {
    
    private final Map<Material, Integer> stackLimits = new HashMap<>();
    
    public StackLimitManager() {
        // Configure stack limits
        stackLimits.put(Material.IRON_ORE, 5000);
        stackLimits.put(Material.GOLD_ORE, 2500);
        stackLimits.put(Material.DIAMOND_ORE, 1000);
        stackLimits.put(Material.EMERALD_ORE, 500);
        stackLimits.put(Material.SPAWNER, 64);
    }
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Material type = event.getBlock().getType();
        int newCount = event.getNewCount();
        
        // Check if block has a stack limit
        if (stackLimits.containsKey(type)) {
            int limit = stackLimits.get(type);
            
            if (newCount > limit) {
                event.setCancelled(true);
                event.getPlayer().sendMessage(
                    "§c" + type.name() + " can only stack up to " + limit + "!"
                );
            }
        }
    }
}

Stack Statistics Tracker

Track player stacking activity for statistics:
public class StackStatistics implements Listener {
    
    private final Map<UUID, PlayerStats> playerStats = new HashMap<>();
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        UUID uuid = event.getPlayer().getUniqueId();
        Material type = event.getBlock().getType();
        int amount = event.getIncreaseAmount();
        
        // Update statistics
        PlayerStats stats = playerStats.computeIfAbsent(uuid, k -> new PlayerStats());
        stats.addStacked(type, amount);
        
        // Achievement checking
        if (stats.getTotalStacked(type) >= 10000) {
            grantAchievement(event.getPlayer(), "stack_master_" + type.name());
        }
    }
    
    @EventHandler
    public void onBlockUnstack(BlockUnstackEvent event) {
        UUID uuid = event.getPlayer().getUniqueId();
        Material type = event.getBlock().getType();
        int amount = event.getDecreaseAmount();
        
        // Update statistics
        PlayerStats stats = playerStats.computeIfAbsent(uuid, k -> new PlayerStats());
        stats.addUnstacked(type, amount);
    }
    
    public static class PlayerStats {
        private final Map<Material, Long> stacked = new HashMap<>();
        private final Map<Material, Long> unstacked = new HashMap<>();
        
        public void addStacked(Material type, int amount) {
            stacked.merge(type, (long) amount, Long::sum);
        }
        
        public void addUnstacked(Material type, int amount) {
            unstacked.merge(type, (long) amount, Long::sum);
        }
        
        public long getTotalStacked(Material type) {
            return stacked.getOrDefault(type, 0L);
        }
        
        public long getTotalUnstacked(Material type) {
            return unstacked.getOrDefault(type, 0L);
        }
    }
}

Permission-Based Stacking

Control who can stack certain block types:
public class PermissionStackManager implements Listener {
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Player player = event.getPlayer();
        Material type = event.getBlock().getType();
        
        // Require permission for valuable blocks
        if (isValuableBlock(type)) {
            String permission = "superiorskyblock.stack." + type.name().toLowerCase();
            
            if (!player.hasPermission(permission)) {
                event.setCancelled(true);
                player.sendMessage(
                    "§cYou don't have permission to stack " + type.name() + "!"
                );
            }
        }
    }
    
    private boolean isValuableBlock(Material type) {
        return type == Material.DIAMOND_ORE ||
               type == Material.EMERALD_ORE ||
               type == Material.GOLD_ORE ||
               type == Material.SPAWNER ||
               type == Material.BEACON;
    }
}

Stack Bonus System

Reward players for efficient stacking:
public class StackBonusSystem implements Listener {
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Player player = event.getPlayer();
        int newCount = event.getNewCount();
        Material type = event.getBlock().getType();
        
        // Milestone rewards
        if (newCount == 1000) {
            giveReward(player, type, "milestone_1k");
            player.sendMessage("§6Reached 1,000 stack! Bonus reward received!");
        } else if (newCount == 5000) {
            giveReward(player, type, "milestone_5k");
            player.sendMessage("§6Reached 5,000 stack! Epic reward received!");
        } else if (newCount == 10000) {
            giveReward(player, type, "milestone_10k");
            player.sendMessage("§6Reached 10,000 stack! Legendary reward received!");
        }
    }
    
    @EventHandler
    public void onBlockUnstack(BlockUnstackEvent event) {
        Player player = event.getPlayer();
        int decreaseAmount = event.getDecreaseAmount();
        
        // Efficiency bonus for breaking large stacks
        if (decreaseAmount >= 64 && player.hasPermission("vip.efficiency")) {
            int bonusAmount = decreaseAmount / 10; // 10% bonus
            
            ItemStack bonus = new ItemStack(event.getBlock().getType(), bonusAmount);
            player.getInventory().addItem(bonus);
            player.sendMessage("§aEfficiency bonus: +" + bonusAmount + " blocks!");
        }
    }
}

Island Worth Integration

Adjust island worth calculations based on stacks:
public class StackWorthListener implements Listener {
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Block block = event.getBlock();
        int increaseAmount = event.getIncreaseAmount();
        
        // Find the island
        Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation());
        if (island != null) {
            // Recalculate worth after stacking completes
            Bukkit.getScheduler().runTaskLater(plugin, () -> {
                island.calcIslandWorth(null);
            }, 1L);
        }
    }
    
    @EventHandler
    public void onBlockUnstack(BlockUnstackEvent event) {
        Block block = event.getBlock();
        int decreaseAmount = event.getDecreaseAmount();
        
        // Find the island
        Island island = SuperiorSkyblockAPI.getIslandAt(block.getLocation());
        if (island != null) {
            // Recalculate worth after unstacking completes
            Bukkit.getScheduler().runTaskLater(plugin, () -> {
                island.calcIslandWorth(null);
            }, 1L);
        }
    }
}

Visual Stack Display

Show stack information to players:
public class StackDisplayListener implements Listener {
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Player player = event.getPlayer();
        Block block = event.getBlock();
        int newCount = event.getNewCount();
        
        // Show action bar message
        player.spigot().sendMessage(
            ChatMessageType.ACTION_BAR,
            new TextComponent(
                "§7[§6" + block.getType().name() + "§7] §fx" + 
                formatNumber(newCount)
            )
        );
        
        // Play sound
        player.playSound(block.getLocation(), Sound.BLOCK_STONE_PLACE, 0.5f, 1.5f);
    }
    
    @EventHandler
    public void onBlockUnstack(BlockUnstackEvent event) {
        Player player = event.getPlayer();
        Block block = event.getBlock();
        int newCount = event.getNewCount();
        int decreaseAmount = event.getDecreaseAmount();
        
        // Show action bar message
        String message;
        if (newCount == 0) {
            message = "§7[§c" + block.getType().name() + "§7] §fStack depleted";
        } else {
            message = "§7[§6" + block.getType().name() + "§7] §fx" + 
                     formatNumber(newCount) + " §7(-" + decreaseAmount + ")";
        }
        
        player.spigot().sendMessage(
            ChatMessageType.ACTION_BAR,
            new TextComponent(message)
        );
        
        // Play sound
        player.playSound(block.getLocation(), Sound.BLOCK_STONE_BREAK, 0.5f, 1.0f);
    }
    
    private String formatNumber(int number) {
        if (number >= 1000000) {
            return String.format("%.1fM", number / 1000000.0);
        } else if (number >= 1000) {
            return String.format("%.1fK", number / 1000.0);
        } else {
            return String.valueOf(number);
        }
    }
}

Anti-Exploit Protection

Prevent potential exploits with stacking:
public class StackProtectionListener implements Listener {
    
    private final Map<Location, Long> recentStacks = new HashMap<>();
    
    @EventHandler
    public void onBlockStack(BlockStackEvent event) {
        Location loc = event.getBlock().getLocation();
        long now = System.currentTimeMillis();
        int increaseAmount = event.getIncreaseAmount();
        
        // Rate limiting - prevent rapid stacking exploits
        if (recentStacks.containsKey(loc)) {
            long lastStack = recentStacks.get(loc);
            if (now - lastStack < 50) { // Less than 50ms
                event.setCancelled(true);
                return;
            }
        }
        recentStacks.put(loc, now);
        
        // Prevent suspiciously large increases
        if (increaseAmount > 64) {
            plugin.getLogger().warning(
                event.getPlayer().getName() + " attempted to stack " + 
                increaseAmount + " blocks at once. Possible exploit?"
            );
            
            // You might want to cancel or adjust
            if (increaseAmount > 640) { // 10 stacks seems excessive
                event.setCancelled(true);
            }
        }
    }
    
    @EventHandler
    public void onBlockUnstack(BlockUnstackEvent event) {
        int decreaseAmount = event.getDecreaseAmount();
        
        // Monitor large-scale harvesting
        if (decreaseAmount > 1000) {
            plugin.getLogger().info(
                event.getPlayer().getName() + " harvested " + 
                decreaseAmount + " " + event.getBlock().getType() + " blocks"
            );
        }
    }
}

Best Practices

Block Event Guidelines:
  • Always validate stack counts to prevent exploits
  • Use limits appropriate for your server’s economy
  • Consider performance when recalculating island worth
  • Cache player statistics rather than querying database on every event
  • Provide visual feedback to players about stack changes
  • Log suspicious stacking activity for anti-cheat purposes

Common Patterns

Checking if Block Can Stack

@EventHandler
public void onBlockStack(BlockStackEvent event) {
    // Check various conditions
    if (!isStackableBlock(event.getBlock().getType())) {
        event.setCancelled(true);
        return;
    }
    
    if (!hasStackPermission(event.getPlayer())) {
        event.setCancelled(true);
        return;
    }
    
    if (exceedsLimit(event.getBlock().getType(), event.getNewCount())) {
        event.setCancelled(true);
        return;
    }
}

Tracking Stack Changes

private final Map<Location, StackInfo> stacks = new HashMap<>();

@EventHandler
public void onBlockStack(BlockStackEvent event) {
    Location loc = event.getBlock().getLocation();
    StackInfo info = stacks.computeIfAbsent(loc, k -> new StackInfo());
    info.lastCount = event.getNewCount();
    info.lastModified = System.currentTimeMillis();
    info.totalStacked += event.getIncreaseAmount();
}

@EventHandler
public void onBlockUnstack(BlockUnstackEvent event) {
    Location loc = event.getBlock().getLocation();
    StackInfo info = stacks.get(loc);
    if (info != null) {
        info.lastCount = event.getNewCount();
        info.lastModified = System.currentTimeMillis();
        info.totalUnstacked += event.getDecreaseAmount();
        
        // Remove if stack is depleted
        if (event.getNewCount() == 0) {
            stacks.remove(loc);
        }
    }
}

Integration with Island System

@EventHandler
public void onBlockStack(BlockStackEvent event) {
    // Get island at location
    Island island = SuperiorSkyblockAPI.getIslandAt(event.getBlock().getLocation());
    
    if (island != null) {
        // Check if player has permission on island
        SuperiorPlayer sp = SuperiorSkyblockAPI.getPlayer(event.getPlayer());
        if (!island.hasPermission(sp, IslandPrivilege.BUILD)) {
            event.setCancelled(true);
            return;
        }
        
        // Additional island-specific logic
        handleIslandStacking(island, event);
    }
}

Complete Event List

All block-related events:
  • BlockStackEvent - Blocks added to stack
  • BlockUnstackEvent - Blocks removed from stack

Next Steps

Build docs developers (and LLMs) love