diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index b5ec63d..3ea9a48 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -22,10 +22,10 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 16 uses: actions/setup-java@v2 with: - java-version: 8 + java-version: 16 distribution: adopt - name: Build with Maven run: mvn package --file pom.xml diff --git a/.gitignore b/.gitignore index cf6c107..c1e3877 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ *.sh dependency-reduced-pom.xml /dependency-reduced-pom.xml +*.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml index 984af2f..2570be0 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ com.github.thebusybiscuit Slimefun4 - 2c4f886fe4 + RC-35 provided diff --git a/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java b/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java index 50cf15d..3d7600e 100644 --- a/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java +++ b/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java @@ -1,10 +1,11 @@ package dev.j3fftw.headlimiter; -import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; -import io.github.thebusybiscuit.slimefun4.libraries.dough.updater.GitHubBuildsUpdater; +import java.io.File; + +import dev.j3fftw.headlimiter.blocklimiter.Group; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -12,17 +13,24 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; +import io.github.thebusybiscuit.slimefun4.libraries.dough.updater.GitHubBuildsUpdater; + +import dev.j3fftw.headlimiter.blocklimiter.BlockLimiter; public final class HeadLimiter extends JavaPlugin implements Listener { private static HeadLimiter instance; + private BlockLimiter blockLimiter; + @Override public void onEnable() { instance = this; - if (!new File(getDataFolder(), "config.yml").exists()) + if (!new File(getDataFolder(), "config.yml").exists()) { saveDefaultConfig(); + } Utils.loadPermissions(); @@ -35,6 +43,9 @@ public void onEnable() { if (getConfig().getBoolean("auto-update", true) && getDescription().getVersion().startsWith("DEV - ")) { new GitHubBuildsUpdater(this, getFile(), "J3fftw1/HeadLimiter/master").start(); } + + this.blockLimiter = new BlockLimiter(this); + loadConfig(); } @Override @@ -64,13 +75,29 @@ public void onPlace(BlockPlaceEvent e) { && isCargo(sfItem) ) { final int maxAmount = Utils.getMaxHeads(player); - Utils.count(block.getChunk(), - result -> Utils.onCheck(player, block, maxAmount, result.getTotal(), sfItem)); + Utils.count( + block.getChunk(), + result -> Utils.onCheck(player, block, maxAmount, result.getTotal(), sfItem) + ); } } } + public BlockLimiter getBlockLimiter() { + return blockLimiter; + } + public static HeadLimiter getInstance() { return instance; } + + public void loadConfig() { + ConfigurationSection configurationSection = instance.getConfig().getConfigurationSection("block-limits"); + if (configurationSection == null) { + throw new IllegalStateException("No configuration for groups is available."); + } + for (String key : configurationSection.getKeys(false)) { + BlockLimiter.getInstance().getGroups().add(new Group(configurationSection.getConfigurationSection(key))); + } + } } diff --git a/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockLimiter.java b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockLimiter.java new file mode 100644 index 0000000..09b5c96 --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockLimiter.java @@ -0,0 +1,107 @@ +package dev.j3fftw.headlimiter.blocklimiter; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import com.google.common.base.Preconditions; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.ChunkPosition; + +import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import me.mrCookieSlime.Slimefun.api.BlockStorage; + +import dev.j3fftw.headlimiter.HeadLimiter; + +public final class BlockLimiter { + + private static BlockLimiter instance; + private final HashSet groups = new HashSet<>(); + private final Map contentMap = new HashMap<>(); + + public BlockLimiter(@Nonnull HeadLimiter headLimiter) { + Preconditions.checkArgument(instance == null, "Cannot create a new instance of the BlockLimiter"); + instance = this; + new BlockListener(headLimiter); + headLimiter.getServer().getScheduler().runTaskLater(headLimiter, this::loadBlockStorage, 1); + } + + private void loadBlockStorage() { + for (World world : Bukkit.getWorlds()) { + BlockStorage worldStorage = BlockStorage.getStorage(world); + if (worldStorage == null) { + return; + } else { + for (Map.Entry entry : worldStorage.getRawStorage().entrySet()) { + Location location = entry.getKey(); + String id = entry.getValue().getString("id"); + ChunkPosition chunkPosition = new ChunkPosition(location.getChunk()); + ChunkContent content = contentMap.get(chunkPosition); + if (content == null) { + content = new ChunkContent(); + content.incrementAmount(id); + contentMap.put(chunkPosition, content); + } else { + content.incrementAmount(id); + } + } + } + + } + } + + @Nullable + public ChunkContent getChunkContent(@Nonnull ChunkPosition chunkPosition) { + return contentMap.get(chunkPosition); + } + + public Group getGroupByItem(@Nonnull SlimefunItem slimefunItem) { + return getGroupByItem(slimefunItem.getId()); + } + + @Nullable + public Group getGroupByItem(@Nonnull String itemId) { + for (Group group : this.groups) { + if (group.contains(itemId)) { + return group; + } + } + return null; + } + + public int getPlayerLimitByItem(@Nonnull Player player, @Nonnull SlimefunItem slimefunItem) { + return getPlayerLimitByItem(player, slimefunItem.getId()); + } + + public int getPlayerLimitByItem(@Nonnull Player player, @Nonnull String itemId) { + Group group = getGroupByItem(itemId); + if (group == null) { + return -1; + } else { + return group.getPermissibleAmount(player); + } + } + + public Set getGroups() { + return groups; + } + + public void setChunkContent(@Nonnull ChunkPosition chunkPosition, @Nonnull ChunkContent content) { + contentMap.put(chunkPosition, content); + } + + @Nonnull + public static BlockLimiter getInstance() { + return instance; + } +} diff --git a/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockListener.java b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockListener.java new file mode 100644 index 0000000..698220f --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockListener.java @@ -0,0 +1,74 @@ +package dev.j3fftw.headlimiter.blocklimiter; + +import javax.annotation.Nonnull; + +import org.bukkit.ChatColor; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockBreakEvent; +import io.github.thebusybiscuit.slimefun4.api.events.SlimefunBlockPlaceEvent; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.ChunkPosition; + +import dev.j3fftw.headlimiter.HeadLimiter; + +public class BlockListener implements Listener { + + public BlockListener(@Nonnull HeadLimiter headLimiter) { + headLimiter.getServer().getPluginManager().registerEvents(this, headLimiter); + } + + @EventHandler + public void onSlimefunItemPlaced(@Nonnull SlimefunBlockPlaceEvent event) { + SlimefunItem slimefunItem = event.getSlimefunItem(); + String slimefunItemId = slimefunItem.getId(); + int definedLimit = BlockLimiter.getInstance().getPlayerLimitByItem(event.getPlayer(), slimefunItem); + + if (definedLimit == -1) { + // No limit has been set, nothing required for HeadLimiter + return; + } + + ChunkPosition chunkPosition = new ChunkPosition(event.getBlockPlaced().getChunk()); + ChunkContent content = BlockLimiter.getInstance().getChunkContent(chunkPosition); + + if (content == null) { + // Content is null so no blocks are currently in this chunk, lets set one up - event can continue + content = new ChunkContent(); + content.incrementAmount(slimefunItemId); + BlockLimiter.getInstance().setChunkContent(chunkPosition, content); + } else if (content.getGroupTotal(slimefunItemId) < definedLimit) { + // This chunk can take more of the specified item type + content.incrementAmount(slimefunItemId); + } else { + // Chunk has hit its limit for this type, time to deny the placement + event.setCancelled(true); + event.getPlayer().sendMessage(ChatColor.RED + "You cannot place any more of this item within this chunk."); + } + } + + @EventHandler + public void onSlimefunItemBroken(@Nonnull SlimefunBlockBreakEvent event) { + SlimefunItem slimefunItem = event.getSlimefunItem(); + String slimefunItemId = slimefunItem.getId(); + int definedLimit = BlockLimiter.getInstance().getPlayerLimitByItem(event.getPlayer(), slimefunItem); + if (definedLimit == -1) { + // No limit has been set, nothing required for HeadLimiter + return; + } + + ChunkPosition chunkPosition = new ChunkPosition(event.getBlockBroken().getChunk()); + ChunkContent content = BlockLimiter.getInstance().getChunkContent(chunkPosition); + + if (content == null) { + // Content is null so no blocks are currently in this chunk, shouldn't be possible, but never mind + return; + } + + // This chunk can take more of the specified item type + content.decrementAmount(slimefunItemId); + + } + +} diff --git a/src/main/java/dev/j3fftw/headlimiter/blocklimiter/ChunkContent.java b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/ChunkContent.java new file mode 100644 index 0000000..30adb45 --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/ChunkContent.java @@ -0,0 +1,68 @@ +package dev.j3fftw.headlimiter.blocklimiter; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +public class ChunkContent { + + private final Map contentMap = new HashMap<>(); + + public int getCurrentAmount(@Nonnull SlimefunItem slimefunItem) { + return getCurrentAmount(slimefunItem.getId()); + } + + public int getCurrentAmount(@Nonnull String itemId) { + return this.contentMap.getOrDefault(itemId, 0); + } + + public int getGroupTotal(@Nonnull SlimefunItem slimefunItem) { + return getGroupTotal(slimefunItem.getId()); + } + + public int getGroupTotal(@Nonnull String itemId) { + Set groupSet = BlockLimiter.getInstance().getGroups(); + + for (Group group : groupSet) { + if (group.contains(itemId)) { + int amount = 0; + for (String item : group.getItems()) { + amount += this.contentMap.get(item); + } + return amount; + } + } + + return -1; + } + + public void incrementAmount(@Nonnull SlimefunItem slimefunItem) { + incrementAmount(slimefunItem.getId()); + } + + public void incrementAmount(@Nonnull String itemId) { + int amount = getCurrentAmount(itemId); + setAmount(itemId, amount + 1); + } + + public void decrementAmount(@Nonnull SlimefunItem slimefunItem) { + incrementAmount(slimefunItem.getId()); + } + + public void decrementAmount(@Nonnull String itemId) { + int amount = getCurrentAmount(itemId); + setAmount(itemId, Math.max(0, amount - 1)); + } + + public void setAmount(@Nonnull SlimefunItem slimefunItem, int amount) { + setAmount(slimefunItem.getId(), amount); + } + + public void setAmount(@Nonnull String itemId, int amount) { + contentMap.put(itemId, amount); + } +} diff --git a/src/main/java/dev/j3fftw/headlimiter/blocklimiter/Group.java b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/Group.java new file mode 100644 index 0000000..fb12500 --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/Group.java @@ -0,0 +1,98 @@ +package dev.j3fftw.headlimiter.blocklimiter; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; + +import com.google.common.base.Objects; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +public class Group { + + private final String groupName; + private final int defaultAmount; + private final HashSet items; + private final HashMap permissionAmounts; + + public Group(ConfigurationSection configurationSection) { + this.groupName = configurationSection.getName(); + this.defaultAmount = configurationSection.getInt("items-amount", 0); + this.items = new HashSet<>(configurationSection.getStringList("items")); + this.permissionAmounts = new HashMap<>(); + + ConfigurationSection permissionSection = configurationSection.getConfigurationSection("permission-amount"); + + if (permissionSection != null) { + for (String key : permissionSection.getKeys(false)) { + permissionAmounts.put(key, permissionSection.getInt(key, 0)); + } + } + } + + public String getGroupName() { + return groupName; + } + + public int getDefaultAmount() { + return defaultAmount; + } + + public Set getItems() { + return items; + } + + public Map getPermissionAmounts() { + return permissionAmounts; + } + + public boolean contains(@Nonnull SlimefunItem slimefunItem) { + return contains(slimefunItem.getId()); + } + + public boolean contains(@Nonnull String itemId) { + return this.items.contains(itemId); + } + + public int getPermissibleAmount(@Nonnull Player player) { + int allowable = defaultAmount; + if (!this.permissionAmounts.isEmpty()) { + for (Map.Entry entry : this.permissionAmounts.entrySet()) { + String permission = entry.getKey(); + if (player.hasPermission(permission)) { + allowable = Math.max(entry.getValue(), allowable); + } + } + } + return allowable; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Group group = (Group) o; + return defaultAmount == group.defaultAmount && Objects.equal( + groupName, + group.groupName + ) && Objects.equal(items, group.items) && Objects.equal( + permissionAmounts, + group.permissionAmounts + ); + } + + @Override + public int hashCode() { + return Objects.hashCode(groupName, defaultAmount, items, permissionAmounts); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 157fe55..475946a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,28 +1,36 @@ -## Amount of Cargo machines per chunk (25 by default) -amount: 25 - -## Auto updater (true by default) -auto-update: true - -## Size of the thread pool (4 by default) -## Only touch this setting when you know what you are doing! -thread-pool-size: 4 - -## If a claiming plugin is enabled, this setting prevents players from placing cargo outside of claimed areas -## This makes it impossible to "extend" existing networks from just outside the border in order to steal from claims -block-wilderness-cargo: false - -## Permissions (false by default) -## Permission node: headlimiter.permission. -## Example: headlimiter.permission.noob +## Block Limits +## You can limit all Slimefun Blocks including all addons. +## You will have to make a group per item or item set. ## -## When permissions have been set to true, you can use the values here to set the amount of heads checked. -## You can add as many entries as you want. -## Order has to be reversed or breaky breaky -permissions: false -permission: - ## sefi: 25 - ## walshy: 20 - ## alessio: 15 - ## jeff: 10 - noob: 5 \ No newline at end of file +## Down here you have an example for Cargo. +## cargo: is the group name +## items-amount: is the default amount the group is limited to +## items: will have a list of items you want to block. These are all Slimefun Items IDs (Addons included) +## permission-amount: this will have a list of permissions. This is completely optional and doesn't have to be there, +## If you do not want permissions you can just remove it or not add it to the group. +## cargo: +## items-amount: 25 +## items: +## - CARGO_NODE_INPUT +## - CARGO_NODE_OUTPUT +## - CARGO_NODE_OUTPUT_ADVANCED +## - CARGO_NODE +## - CARGO_MANAGER +## permission-amount: +## example_1: 50 +## example_2: 100 +## example_3: 150 + +block-limits: + cargo: + items-amount: 25 + items: + - CARGO_NODE_INPUT + - CARGO_NODE_OUTPUT + - CARGO_NODE_OUTPUT_ADVANCED + - CARGO_NODE + - CARGO_MANAGER + permission-amount: + example_1: 50 + example_2: 100 + example_3: 150 \ No newline at end of file