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