diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/BukkitMain.java index 0d5530424..64f2f4d1f 100644 --- a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/BukkitMain.java @@ -19,6 +19,7 @@ import com.github.intellectualsites.plotsquared.bukkit.listeners.PlotPlusListene import com.github.intellectualsites.plotsquared.bukkit.listeners.SingleWorldListener; import com.github.intellectualsites.plotsquared.bukkit.listeners.WorldEvents; import com.github.intellectualsites.plotsquared.bukkit.titles.DefaultTitle_111; +import com.github.intellectualsites.plotsquared.bukkit.util.BukkitBlockRegistry; import com.github.intellectualsites.plotsquared.bukkit.util.BukkitChatManager; import com.github.intellectualsites.plotsquared.bukkit.util.BukkitChunkManager; import com.github.intellectualsites.plotsquared.bukkit.util.BukkitCommand; @@ -51,6 +52,7 @@ import com.github.intellectualsites.plotsquared.plot.generator.GeneratorWrapper; import com.github.intellectualsites.plotsquared.plot.generator.HybridGen; import com.github.intellectualsites.plotsquared.plot.generator.HybridUtils; import com.github.intellectualsites.plotsquared.plot.generator.IndependentPlotGenerator; +import com.github.intellectualsites.plotsquared.plot.object.BlockRegistry; import com.github.intellectualsites.plotsquared.plot.object.Plot; import com.github.intellectualsites.plotsquared.plot.object.PlotArea; import com.github.intellectualsites.plotsquared.plot.object.PlotId; @@ -98,6 +100,7 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.PluginCommand; @@ -112,7 +115,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain { +public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain { @Getter private static WorldEdit worldEdit; private static Map pluginMap; @@ -169,6 +172,7 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain @Getter private SingleWorldListener singleWorldListener; private Method methodUnloadChunk0; private boolean methodUnloadSetup = false; + private final BlockRegistry blockRegistry = new BukkitBlockRegistry(Material.values()); @Override public int[] getServerVersion() { if (this.version == null) { @@ -922,6 +926,11 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain return names; } + @Override + public BlockRegistry getBlockRegistry() { + return this.blockRegistry; + } + @Override public LegacyMappings getLegacyMappings() { return this.legacyMappings; } diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitBlockRegistry.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitBlockRegistry.java new file mode 100644 index 000000000..1d416f6bf --- /dev/null +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/BukkitBlockRegistry.java @@ -0,0 +1,19 @@ +package com.github.intellectualsites.plotsquared.bukkit.util; + +import com.github.intellectualsites.plotsquared.plot.object.BlockRegistry; +import com.github.intellectualsites.plotsquared.plot.object.PlotBlock; +import lombok.NonNull; +import org.bukkit.Material; + +public class BukkitBlockRegistry extends BlockRegistry { + + public BukkitBlockRegistry(final Material... preInitializedItems) { + super(Material.class, preInitializedItems); + } + + @Override + public PlotBlock getPlotBlock(@NonNull final Material item) { + return PlotBlock.get(item.name()); + } + +} diff --git a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/LegacyMappings.java b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/LegacyMappings.java index f23752d5a..b38ea9e0e 100644 --- a/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/LegacyMappings.java +++ b/Bukkit/src/main/java/com/github/intellectualsites/plotsquared/bukkit/util/LegacyMappings.java @@ -1,11 +1,15 @@ package com.github.intellectualsites.plotsquared.bukkit.util; -import lombok.*; -import org.bukkit.Material; - import java.util.HashMap; import java.util.Locale; import java.util.Map; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.bukkit.Material; /** * Borrowed from https://github.com/Phoenix616/IDConverter/blob/master/mappings/src/main/java/de/themoep/idconverter/IdMappings.java @@ -849,6 +853,51 @@ public class LegacyMappings { } } + /** + * Try to find a legacy plot block by any means possible. + * Strategy: + * - Check if the name contains a namespace, if so, strip it + * - Check if there's a (new) material matching the name + * - Check if there's a legacy material matching the name + * - Check if there's a numerical ID matching the name + * - Return null if everything else fails + * + * @param string String ID + * @return LegacyBlock if found, else null + */ + public static LegacyBlock fromAny(@NonNull final String string) { + String workingString = string; + String[] parts = null; + if (string.contains(":")) { + parts = string.split(":"); + if (parts.length > 1) { + if (parts[0].equalsIgnoreCase("minecraft")) { + workingString = parts[1]; + } else { + workingString = parts[0]; + } + } + } + LegacyBlock plotBlock = fromNewName(workingString); + if (plotBlock != null) { + return plotBlock; + } else if ((plotBlock = fromLegacyName(workingString)) != null) { + return plotBlock; + } else { + try { + if (parts != null && parts.length > 1) { + final int id = Integer.parseInt(parts[0]); + final int data = Integer.parseInt(parts[1]); + return fromIdAndData(id, data); + } else { + return fromLegacyId(Integer.parseInt(workingString)); + } + } catch (final Throwable exception) { + return null; + } + } + } + public static LegacyBlock fromLegacyId(final int id) { return NUMERICAL_MAP.get(id); } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/IPlotMain.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/IPlotMain.java index 4c9356dcb..352c2fc3a 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/IPlotMain.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/IPlotMain.java @@ -4,6 +4,7 @@ import com.github.intellectualsites.plotsquared.plot.generator.GeneratorWrapper; import com.github.intellectualsites.plotsquared.plot.generator.HybridUtils; import com.github.intellectualsites.plotsquared.plot.generator.IndependentPlotGenerator; import com.github.intellectualsites.plotsquared.plot.logger.ILogger; +import com.github.intellectualsites.plotsquared.plot.object.BlockRegistry; import com.github.intellectualsites.plotsquared.plot.object.PlotPlayer; import com.github.intellectualsites.plotsquared.plot.util.*; import com.github.intellectualsites.plotsquared.plot.util.block.QueueProvider; @@ -11,7 +12,7 @@ import com.github.intellectualsites.plotsquared.plot.util.block.QueueProvider; import java.io.File; import java.util.List; -public interface IPlotMain extends ILogger { +public interface IPlotMain extends ILogger { /** * Log a message to console. @@ -273,5 +274,8 @@ public interface IPlotMain extends ILogger { List getPluginIds(); + BlockRegistry getBlockRegistry(); + LegacyMappings getLegacyMappings(); + } diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java index 7df62a6d0..517678835 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/config/C.java @@ -831,7 +831,11 @@ public enum C { HELP_FOOTER("$3&m---------&r $1Plot\u00B2 Help $3&m---------", "Help"), HELP_INFO_ITEM("$1/plot help %category% $3- $2%category_desc%", "Help"), HELP_ITEM( - "$1%usage% [%alias%]&- $3- $2%desc%&-", "Help"), /* + "$1%usage% [%alias%]&- $3- $2%desc%&-", "Help"), + + BUCKET_ENTRIES_IGNORED("$2Total bucket values add up to 1 or more. Blocks without a spcified chance will be ignored", "Generator_Bucket"), + + /* * Direction */ diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java new file mode 100644 index 000000000..49d921ad8 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockBucket.java @@ -0,0 +1,154 @@ +package com.github.intellectualsites.plotsquared.plot.object; + +import com.github.intellectualsites.plotsquared.plot.config.C; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * A block bucket is a container of block types, where each block + * has a specified chance of being randomly picked + */ +@SuppressWarnings({"unused", "WeakerAccess"}) +public final class BlockBucket implements Iterable { + + private final Random random = new Random(); + private final Map ranges = new HashMap<>(); + private final Map blocks; + private final BucketIterator bucketIterator = new BucketIterator(); + private boolean compiled; + + public BlockBucket() { + this.blocks = new HashMap<>(); + } + + public void addBlock(@NonNull final PlotBlock block) { + this.addBlock(block, -1); + } + + public void addBlock(@NonNull final PlotBlock block, final int chance) { + this.blocks.put(block, chance); + this.compiled = false; + } + + public void compile() { + if (isCompiled()) { + return; + } + final Map temp = new HashMap<>(blocks.size()); + final List unassigned = new ArrayList<>(blocks.size()); + + int sum = 0; + for (final Map.Entry entry : blocks.entrySet()) { + if (entry.getValue() == -1) { + unassigned.add(entry.getKey()); + } else { + sum += entry.getValue(); + } + } + // + // If this doesn't amount to 100 add it up to exactly 100. + // + if (sum < 100) { + final int remaining = 100 - sum; + final int perUnassigned = remaining / unassigned.size(); + for (final PlotBlock block : unassigned) { + temp.put(block, perUnassigned); + sum += perUnassigned; + } + // Make sure there isn't a tiny difference remaining + if (sum < 100) { + final int difference = 100 - sum; + temp.put(unassigned.get(0), perUnassigned + difference); + sum = 100; + } + } else if (!unassigned.isEmpty()) { + C.BUCKET_ENTRIES_IGNORED.send(ConsolePlayer.getConsole()); + } + // + // If the sum adds up to more than 100, divide all values + // + if (sum > 100) { + final double ratio = 100D / sum; + for (final Map.Entry entry : blocks.entrySet()) { + if (entry.getValue() == -1) { + continue; + } + temp.put(entry.getKey(), (int)(entry.getValue() * ratio)); + } + } else { + temp.forEach(temp::put); + } + int start = 0; + for (final Map.Entry entry : temp.entrySet()) { + final int rangeStart = start; + final int rangeEnd = rangeStart + entry.getValue(); + start = rangeEnd + 1; + final Range range = new Range(rangeStart, rangeEnd); + this.ranges.put(range, entry.getKey()); + } + this.blocks.clear(); + this.compiled = true; + } + + @Override + public Iterator iterator() { + return this.bucketIterator; + } + + public boolean isCompiled() { + return this.compiled; + } + + /** + * Get a random block out of the bucket + * + * @return Randomly picked block (cased on specified rates) + */ + public PlotBlock getBlock() { + if (!isCompiled()) { + this.compile(); + } + final int number = random.nextInt(101); + for (final Map.Entry entry : ranges.entrySet()) { + if (entry.getKey().isInRange(number)) { + return entry.getValue(); + } + } + // Didn't find a block? Try again + return getBlock(); + } + + private final class BucketIterator implements Iterator { + + @Override + public boolean hasNext() { + return true; + } + + @Override + public PlotBlock next() { + return getBlock(); + } + } + + @Getter + @EqualsAndHashCode + @RequiredArgsConstructor + private final static class Range { + + private final int min; + private final int max; + + public boolean isInRange(final int num) { + return num <= max && num >= min; + } + } +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockRegistry.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockRegistry.java new file mode 100644 index 000000000..49c6ab0b4 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockRegistry.java @@ -0,0 +1,34 @@ +package com.github.intellectualsites.plotsquared.plot.object; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.NonNull; + +public abstract class BlockRegistry { + + @Getter + private final Class type; + private final Map map = new HashMap<>(); + + public BlockRegistry(@NonNull final Class type, final T... preInitializedItems) { + this.type = type; + for (final T preInitializedItem : preInitializedItems) { + this.addMapping(getPlotBlock(preInitializedItem), preInitializedItem); + } + } + + public final void addMapping(@NonNull final PlotBlock plotBlock, @NonNull final T t) { + if (map.containsKey(plotBlock)) { + return; + } + this.map.put(plotBlock, t); + } + + public abstract PlotBlock getPlotBlock(final T item); + + public final T getItem(final PlotBlock plotBlock) { + return this.map.get(plotBlock); + } + +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/PlotBlock.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/PlotBlock.java index 17bbccc39..4599c0d14 100644 --- a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/PlotBlock.java +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/PlotBlock.java @@ -1,5 +1,6 @@ package com.github.intellectualsites.plotsquared.plot.object; +import com.github.intellectualsites.plotsquared.plot.PlotSquared; import com.github.intellectualsites.plotsquared.plot.config.Settings; import lombok.NonNull; @@ -7,6 +8,9 @@ import java.util.Collection; public abstract class PlotBlock { + private static Class conversionType; + private static BlockRegistry blockRegistry; + public static boolean isEverything(@NonNull final PlotBlock block) { return block.equals(LegacyPlotBlock.EVERYTHING) || block.equals(StringPlotBlock.EVERYTHING); } @@ -56,6 +60,21 @@ public abstract class PlotBlock { return get(((LegacyPlotBlock) plotBlock).getId(), (byte) 0); } + public static PlotBlock get(@NonNull final Object type) { + if (blockRegistry == null) { + blockRegistry = PlotSquared.imp().getBlockRegistry(); + if (blockRegistry == null) { + throw new UnsupportedOperationException("The PlotSquared implementation has not registered a custom block registry." + + " This method can't be used."); + } + conversionType = blockRegistry.getType(); + } + if (!type.getClass().equals(conversionType)) { + throw new UnsupportedOperationException("The PlotSquared implementation has not registered a block registry for this object type"); + } + return blockRegistry.getPlotBlock(type); + } + public final boolean equalsAny(final int id, @NonNull final String stringId) { if (this instanceof StringPlotBlock) { final StringPlotBlock stringPlotBlock = (StringPlotBlock) this;