diff --git a/.gitignore b/.gitignore index cf6c107..de031b0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ *.sh dependency-reduced-pom.xml /dependency-reduced-pom.xml +*.DS_Store +/local/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index bbda6e5..d8ce874 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ clean package - ${project.artifactId}-${project.version} + ${project.artifactId} ${basedir}/src/main/java @@ -107,9 +107,9 @@ - com.github.Slimefun + com.github.StarWishsama Slimefun4 - RC-32 + eb20e5911f provided @@ -136,7 +136,7 @@ net.guizhanss GuizhanLibPlugin - 1.1.0 + 1.3.1 provided diff --git a/src/main/java/dev/j3fftw/headlimiter/CountCommand.java b/src/main/java/dev/j3fftw/headlimiter/CountCommand.java index 70398ed..5d19c3b 100644 --- a/src/main/java/dev/j3fftw/headlimiter/CountCommand.java +++ b/src/main/java/dev/j3fftw/headlimiter/CountCommand.java @@ -27,7 +27,7 @@ public boolean onCommand(CommandSender sender, Command command, String s, String } message.append(ChatColor.GOLD) - .append("当前数量: ") + .append("当前数量:") .append(result.getTotal()) .append("/") .append(Utils.getMaxHeads(player)) @@ -38,7 +38,7 @@ public boolean onCommand(CommandSender sender, Command command, String s, String message.append(" ") .append(ChatColor.GRAY) .append(entry.getKey()) - .append(": ") + .append(":") .append(ChatColor.YELLOW) .append(entry.getValue()) .append('\n'); @@ -48,7 +48,7 @@ public boolean onCommand(CommandSender sender, Command command, String s, String }); } else { sender.sendMessage(ChatColor.GOLD + "/hl count" - + ChatColor.GRAY + " - 统计该区块中有多少个头颅" + + ChatColor.GRAY + " - 统计该区块中有多少个方块" ); } diff --git a/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java b/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java index 9b36549..e711d5f 100644 --- a/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java +++ b/src/main/java/dev/j3fftw/headlimiter/HeadLimiter.java @@ -1,10 +1,13 @@ package dev.j3fftw.headlimiter; -import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; -import net.guizhanss.guizhanlibplugin.updater.GuizhanBuildsUpdaterWrapper; +import java.io.File; +import java.util.logging.Level; + +import dev.j3fftw.headlimiter.blocklimiter.Group; +import net.guizhanss.guizhanlibplugin.updater.GuizhanUpdater; 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 +15,31 @@ 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 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 (!getServer().getPluginManager().isPluginEnabled("GuizhanLibPlugin")) { + getLogger().log(Level.SEVERE, "本插件需要 鬼斩前置库插件(GuizhanLibPlugin) 才能运行!"); + getLogger().log(Level.SEVERE, "从此处下载: https://50L.cc/gzlib"); + getServer().getPluginManager().disablePlugin(this); + return; + } + + if (!new File(getDataFolder(), "config.yml").exists()) { saveDefaultConfig(); + } Utils.loadPermissions(); @@ -33,8 +50,11 @@ public void onEnable() { new MetricsService(this).start(); if (getConfig().getBoolean("auto-update") && getDescription().getVersion().startsWith("Build")) { - GuizhanBuildsUpdaterWrapper.start(this, getFile(), "ybw0014", "HeadLimiter-CN", "master", false); + GuizhanUpdater.start(this, getFile(), "SlimefunGuguProject", "HeadLimiter", "master"); } + + this.blockLimiter = new BlockLimiter(this); + loadConfig(); } @Override @@ -64,13 +84,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("没有配置任何方块组!"); + } + 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..fce5a90 --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockLimiter.java @@ -0,0 +1,82 @@ +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 com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.BlockDataController; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import org.bukkit.Bukkit; +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 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); + } + + @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..4b8cb21 --- /dev/null +++ b/src/main/java/dev/j3fftw/headlimiter/blocklimiter/BlockListener.java @@ -0,0 +1,96 @@ +package dev.j3fftw.headlimiter.blocklimiter; + +import javax.annotation.Nonnull; + +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; +import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunChunkData; +import com.xzavier0722.mc.plugin.slimefun4.storage.event.SlimefunChunkDataLoadEvent; +import org.bukkit.ChatColor; +import org.bukkit.Chunk; +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 onSlimefunChunkLoad(@Nonnull SlimefunChunkDataLoadEvent event) { + BlockLimiter blockLimiter = HeadLimiter.getInstance().getBlockLimiter(); + ChunkPosition chunkPos = new ChunkPosition(event.getChunk()); + + for (SlimefunBlockData blockData : event.getChunkData().getAllBlockData()) { + String id = blockData.getSfId(); + ChunkContent content = blockLimiter.getChunkContent(chunkPos); + if (content == null) { + content = new ChunkContent(); + content.incrementAmount(id); + blockLimiter.setChunkContent(chunkPos, content); + } else { + content.incrementAmount(id); + } + } + } + + @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 + "你不能在该区块中放置更多。"); + } + } + + @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 1aca27f..5afb64a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,27 +1,37 @@ -## 每个区域的货运数量 (默认: 25) -amount: 25 - -## 自动更新 (默认: true) -auto-update: true - -## 线程池大小 (默认: 4) -## 如果你更改此设置,请确保你清楚你在做什么! -thread-pool-size: 4 - -## 如果服务器拥有领地类型(Towny)的插件,则玩家无法在领地外放置货运节点等 -## 这将阻止从领地外延伸货运系统并偷取物品 -block-wilderness-cargo: false - -## 启用权限 (默认为false,关闭) -## 权限节点: headlimiter.permission. -## 例子: headlimiter.permission.noob +## 方块限制 +## 现在可以限制任何粘液科技中的方块放置,包括附属中的方块。 +## 你需要添加方块组,然后进行相关设置。 ## -## 当开启权限时,你可以设置权限对应的头颅数量上限 -## 权限节点的数量应从大到小排序(参考默认顺序) -permissions: false -permission: - ## sefi: 25 - ## walshy: 20 - ## alessio: 15 - ## jeff: 10 - noob: 5 \ No newline at end of file +## 示例配置是一个限制区块内所有货运节点的例子。 +## cargo:这是方块组的名称,使用英文、数字或下划线的组合,不要使用其他的奇怪名称和符号。 +## items-amount:每个区块内最多可以放置多少方块。(组内的所有方块叠加起来的上限) +## items:方块的粘液物品ID列表(支持附属)。 +## permission-amount:你可以在此增加权限节点,以设置不同的数量限制。 +## 如果你不需要这一项,可以删除。 +## 对应的权限节点为:headlimiter.permission.<权限> +## 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 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a3c4862..eeeccd6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: HeadLimiter version: ${project.version} main: dev.j3fftw.headlimiter.HeadLimiter -depend: [Slimefun, GuizhanLibPlugin] +depend: [Slimefun] +softdepend: [GuizhanLibPlugin] api-version: 1.16 commands: