Added /(admincmd) limitscalc <player> (#14)

* Added /(admincmd) limitscalc <player>

* Remove blocks attached to block and redstone torch fix

* Removed unnecesary method

* Made /(admin) limits calc <player> a subcommand + fix redstone displaying on GUI

* Correct player has island check
This commit is contained in:
YellowZaki 2019-02-26 16:35:28 +01:00 committed by tastybento
parent f4740869da
commit 8248d62a98
6 changed files with 290 additions and 13 deletions

View File

@ -26,6 +26,7 @@ public class AdminCommand extends CompositeCommand {
public AdminCommand(Limits addon, CompositeCommand parent) {
super(parent, "limits");
this.addon = addon;
new CalcCommand(addon, this);
}
/* (non-Javadoc)
@ -35,8 +36,8 @@ public class AdminCommand extends CompositeCommand {
public void setup() {
this.setPermission("limits.admin.limits");
this.setOnlyPlayer(true);
this.setParametersHelp("admin.limits.parameters");
this.setDescription("admin.limits.description");
this.setParametersHelp("admin.limits.main.parameters");
this.setDescription("admin.limits.main.description");
}
/* (non-Javadoc)

View File

@ -0,0 +1,81 @@
package bentobox.addon.limits.commands;
import bentobox.addon.limits.Limits;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
/**
*
* @author YellowZaki
*/
public class CalcCommand extends CompositeCommand {
private final Limits addon;
/**
* Admin command
*
* @param addon - addon
*/
public CalcCommand(Limits addon, CompositeCommand parent) {
super(parent, "calc");
this.addon = addon;
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup()
*/
@Override
public void setup() {
this.setPermission("limits.admin.limits.calc");
this.setOnlyPlayer(false);
this.setParametersHelp("admin.limits.calc.parameters");
this.setDescription("admin.limits.calc.description");
}
/* (non-Javadoc)
* @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List)
*/
@Override
public boolean execute(User user, String label, List<String> args) {
if (args.size() == 1) {
final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0));
if (playerUUID == null) {
user.sendMessage("general.errors.unknown-player", args.get(0));
return true;
} else {
//Calculate
calcLimits(playerUUID, user);
}
return true;
} else {
showHelp(this, user);
return false;
}
}
public void calcLimits(UUID targetPlayer, User sender) {
if (addon.getIslands().getIsland(getWorld(), targetPlayer) != null) {
new LimitsCalc(getWorld(), getPlugin(), targetPlayer, addon, sender);
} else {
sender.sendMessage("general.errors.player-has-no-island");
}
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
}
}

View File

@ -53,7 +53,12 @@ class LimitPanel {
for (Entry<Material, Integer> en : matLimits.entrySet()) {
PanelItemBuilder pib = new PanelItemBuilder();
pib.name(Util.prettifyText(en.getKey().toString()));
if (en.getKey() == Material.REDSTONE_WIRE) {
pib.icon(Material.REDSTONE);
}
else {
pib.icon(en.getKey());
}
int count = ibc == null ? 0 : ibc.getBlockCount().getOrDefault(en.getKey(), 0);
String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color");
pib.description(color

View File

@ -0,0 +1,143 @@
package bentobox.addon.limits.commands;
import bentobox.addon.limits.Limits;
import bentobox.addon.limits.listeners.BlockLimitsListener;
import bentobox.addon.limits.objects.IslandBlockCount;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitTask;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
/**
*
* @author YellowZaki
*/
public class LimitsCalc {
private boolean checking;
private Limits addon;
private BentoBox instance;
private World world;
private Island island;
private BlockLimitsListener bll;
private IslandBlockCount ibc;
private Map<Material, Integer> blockCount;
private BukkitTask task;
private User sender;
LimitsCalc(World world, BentoBox instance, UUID targetPlayer, Limits addon, User sender) {
this.checking = true;
this.addon = addon;
this.instance = instance;
this.world = world;
this.island = instance.getIslands().getIsland(world, targetPlayer);
this.bll = addon.getBlockLimitListener();
this.ibc = bll.getIsland(island.getUniqueId());
blockCount = new HashMap<>();
this.sender = sender;
Set<Pair<Integer, Integer>> chunksToScan = getChunksToScan(island);
this.task = addon.getServer().getScheduler().runTaskTimer(addon.getPlugin(), () -> {
Set<ChunkSnapshot> chunkSnapshot = new HashSet<>();
if (checking) {
Iterator<Pair<Integer, Integer>> it = chunksToScan.iterator();
if (!it.hasNext()) {
// Nothing left
tidyUp();
return;
}
// Add chunk snapshots to the list
while (it.hasNext() && chunkSnapshot.size() < 200) {
Pair<Integer, Integer> pair = it.next();
if (!world.isChunkLoaded(pair.x, pair.z)) {
world.loadChunk(pair.x, pair.z);
chunkSnapshot.add(world.getChunkAt(pair.x, pair.z).getChunkSnapshot());
world.unloadChunk(pair.x, pair.z);
} else {
chunkSnapshot.add(world.getChunkAt(pair.x, pair.z).getChunkSnapshot());
}
it.remove();
}
// Move to next step
checking = false;
checkChunksAsync(chunkSnapshot);
}
}, 0L, 1);
}
private void checkChunksAsync(final Set<ChunkSnapshot> chunkSnapshot) {
// Run async task to scan chunks
addon.getServer().getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
for (ChunkSnapshot chunk : chunkSnapshot) {
scanChunk(chunk);
}
// Nothing happened, change state
checking = true;
});
}
private void scanChunk(ChunkSnapshot chunk) {
for (int x = 0; x < 16; x++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
continue;
}
for (int z = 0; z < 16; z++) {
// Check if the block coordinate is inside the protection zone and if not, don't count it
if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
continue;
}
for (int y = 0; y < island.getCenter().getWorld().getMaxHeight(); y++) {
Material blockData = chunk.getBlockType(x, y, z);
// Air is free
if (!blockData.equals(Material.AIR)) {
checkBlock(blockData);
}
}
}
}
}
private void checkBlock(Material md) {
md = bll.fixMaterial(md);
// md is limited
if (bll.getMaterialLimits(world, island.getUniqueId()).containsKey(md)) {
if (!blockCount.containsKey(md)) {
blockCount.put(md, 1);
} else {
blockCount.put(md, blockCount.get(md) + 1);
}
}
}
private Set<Pair<Integer, Integer>> getChunksToScan(Island island) {
Set<Pair<Integer, Integer>> chunkSnapshot = new HashSet<>();
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
Pair<Integer, Integer> pair = new Pair<>(world.getBlockAt(x, 0, z).getChunk().getX(), world.getBlockAt(x, 0, z).getChunk().getZ());
chunkSnapshot.add(pair);
}
}
return chunkSnapshot;
}
private void tidyUp() {
// Cancel
task.cancel();
ibc.setBlockCount(blockCount);
bll.setIsland(island.getUniqueId(), ibc);
sender.sendMessage("admin.limits.calc.finished");
}
}

View File

@ -32,6 +32,7 @@ import org.bukkit.event.block.BlockFromToEvent;
import bentobox.addon.limits.Limits;
import bentobox.addon.limits.objects.IslandBlockCount;
import org.bukkit.block.BlockFace;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
@ -107,6 +108,7 @@ public class BlockLimitsListener implements Listener {
/**
* Loads limit map from configuration section
*
* @param cs - configuration section
* @return limit map
*/
@ -124,6 +126,7 @@ public class BlockLimitsListener implements Listener {
return mats;
}
/**
* Save the count database completely
*/
@ -140,6 +143,22 @@ public class BlockLimitsListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlock(BlockBreakEvent e) {
notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), false), e.getBlock().getType());
// Player breaks a block and there was a redstone dust/repeater/... above
if (e.getBlock().getRelative(BlockFace.UP).getType() == Material.REDSTONE_WIRE || e.getBlock().getRelative(BlockFace.UP).getType() == Material.REPEATER || e.getBlock().getRelative(BlockFace.UP).getType() == Material.COMPARATOR || e.getBlock().getRelative(BlockFace.UP).getType() == Material.REDSTONE_TORCH) {
process(e.getBlock().getRelative(BlockFace.UP), false);
}
if (e.getBlock().getRelative(BlockFace.EAST).getType() == Material.REDSTONE_WALL_TORCH) {
process(e.getBlock().getRelative(BlockFace.EAST), false);
}
if (e.getBlock().getRelative(BlockFace.WEST).getType() == Material.REDSTONE_WALL_TORCH) {
process(e.getBlock().getRelative(BlockFace.WEST), false);
}
if (e.getBlock().getRelative(BlockFace.SOUTH).getType() == Material.REDSTONE_WALL_TORCH) {
process(e.getBlock().getRelative(BlockFace.SOUTH), false);
}
if (e.getBlock().getRelative(BlockFace.NORTH).getType() == Material.REDSTONE_WALL_TORCH) {
process(e.getBlock().getRelative(BlockFace.NORTH), false);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@ -216,19 +235,38 @@ public class BlockLimitsListener implements Listener {
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlock(BlockFromToEvent e) {
if (e.getBlock().isLiquid()) {
if (e.getToBlock().getType() == Material.REDSTONE_WIRE || e.getToBlock().getType() == Material.REPEATER || e.getToBlock().getType() == Material.COMPARATOR || e.getToBlock().getType() == Material.REDSTONE_TORCH || e.getToBlock().getType() == Material.REDSTONE_WALL_TORCH) {
process(e.getToBlock(), false);
}
}
}
private int process(Block b, boolean add) {
return process(b, add, b.getType());
}
// It wouldn't make sense to count REDSTONE_WALL_TORCH and REDSTONE_TORCH as separed limits.
public Material fixMaterial(Material b) {
if (b == Material.REDSTONE_WALL_TORCH) {
return Material.REDSTONE_TORCH;
} else {
return b;
}
}
/**
* Check if a block can be
*
* @param b - block
* @param add - true to add a block, false to remove
* @param changeTo - material this block will become
* @return limit amount if over limit, or -1 if no limitation
*/
private int process(Block b, boolean add, Material changeTo) {
if (DO_NOT_COUNT.contains(b.getType()) || !addon.inGameModeWorld(b.getWorld())) {
if (DO_NOT_COUNT.contains(fixMaterial(b.getType())) || !addon.inGameModeWorld(b.getWorld())) {
return -1;
}
// Check if on island
@ -243,24 +281,24 @@ public class BlockLimitsListener implements Listener {
saveMap.putIfAbsent(id, 0);
if (add) {
// Check limit
int limit = checkLimit(b.getWorld(), b.getType(), id);
int limit = checkLimit(b.getWorld(), fixMaterial(b.getType()), id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(b.getType());
islandCountMap.get(id).add(fixMaterial(b.getType()));
saveMap.merge(id, 1, Integer::sum);
} else {
if (islandCountMap.containsKey(id)) {
// Check for changes
if (!changeTo.equals(b.getType()) && changeTo.isBlock() && !DO_NOT_COUNT.contains(changeTo)) {
if (!fixMaterial(changeTo).equals(fixMaterial(b.getType())) && fixMaterial(changeTo).isBlock() && !DO_NOT_COUNT.contains(fixMaterial(changeTo))) {
// Check limit
int limit = checkLimit(b.getWorld(), changeTo, id);
int limit = checkLimit(b.getWorld(), fixMaterial(changeTo), id);
if (limit > -1) {
return limit;
}
islandCountMap.get(id).add(changeTo);
islandCountMap.get(id).add(fixMaterial(changeTo));
}
islandCountMap.get(id).remove(b.getType());
islandCountMap.get(id).remove(fixMaterial(b.getType()));
saveMap.merge(id, 1, Integer::sum);
}
}
@ -274,6 +312,7 @@ public class BlockLimitsListener implements Listener {
/**
* Check if this material is at its limit for world on this island
*
* @param w - world
* @param m - material
* @param id - island id
@ -300,6 +339,7 @@ public class BlockLimitsListener implements Listener {
/**
* Gets an aggregate map of the limits for this island
*
* @param w - world
* @param id - island id
* @return map of limits for materials
@ -322,6 +362,7 @@ public class BlockLimitsListener implements Listener {
/**
* Removes island from the database
*
* @param e - island delete event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@ -333,9 +374,9 @@ public class BlockLimitsListener implements Listener {
}
}
/**
* Set the island block count values
*
* @param islandId - island unique id
* @param ibc - island block count
*/
@ -346,6 +387,7 @@ public class BlockLimitsListener implements Listener {
/**
* Get the island block count
*
* @param islandId - island unique id
* @return island block count or null if there is none yet
*/

View File

@ -9,8 +9,13 @@ limits:
admin:
limits:
main:
parameters: "<player>"
description: "show the island limits for player"
calc:
parameters: "<player>"
description: "recalculate the island limits for player"
finished: "&aIsland recalc finished sucessfully!"
island:
limits: