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 e3535231b..e1be65cd6 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; @@ -168,6 +171,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) { @@ -920,4 +924,9 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain } return names; } + + @Override + public BlockRegistry getBlockRegistry() { + return this.blockRegistry; + } } 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 c797a76f3..972c7396f 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. @@ -272,4 +273,6 @@ public interface IPlotMain extends ILogger { AbstractTitle initTitleManager(); List getPluginIds(); + + BlockRegistry getBlockRegistry(); } 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..f8be06c45 --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockRegistry.java @@ -0,0 +1,31 @@ +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; + } + + 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/BlockWrapper.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapper.java new file mode 100644 index 000000000..b63de816a --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapper.java @@ -0,0 +1,40 @@ +package com.github.intellectualsites.plotsquared.plot.object; + +import java.util.Collections; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public final class BlockWrapper { + + @Getter + private final PlotBlock type; + private final Map blockStates; + + public T getState(@NonNull final Class stateType, @NonNull final Class valueType, @NonNull final T defaultValue) { + if (!blockStates.containsKey(stateType)) { + return defaultValue; + } + final Object rawValue = blockStates.get(stateType); + if (!rawValue.getClass().equals(valueType)) { + throw new ClassCastException(String.format("State type %s has a value of type %s but %s was requested", + stateType.getSimpleName(), rawValue.getClass().getSimpleName(), valueType.getSimpleName())); + } + return valueType.cast(rawValue); + } + + public void setState(@NonNull final Class stateType, @NonNull final T value) { + this.blockStates.put(stateType, value); + } + + public Map getAllStates() { + return Collections.unmodifiableMap(this.blockStates); + } + +} diff --git a/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java new file mode 100644 index 000000000..9e8ce873c --- /dev/null +++ b/Core/src/main/java/com/github/intellectualsites/plotsquared/plot/object/BlockWrapperFactory.java @@ -0,0 +1,97 @@ +package com.github.intellectualsites.plotsquared.plot.object; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +public final class BlockWrapperFactory { + + @RequiredArgsConstructor + public static final class StateEntry { + + @Getter + private final StateType stateType; + @Getter + private final ObjectType value; + + } + + public interface BlockStateDeserializer { + StateEntry deserialize(PlotBlock type, String serialized); + boolean isOfType(String serializedString); + } + + @FunctionalInterface + public interface BlockStateSerializer { + String serialize(PlotBlock type, StateEntry entry); + } + + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) + public static final class StateSerializationMapping { + @Getter + private final StateType type; + @Getter + private final ObjectType objectType; + @Getter + private final BlockStateSerializer serializer; + @Getter + private final BlockStateDeserializer deserializer; + } + + private final Map stateSerializationMappings = new HashMap<>(); + + public StateSerializationMapping + addStateMapping(@NonNull final StateType type, @NonNull final ObjectType objectType, @NonNull final BlockStateSerializer serializer, @NonNull final BlockStateDeserializer deserializer) { + final StateSerializationMapping stateSerializationMapping = new StateSerializationMapping<>(type, objectType, serializer, deserializer); + this.stateSerializationMappings.put(type, stateSerializationMapping); + return stateSerializationMapping; + } + + public StateSerializationMapping + getStateMapping(@NonNull final StateType type) { + return this.stateSerializationMappings.get(type); + } + + public Collection serializeStates(@NonNull final BlockWrapper blockWrapper) { + final List serializedStates = new ArrayList<>(); + blockWrapper.getAllStates().entrySet().stream().map(entry -> new StateEntry(entry.getKey(), entry.getValue())) + .forEach(entry -> { + final StateSerializationMapping stateSerializationMapping = getStateMapping(entry.getStateType()); + final BlockStateSerializer blockStateSerializer = stateSerializationMapping.getSerializer(); + final String serialized = blockStateSerializer.serialize(blockWrapper.getType(), entry); + serializedStates.add(serialized); + }); + return serializedStates; + } + + public BlockStateDeserializer getDeserializerRaw(@NonNull final String serializedString) { + for (final StateSerializationMapping stateSerializationMapping : this.stateSerializationMappings.values()) { + if (stateSerializationMapping.getDeserializer().isOfType(serializedString)) { + return stateSerializationMapping.getDeserializer(); + } + } + return null; + } + + public Collection deserializeStates(@NonNull final PlotBlock plotBlock, @NonNull final Collection serializedStates) { + final Collection stateEntries = new ArrayList<>(serializedStates.size()); + for (final String serializedState : serializedStates) { + if (serializedState == null || serializedState.isEmpty()) { + continue; + } + final BlockStateDeserializer blockStateDeserializer = getDeserializerRaw(serializedState); + if (blockStateDeserializer == null) { + throw new IllegalStateException(String.format("No deserializer available for %s", serializedState)); + } + stateEntries.add(blockStateDeserializer.deserialize(plotBlock, serializedState)); + } + return stateEntries; + } + +} 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..c73a0ccbb 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(blockRegistry.getClass())) { + 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;