From 789098dc7959ea274b3451d222d39181b287e497 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Sun, 3 Apr 2016 02:50:50 +1000 Subject: [PATCH] Finish with sponge depend Also fix 1.8 issue for kh498 --- build.gradle | 2 +- bukkit/build/resources/main/plugin.yml | 2 +- .../com/boydti/fawe/bukkit/FaweBukkit.java | 33 +- .../fawe/bukkit}/logging/BlocksHubHook.java | 11 +- .../fawe/bukkit}/logging/LoggingExtent.java | 24 +- .../bukkit/v0/BukkitEditSessionWrapper_0.java | 6 +- .../fawe/bukkit/v1_8/BukkitQueue_1_8.java | 123 ++- bukkit/src/main/resources/plugin.yml | 2 +- core/src/main/java/com/boydti/fawe/Fawe.java | 42 +- core/src/main/java/com/boydti/fawe/IFawe.java | 8 +- .../main/java/com/boydti/fawe/config/BBC.java | 20 +- .../java/com/boydti/fawe/config/Settings.java | 5 +- .../fawe/configuration/Configuration.java | 86 ++ .../configuration/ConfigurationOptions.java | 92 ++ .../configuration/ConfigurationSection.java | 646 +++++++++++++ .../InvalidConfigurationException.java | 45 + .../configuration/MemoryConfiguration.java | 93 ++ .../MemoryConfigurationOptions.java | 30 + .../fawe/configuration/MemorySection.java | 847 ++++++++++++++++++ .../configuration/file/FileConfiguration.java | 219 +++++ .../file/FileConfigurationOptions.java | 119 +++ .../configuration/file/YamlConfiguration.java | 229 +++++ .../file/YamlConfigurationOptions.java | 76 ++ .../configuration/file/YamlConstructor.java | 47 + .../configuration/file/YamlRepresenter.java | 38 + .../ConfigurationSerializable.java | 35 + .../ConfigurationSerialization.java | 264 ++++++ .../DelegateDeserialization.java | 23 + .../serialization/SerializableAs.java | 34 + .../fawe/object/EditSessionWrapper.java | 4 +- .../com/boydti/fawe/object/FaweChangeSet.java | 44 - .../com/boydti/fawe/object/FawePlayer.java | 54 ++ .../com/boydti/fawe/object/HistoryExtent.java | 113 +-- .../com/boydti/fawe/object/NullChangeSet.java | 16 +- .../object/changeset/CPUOptimizedHistory.java | 13 - .../object/changeset/DiskStorageHistory.java | 77 +- .../fawe/object/changeset/FaweChangeSet.java | 11 + .../object/changeset/FlushableChangeSet.java | 5 - .../changeset/MemoryOptimizedHistory.java | 110 +-- .../regions/general/PlotSquaredFeature.java | 7 +- .../java/com/boydti/fawe/util/MainUtil.java | 14 +- .../com/boydti/fawe/util/ReflectionUtils.java | 6 +- .../java/com/sk89q/worldedit/EditSession.java | 40 +- .../com/sk89q/worldedit/LocalSession.java | 27 +- .../extension/platform/CommandManager.java | 24 +- forge/.gradle/gradle.log | 93 ++ .../com/boydti/fawe/forge/FaweSponge.java | 119 ++- .../com/boydti/fawe/forge/SpongeMain.java | 57 ++ .../com/boydti/fawe/forge/SpongePlayer.java | 9 +- .../com/boydti/fawe/forge/SpongeTaskMan.java | 72 ++ .../com/boydti/fawe/forge/SpongeUtil.java | 75 ++ .../forge/v0/SpongeEditSessionWrapper_0.java | 13 + .../boydti/fawe/forge/v0/SpongeQueue_0.java | 142 +++ .../fawe/forge/v1_8/SpongeChunk_1_8.java | 228 +++++ .../fawe/forge/v1_8/SpongeQueue_1_8.java | 304 +++++++ pom.xml | 2 +- 56 files changed, 4373 insertions(+), 507 deletions(-) rename {core/src/main/java/com/boydti/fawe => bukkit/src/main/java/com/boydti/fawe/bukkit}/logging/BlocksHubHook.java (94%) rename {core/src/main/java/com/boydti/fawe => bukkit/src/main/java/com/boydti/fawe/bukkit}/logging/LoggingExtent.java (98%) create mode 100644 core/src/main/java/com/boydti/fawe/configuration/Configuration.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/ConfigurationOptions.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/ConfigurationSection.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/InvalidConfigurationException.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/MemoryConfiguration.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/MemoryConfigurationOptions.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/MemorySection.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/FileConfiguration.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/FileConfigurationOptions.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/YamlConfiguration.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/YamlConfigurationOptions.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/YamlConstructor.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/file/YamlRepresenter.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerializable.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerialization.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/serialization/DelegateDeserialization.java create mode 100644 core/src/main/java/com/boydti/fawe/configuration/serialization/SerializableAs.java delete mode 100644 core/src/main/java/com/boydti/fawe/object/FaweChangeSet.java delete mode 100644 core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedHistory.java create mode 100644 core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java delete mode 100644 core/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java create mode 100644 forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java diff --git a/build.gradle b/build.gradle index 13e9eed3..0908e34b 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } group = 'com.boydti.fawe' -version = '3.3.4' +version = '3.3.5' description = """FastAsyncWorldEdit""" subprojects { diff --git a/bukkit/build/resources/main/plugin.yml b/bukkit/build/resources/main/plugin.yml index c38f7c85..18e2a830 100644 --- a/bukkit/build/resources/main/plugin.yml +++ b/bukkit/build/resources/main/plugin.yml @@ -1,6 +1,6 @@ name: FastAsyncWorldEdit main: com.boydti.fawe.bukkit.FaweBukkit -version: 3.3.4 +version: 3.3.5 description: Fast Async WorldEdit plugin authors: [Empire92] loadbefore: [WorldEdit] diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index dd401a27..a9aabe42 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -28,13 +28,18 @@ import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; -public class FaweBukkit extends JavaPlugin implements IFawe { +public class FaweBukkit extends JavaPlugin implements IFawe, Listener { private VaultUtil vault; private WorldEditPlugin worldedit; @@ -53,6 +58,7 @@ public class FaweBukkit extends JavaPlugin implements IFawe { @Override public void onEnable() { try { + Bukkit.getPluginManager().registerEvents(this, this); Fawe.set(this); try { final Class clazz = Class.forName("org.spigotmc.AsyncCatcher"); @@ -85,9 +91,16 @@ public class FaweBukkit extends JavaPlugin implements IFawe { @Override public FawePlayer wrap(final Object obj) { if (obj.getClass() == String.class) { - return new BukkitPlayer(Bukkit.getPlayer((String) obj)); + String name = (String) obj; + FawePlayer existing = Fawe.get().getCachedPlayer(name); + if (existing != null) { + return existing; + } + return new BukkitPlayer(Bukkit.getPlayer(name)); } else if (obj instanceof Player) { - return new BukkitPlayer((Player) obj); + Player player = (Player) obj; + FawePlayer existing = Fawe.get().getCachedPlayer(player.getName()); + return existing != null ? existing : new BukkitPlayer(player); } else { return null; } @@ -100,6 +113,15 @@ public class FaweBukkit extends JavaPlugin implements IFawe { debug("&6Metrics enabled."); } + @Override + public Set getPlayers() { + HashSet players = new HashSet<>(); + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + players.add(wrap(player)); + } + return players; + } + /** * Kinda a really messy class I just copied over from an old project
* - Still works, so cbf cleaning it up
@@ -300,4 +322,9 @@ public class FaweBukkit extends JavaPlugin implements IFawe { } return managers; } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Fawe.get().unregister(event.getPlayer().getName()); + } } diff --git a/core/src/main/java/com/boydti/fawe/logging/BlocksHubHook.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/BlocksHubHook.java similarity index 94% rename from core/src/main/java/com/boydti/fawe/logging/BlocksHubHook.java rename to bukkit/src/main/java/com/boydti/fawe/bukkit/logging/BlocksHubHook.java index 7bfd972b..f6797ceb 100644 --- a/core/src/main/java/com/boydti/fawe/logging/BlocksHubHook.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/BlocksHubHook.java @@ -1,13 +1,12 @@ -package com.boydti.fawe.logging; - -import org.PrimeSoft.blocksHub.BlocksHub; -import org.PrimeSoft.blocksHub.IBlocksHubApi; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; +package com.boydti.fawe.bukkit.logging; import com.boydti.fawe.object.FawePlayer; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.history.changeset.ChangeSet; +import org.PrimeSoft.blocksHub.BlocksHub; +import org.PrimeSoft.blocksHub.IBlocksHubApi; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; public class BlocksHubHook { private final BlocksHub hub; diff --git a/core/src/main/java/com/boydti/fawe/logging/LoggingExtent.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java similarity index 98% rename from core/src/main/java/com/boydti/fawe/logging/LoggingExtent.java rename to bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java index 7d25412c..76fe933f 100644 --- a/core/src/main/java/com/boydti/fawe/logging/LoggingExtent.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/logging/LoggingExtent.java @@ -1,15 +1,4 @@ -package com.boydti.fawe.logging; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nullable; - -import org.PrimeSoft.blocksHub.IBlocksHubApi; -import org.bukkit.World; -import org.bukkit.entity.Player; +package com.boydti.fawe.bukkit.logging; import com.boydti.fawe.object.FawePlayer; import com.sk89q.worldedit.Vector; @@ -25,9 +14,18 @@ import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; +import org.PrimeSoft.blocksHub.IBlocksHubApi; +import org.bukkit.World; +import org.bukkit.entity.Player; + + +import static com.google.common.base.Preconditions.checkNotNull; /** - * Stores changes to a {@link ChangeSet}. + * Stores changes to a {@link com.sk89q.worldedit.history.changeset.ChangeSet}. * Logs changes to BlocksHub */ public class LoggingExtent extends AbstractDelegateExtent { diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java index eb5ffae3..4577ade3 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java @@ -1,11 +1,11 @@ package com.boydti.fawe.bukkit.v0; -import com.boydti.fawe.logging.BlocksHubHook; +import com.boydti.fawe.bukkit.logging.BlocksHubHook; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.history.changeset.ChangeSet; public class BukkitEditSessionWrapper_0 extends EditSessionWrapper { @@ -20,7 +20,7 @@ public class BukkitEditSessionWrapper_0 extends EditSessionWrapper { } @Override - public Extent getHistoryExtent(final Extent parent, final ChangeSet set, final FawePlayer player) { + public Extent getHistoryExtent(final Extent parent, final FaweChangeSet set, final FawePlayer player) { if (this.hook != null) { // If we are doing logging, return a custom logging extent return this.hook.getLoggingExtent(parent, set, player); diff --git a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java index 3fe69cfe..f2252712 100644 --- a/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java +++ b/bukkit/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue_1_8.java @@ -1,31 +1,5 @@ package com.boydti.fawe.bukkit.v1_8; -import static com.boydti.fawe.util.ReflectionUtils.getRefClass; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.World.Environment; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.generator.BlockPopulator; -import org.bukkit.generator.ChunkGenerator; - import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; @@ -40,11 +14,36 @@ import com.boydti.fawe.util.ReflectionUtils.RefConstructor; import com.boydti.fawe.util.ReflectionUtils.RefField; import com.boydti.fawe.util.ReflectionUtils.RefMethod; import com.boydti.fawe.util.ReflectionUtils.RefMethod.RefExecutor; +import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalWorld; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.bukkit.BukkitUtil; import com.sk89q.worldedit.world.biome.BaseBiome; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.ChunkGenerator; + + +import static com.boydti.fawe.util.ReflectionUtils.getRefClass; public class BukkitQueue_1_8 extends BukkitQueue_0 { @@ -116,60 +115,35 @@ public class BukkitQueue_1_8 extends BukkitQueue_0 { @Override public Collection> sendChunk(final Collection> fcs) { - final HashMap, Object> packets = new HashMap<>(); - final HashMap>> map = new HashMap<>(); - for (final FaweChunk fc : fcs) { - final String world = fc.getChunkLoc().world; - ArrayList> list = map.get(world); - if (list == null) { - list = new ArrayList<>(); - map.put(world, list); - } - list.add(fc); + sendChunk(fc); } + return new ArrayList<>(); + } + + public void sendChunk(FaweChunk fc) { + fixLighting(fc, Settings.FIX_ALL_LIGHTING); + Chunk chunk = fc.getChunk(); + World world = chunk.getWorld(); final int view = Bukkit.getServer().getViewDistance(); + int cx = chunk.getX(); + int cz = chunk.getZ(); for (final Player player : Bukkit.getOnlinePlayers()) { - final String world = player.getWorld().getName(); - final ArrayList> list = map.get(world); - if (list == null) { + if (!player.getWorld().equals(world)) { continue; } final Location loc = player.getLocation(); - final int cx = loc.getBlockX() >> 4; - final int cz = loc.getBlockZ() >> 4; + final int px = loc.getBlockX() >> 4; + final int pz = loc.getBlockZ() >> 4; + if ((Math.abs(cx - px) > view) || (Math.abs(cz - pz) > view)) { + continue; + } final Object entity = this.methodGetHandlePlayer.of(player).call(); - - for (final FaweChunk fc : list) { - final int dx = Math.abs(cx - fc.getChunkLoc().x); - final int dz = Math.abs(cz - fc.getChunkLoc().z); - if ((dx > view) || (dz > view)) { - continue; - } - final RefExecutor con = this.send.of(this.connection.of(entity).get()); - Object packet = packets.get(fc); - if (packet == null) { - final Object c = this.methodGetHandleChunk.of(fc.getChunk()).call(); - packet = this.MapChunk.create(c, true, 65535); - packets.put(fc, packet); - con.call(packet); - } else { - con.call(packet); - } - } + final RefExecutor con = this.send.of(this.connection.of(entity).get()); + final Object c = this.methodGetHandleChunk.of(fc.getChunk()).call(); + Object packet = this.MapChunk.create(c, false, 65535); + con.call(packet); } - final HashSet> chunks = new HashSet>(); - for (final FaweChunk fc : fcs) { - final Chunk chunk = fc.getChunk(); - chunk.unload(true, false); - chunk.load(); - final ChunkLoc loc = fc.getChunkLoc(); - chunk.getWorld().refreshChunk(loc.x, loc.z); - if (!this.fixLighting(fc, Settings.FIX_ALL_LIGHTING)) { - chunks.add(fc); - } - } - return chunks; } @Override @@ -424,6 +398,13 @@ public class BukkitQueue_1_8 extends BukkitQueue_0 { // Clear fs.clear(); + + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + sendChunk(fs); + } + }, 1); return true; } catch (final Exception e) { e.printStackTrace(); diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index c38f7c85..18e2a830 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ name: FastAsyncWorldEdit main: com.boydti.fawe.bukkit.FaweBukkit -version: 3.3.4 +version: 3.3.5 description: Fast Async WorldEdit plugin authors: [Empire92] loadbefore: [WorldEdit] diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index a4afc7c7..5947bea1 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -1,23 +1,13 @@ package com.boydti.fawe; -import java.io.File; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.MemoryPoolMXBean; -import java.lang.management.MemoryUsage; -import java.util.List; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.Notification; -import javax.management.NotificationEmitter; -import javax.management.NotificationListener; - import com.boydti.fawe.command.FixLighting; import com.boydti.fawe.command.Stream; import com.boydti.fawe.command.Wea; import com.boydti.fawe.command.WorldEditRegion; import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.regions.general.PlotSquaredFeature; import com.boydti.fawe.util.Lag; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MemUtil; @@ -42,6 +32,17 @@ import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityRemove; +import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import javax.management.InstanceAlreadyExistsException; +import javax.management.Notification; +import javax.management.NotificationEmitter; +import javax.management.NotificationListener; /**[ WorldEdit action] * | @@ -159,6 +160,9 @@ public class Fawe { public void run() { // worldedit WEManager.IMP.managers.addAll(Fawe.this.IMP.getMaskManagers()); + try { + WEManager.IMP.managers.add(new PlotSquaredFeature()); + } catch (Throwable e) {} Fawe.this.worldedit = WorldEdit.getInstance(); // Events Fawe.this.setupEvents(); @@ -258,6 +262,20 @@ public class Fawe { return this.thread; } + private ConcurrentHashMap players = new ConcurrentHashMap<>(); + + public void register(FawePlayer player) { + players.put(player.getName(), player); + } + + public void unregister(String name) { + players.remove(name); + } + + public FawePlayer getCachedPlayer(String name) { + return players.get(name); + } + /* * TODO FIXME * - Async packet sending diff --git a/core/src/main/java/com/boydti/fawe/IFawe.java b/core/src/main/java/com/boydti/fawe/IFawe.java index c8573357..36e32386 100644 --- a/core/src/main/java/com/boydti/fawe/IFawe.java +++ b/core/src/main/java/com/boydti/fawe/IFawe.java @@ -1,8 +1,5 @@ package com.boydti.fawe; -import java.io.File; -import java.util.Collection; - import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; @@ -10,6 +7,9 @@ import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.TaskManager; import com.sk89q.worldedit.EditSession; +import java.io.File; +import java.util.Collection; +import java.util.Set; public interface IFawe { public void debug(final String s); @@ -35,4 +35,6 @@ public interface IFawe { public Collection getMaskManagers(); public void startMetrics(); + + public Set getPlayers(); } diff --git a/core/src/main/java/com/boydti/fawe/config/BBC.java b/core/src/main/java/com/boydti/fawe/config/BBC.java index d69e92e4..c5131a81 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -1,17 +1,14 @@ package com.boydti.fawe.config; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.StringMan; import java.io.File; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Set; - -import org.bukkit.ChatColor; -import org.bukkit.configuration.file.YamlConfiguration; - -import com.boydti.fawe.Fawe; -import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.configuration.file.YamlConfiguration; public enum BBC { @@ -191,15 +188,6 @@ public enum BBC { return this.prefix; } - /** - * @return translated and color decoded - * - * @see org.bukkit.ChatColor#translateAlternateColorCodes(char, String) - */ - public String translated() { - return ChatColor.translateAlternateColorCodes('&', this.s()); - } - public String getCat() { return this.cat; } diff --git a/core/src/main/java/com/boydti/fawe/config/Settings.java b/core/src/main/java/com/boydti/fawe/config/Settings.java index 4a3536a8..cecf0f1d 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.bukkit.configuration.file.YamlConfiguration; +import com.boydti.fawe.configuration.file.YamlConfiguration; import com.sk89q.worldedit.LocalSession; @@ -26,6 +26,7 @@ public class Settings { public static boolean ENABLE_HARD_LIMIT = true; public static boolean STORE_HISTORY_ON_DISK = false; public static boolean COMPRESS_HISTORY = false; + public static boolean METRICS = true; public static void setup(final File file) { if (!file.exists()) { @@ -51,6 +52,7 @@ public class Settings { options.put("fix-all-lighting", FIX_ALL_LIGHTING); options.put("history.use-disk", STORE_HISTORY_ON_DISK); options.put("history.compress", COMPRESS_HISTORY); + options.put("metrics", METRICS); for (final Entry node : options.entrySet()) { if (!config.contains(node.getKey())) { @@ -67,6 +69,7 @@ public class Settings { REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask"); WE_BLACKLIST = config.getStringList("command-blacklist"); ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation"); + METRICS = config.getBoolean("metrics"); COMPRESS_HISTORY = config.getBoolean("history.compress"); if (STORE_HISTORY_ON_DISK = config.getBoolean("history.use-disk")) { LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; diff --git a/core/src/main/java/com/boydti/fawe/configuration/Configuration.java b/core/src/main/java/com/boydti/fawe/configuration/Configuration.java new file mode 100644 index 00000000..2b57cdfa --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/Configuration.java @@ -0,0 +1,86 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.ConfigurationOptions; +import com.boydti.fawe.configuration.ConfigurationSection; +import java.util.Map; + +/** + * Represents a source of configurable options and settings + */ +public interface Configuration extends ConfigurationSection { + /** + * Sets the default value of the given path as provided. + *

+ * If no source {@link com.boydti.fawe.configuration.Configuration} was provided as a default + * collection, then a new {@link com.boydti.fawe.configuration.MemoryConfiguration} will be created to + * hold the new default value. + *

+ * If value is null, the value will be removed from the default + * Configuration source. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + @Override void addDefault(final String path, final Object value); + + /** + * Sets the default values of the given paths as provided. + *

+ * If no source {@link com.boydti.fawe.configuration.Configuration} was provided as a default + * collection, then a new {@link com.boydti.fawe.configuration.MemoryConfiguration} will be created to + * hold the new default values. + * + * @param defaults A map of Path->Values to add to defaults. + * @throws IllegalArgumentException Thrown if defaults is null. + */ + void addDefaults(final Map defaults); + + /** + * Sets the default values of the given paths as provided. + *

+ * If no source {@link com.boydti.fawe.configuration.Configuration} was provided as a default + * collection, then a new {@link com.boydti.fawe.configuration.MemoryConfiguration} will be created to + * hold the new default value. + *

+ * This method will not hold a reference to the specified Configuration, + * nor will it automatically update if that Configuration ever changes. If + * you require this, you should set the default source with {@link + * #setDefaults(com.boydti.fawe.configuration.Configuration)}. + * + * @param defaults A configuration holding a list of defaults to copy. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + void addDefaults(final com.boydti.fawe.configuration.Configuration defaults); + + /** + * Sets the source of all default values for this {@link com.boydti.fawe.configuration.Configuration}. + *

+ * If a previous source was set, or previous default values were defined, + * then they will not be copied to the new source. + * + * @param defaults New source of default values for this configuration. + * @throws IllegalArgumentException Thrown if defaults is null or this. + */ + void setDefaults(final com.boydti.fawe.configuration.Configuration defaults); + + /** + * Gets the source {@link com.boydti.fawe.configuration.Configuration} for this configuration. + *

+ * If no configuration source was set, but default values were added, then + * a {@link com.boydti.fawe.configuration.MemoryConfiguration} will be returned. If no source was set + * and no defaults were set, then this method will return null. + * + * @return Configuration source for default values, or null if none exist. + */ + com.boydti.fawe.configuration.Configuration getDefaults(); + + /** + * Gets the {@link com.boydti.fawe.configuration.ConfigurationOptions} for this {@link com.boydti.fawe.configuration.Configuration}. + *

+ * All setters through this method are chainable. + * + * @return Options for this configuration + */ + ConfigurationOptions options(); +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/ConfigurationOptions.java b/core/src/main/java/com/boydti/fawe/configuration/ConfigurationOptions.java new file mode 100644 index 00000000..a5270836 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/ConfigurationOptions.java @@ -0,0 +1,92 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.Configuration; + +/** + * Various settings for controlling the input and output of a {@link + * com.boydti.fawe.configuration.Configuration} + */ +public class ConfigurationOptions { + private char pathSeparator = '.'; + private boolean copyDefaults = false; + private final Configuration configuration; + + protected ConfigurationOptions(final Configuration configuration) { + this.configuration = configuration; + } + + /** + * Returns the {@link com.boydti.fawe.configuration.Configuration} that this object is responsible for. + * + * @return Parent configuration + */ + public Configuration configuration() { + return configuration; + } + + /** + * Gets the char that will be used to separate {@link + * com.boydti.fawe.configuration.ConfigurationSection}s + *

+ * This value does not affect how the {@link com.boydti.fawe.configuration.Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @return Path separator + */ + public char pathSeparator() { + return pathSeparator; + } + + /** + * Sets the char that will be used to separate {@link + * com.boydti.fawe.configuration.ConfigurationSection}s + *

+ * This value does not affect how the {@link com.boydti.fawe.configuration.Configuration} is stored, + * only in how you access the data. The default value is '.'. + * + * @param value Path separator + * @return This object, for chaining + */ + public com.boydti.fawe.configuration.ConfigurationOptions pathSeparator(final char value) { + pathSeparator = value; + return this; + } + + /** + * Checks if the {@link com.boydti.fawe.configuration.Configuration} should copy values from its default + * {@link com.boydti.fawe.configuration.Configuration} directly. + *

+ * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link com.boydti.fawe.configuration.ConfigurationSection#contains(String)} will always + * return the same value as {@link + * com.boydti.fawe.configuration.ConfigurationSection#isSet(String)}. The default value is + * false. + * + * @return Whether or not defaults are directly copied + */ + public boolean copyDefaults() { + return copyDefaults; + } + + /** + * Sets if the {@link com.boydti.fawe.configuration.Configuration} should copy values from its default + * {@link com.boydti.fawe.configuration.Configuration} directly. + *

+ * If this is true, all values in the default Configuration will be + * directly copied, making it impossible to distinguish between values + * that were set and values that are provided by default. As a result, + * {@link com.boydti.fawe.configuration.ConfigurationSection#contains(String)} will always + * return the same value as {@link + * com.boydti.fawe.configuration.ConfigurationSection#isSet(String)}. The default value is + * false. + * + * @param value Whether or not defaults are directly copied + * @return This object, for chaining + */ + public com.boydti.fawe.configuration.ConfigurationOptions copyDefaults(final boolean value) { + copyDefaults = value; + return this; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/ConfigurationSection.java b/core/src/main/java/com/boydti/fawe/configuration/ConfigurationSection.java new file mode 100644 index 00000000..7b21e8ec --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/ConfigurationSection.java @@ -0,0 +1,646 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.Configuration; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents a section of a {@link com.boydti.fawe.configuration.Configuration} + */ +public interface ConfigurationSection { + + /** + * Gets a set containing all keys in this section. + *

+ * If deep is set to true, then this will contain all the keys within any + * child {@link com.boydti.fawe.configuration.ConfigurationSection}s (and their children, etc). These + * will be in a valid path notation for you to use. + *

+ * If deep is set to false, then this will contain only the keys of any + * direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Set of keys contained within this ConfigurationSection. + */ + Set getKeys(boolean deep); + + /** + * Gets a Map containing all keys and their values for this section. + *

+ * If deep is set to true, then this will contain all the keys and values + * within any child {@link com.boydti.fawe.configuration.ConfigurationSection}s (and their children, + * etc). These keys will be in a valid path notation for you to use. + *

+ * If deep is set to false, then this will contain only the keys and + * values of any direct children, and not their own children. + * + * @param deep Whether or not to get a deep list, as opposed to a shallow + * list. + * @return Map of keys and values of this section. + */ + Map getValues(boolean deep); + + /** + * Checks if this {@link com.boydti.fawe.configuration.ConfigurationSection} contains the given path. + *

+ * If the value for the requested path does not exist but a default value + * has been specified, this will return true. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, either via + * default or being set. + * @throws IllegalArgumentException Thrown when path is null. + */ + boolean contains(String path); + + /** + * Checks if this {@link com.boydti.fawe.configuration.ConfigurationSection} has a value set for the + * given path. + *

+ * If the value for the requested path does not exist but a default value + * has been specified, this will still return false. + * + * @param path Path to check for existence. + * @return True if this section contains the requested path, regardless of + * having a default. + * @throws IllegalArgumentException Thrown when path is null. + */ + boolean isSet(String path); + + /** + * Gets the path of this {@link com.boydti.fawe.configuration.ConfigurationSection} from its root {@link + * com.boydti.fawe.configuration.Configuration} + *

+ * For any {@link com.boydti.fawe.configuration.Configuration} themselves, this will return an empty + * string. + *

+ * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + *

+ * To retrieve the single name of this section, that is, the final part of + * the path returned by this method, you may use {@link #getName()}. + * + * @return Path of this section relative to its root + */ + String getCurrentPath(); + + /** + * Gets the name of this individual {@link com.boydti.fawe.configuration.ConfigurationSection}, in the + * path. + *

+ * This will always be the final part of {@link #getCurrentPath()}, unless + * the section is orphaned. + * + * @return Name of this section + */ + String getName(); + + /** + * Gets the root {@link com.boydti.fawe.configuration.Configuration} that contains this {@link + * com.boydti.fawe.configuration.ConfigurationSection} + *

+ * For any {@link com.boydti.fawe.configuration.Configuration} themselves, this will return its own + * object. + *

+ * If the section is no longer contained within its root for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Root configuration containing this section. + */ + Configuration getRoot(); + + /** + * Gets the parent {@link com.boydti.fawe.configuration.ConfigurationSection} that directly contains + * this {@link com.boydti.fawe.configuration.ConfigurationSection}. + *

+ * For any {@link com.boydti.fawe.configuration.Configuration} themselves, this will return null. + *

+ * If the section is no longer contained within its parent for any reason, + * such as being replaced with a different value, this may return null. + * + * @return Parent section containing this section. + */ + com.boydti.fawe.configuration.ConfigurationSection getParent(); + + /** + * Gets the requested Object by path. + *

+ * If the Object does not exist but a default value has been specified, + * this will return the default value. If the Object does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the Object to get. + * @return Requested Object. + */ + Object get(String path); + + /** + * Gets the requested Object by path, returning a default value if not + * found. + *

+ * If the Object does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the Object to get. + * @param def The default value to return if the path is not found. + * @return Requested Object. + */ + Object get(String path, Object def); + + /** + * Sets the specified path to the given value. + *

+ * If value is null, the entry will be removed. Any existing entry will be + * replaced, regardless of what the new value is. + *

+ * Some implementations may have limitations on what you may store. See + * their individual javadoc for details. No implementations should allow + * you to store {@link com.boydti.fawe.configuration.Configuration}s or {@link com.boydti.fawe.configuration.ConfigurationSection}s, + * please use {@link #createSection(String)} for that. + * + * @param path Path of the object to set. + * @param value New value to set the path to. + */ + void set(String path, Object value); + + /** + * Creates an empty {@link com.boydti.fawe.configuration.ConfigurationSection} at the specified path. + *

+ * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link com.boydti.fawe.configuration.ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @return Newly created section + */ + com.boydti.fawe.configuration.ConfigurationSection createSection(String path); + + /** + * Creates a {@link com.boydti.fawe.configuration.ConfigurationSection} at the specified path, with + * specified values. + *

+ * Any value that was previously set at this path will be overwritten. If + * the previous value was itself a {@link com.boydti.fawe.configuration.ConfigurationSection}, it will + * be orphaned. + * + * @param path Path to create the section at. + * @param map The values to used. + * @return Newly created section + */ + com.boydti.fawe.configuration.ConfigurationSection createSection(String path, Map map); + + // Primitives + + /** + * Gets the requested String by path. + *

+ * If the String does not exist but a default value has been specified, + * this will return the default value. If the String does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the String to get. + * @return Requested String. + */ + String getString(String path); + + /** + * Gets the requested String by path, returning a default value if not + * found. + *

+ * If the String does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the String to get. + * @param def The default value to return if the path is not found or is + * not a String. + * @return Requested String. + */ + String getString(String path, String def); + + /** + * Checks if the specified path is a String. + * + *

If the path exists but is not a String, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a String and return appropriately.

+ * + * @param path Path of the String to check. + * @return Whether or not the specified path is a String. + */ + boolean isString(String path); + + /** + * Gets the requested int by path. + * + *

If the int does not exist but a default value has been specified, this + * will return the default value. If the int does not exist and no default + * value was specified, this will return 0.

+ * + * @param path Path of the int to get. + * @return Requested int. + */ + int getInt(String path); + + /** + * Gets the requested int by path, returning a default value if not found. + * + *

If the int does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}.

+ * + * @param path Path of the int to get. + * @param def The default value to return if the path is not found or is + * not an int. + * @return Requested int. + */ + int getInt(String path, int def); + + /** + * Checks if the specified path is an int. + * + *

If the path exists but is not a int, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a int and return appropriately.

+ * + * @param path Path of the int to check. + * @return Whether or not the specified path is an int. + */ + boolean isInt(String path); + + /** + * Gets the requested boolean by path. + *

+ * If the boolean does not exist but a default value has been specified, + * this will return the default value. If the boolean does not exist and + * no default value was specified, this will return false. + * + * @param path Path of the boolean to get. + * @return Requested boolean. + */ + boolean getBoolean(String path); + + /** + * Gets the requested boolean by path, returning a default value if not + * found. + *

+ * If the boolean does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the boolean to get. + * @param def The default value to return if the path is not found or is + * not a boolean. + * @return Requested boolean. + */ + boolean getBoolean(String path, boolean def); + + /** + * Checks if the specified path is a boolean. + *

+ * If the path exists but is not a boolean, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a boolean and return appropriately. + * + * @param path Path of the boolean to check. + * @return Whether or not the specified path is a boolean. + */ + boolean isBoolean(String path); + + /** + * Gets the requested double by path. + *

+ * If the double does not exist but a default value has been specified, + * this will return the default value. If the double does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the double to get. + * @return Requested double. + */ + double getDouble(String path); + + /** + * Gets the requested double by path, returning a default value if not + * found. + *

+ * If the double does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the double to get. + * @param def The default value to return if the path is not found or is + * not a double. + * @return Requested double. + */ + double getDouble(String path, double def); + + /** + * Checks if the specified path is a double. + *

+ * If the path exists but is not a double, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a double and return appropriately. + * + * @param path Path of the double to check. + * @return Whether or not the specified path is a double. + */ + boolean isDouble(String path); + + /** + * Gets the requested long by path. + *

+ * If the long does not exist but a default value has been specified, this + * will return the default value. If the long does not exist and no + * default value was specified, this will return 0. + * + * @param path Path of the long to get. + * @return Requested long. + */ + long getLong(String path); + + /** + * Gets the requested long by path, returning a default value if not + * found. + *

+ * If the long does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the long to get. + * @param def The default value to return if the path is not found or is + * not a long. + * @return Requested long. + */ + long getLong(String path, long def); + + /** + * Checks if the specified path is a long. + *

+ * If the path exists but is not a long, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a long and return appropriately. + * + * @param path Path of the long to check. + * @return Whether or not the specified path is a long. + */ + boolean isLong(String path); + + // Java + + /** + * Gets the requested List by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return null. + * + * @param path Path of the List to get. + * @return Requested List. + */ + List getList(String path); + + /** + * Gets the requested List by path, returning a default value if not + * found. + *

+ * If the List does not exist then the specified default value will + * returned regardless of if a default has been identified in the root + * {@link com.boydti.fawe.configuration.Configuration}. + * + * @param path Path of the List to get. + * @param def The default value to return if the path is not found or is + * not a List. + * @return Requested List. + */ + List getList(String path, List def); + + /** + * Checks if the specified path is a List. + *

+ * If the path exists but is not a List, this will return false. If the + * path does not exist, this will return false. If the path does not exist + * but a default value has been specified, this will check if that default + * value is a List and return appropriately. + * + * @param path Path of the List to check. + * @return Whether or not the specified path is a List. + */ + boolean isList(String path); + + /** + * Gets the requested List of String by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a String if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of String. + */ + List getStringList(String path); + + /** + * Gets the requested List of Integer by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Integer if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Integer. + */ + List getIntegerList(String path); + + /** + * Gets the requested List of Boolean by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Boolean if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Boolean. + */ + List getBooleanList(String path); + + /** + * Gets the requested List of Double by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Double if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Double. + */ + List getDoubleList(String path); + + /** + * Gets the requested List of Float by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Float if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Float. + */ + List getFloatList(String path); + + /** + * Gets the requested List of Long by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Long if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Long. + */ + List getLongList(String path); + + /** + * Gets the requested List of Byte by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Byte if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Byte. + */ + List getByteList(String path); + + /** + * Gets the requested List of Character by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Character if + * possible, but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Character. + */ + List getCharacterList(String path); + + /** + * Gets the requested List of Short by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Short if possible, + * but may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Short. + */ + List getShortList(String path); + + /** + * Gets the requested List of Maps by path. + *

+ * If the List does not exist but a default value has been specified, this + * will return the default value. If the List does not exist and no + * default value was specified, this will return an empty List. + *

+ * This method will attempt to cast any values into a Map if possible, but + * may miss any values out if they are not compatible. + * + * @param path Path of the List to get. + * @return Requested List of Maps. + */ + List> getMapList(String path); + + /** + * Gets the requested ConfigurationSection by path. + *

+ * If the ConfigurationSection does not exist but a default value has been + * specified, this will return the default value. If the + * ConfigurationSection does not exist and no default value was specified, + * this will return null. + * + * @param path Path of the ConfigurationSection to get. + * @return Requested ConfigurationSection. + */ + com.boydti.fawe.configuration.ConfigurationSection getConfigurationSection(String path); + + /** + * Checks if the specified path is a ConfigurationSection. + *

+ * If the path exists but is not a ConfigurationSection, this will return + * false. If the path does not exist, this will return false. If the path + * does not exist but a default value has been specified, this will check + * if that default value is a ConfigurationSection and return + * appropriately. + * + * @param path Path of the ConfigurationSection to check. + * @return Whether or not the specified path is a ConfigurationSection. + */ + boolean isConfigurationSection(String path); + + /** + * Gets the equivalent {@link com.boydti.fawe.configuration.ConfigurationSection} from the default + * {@link com.boydti.fawe.configuration.Configuration} defined in {@link #getRoot()}. + *

+ * If the root contains no defaults, or the defaults doesn't contain a + * value for this path, or the value at this path is not a {@link + * com.boydti.fawe.configuration.ConfigurationSection} then this will return null. + * + * @return Equivalent section in root configuration + */ + com.boydti.fawe.configuration.ConfigurationSection getDefaultSection(); + + /** + * Sets the default value in the root at the given path as provided. + *

+ * If no source {@link com.boydti.fawe.configuration.Configuration} was provided as a default + * collection, then a new {@link com.boydti.fawe.configuration.MemoryConfiguration} will be created to + * hold the new default value. + *

+ * If value is null, the value will be removed from the default + * Configuration source. + *

+ * If the value as returned by {@link #getDefaultSection()} is null, then + * this will create a new section at the path, replacing anything that may + * have existed there previously. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + void addDefault(String path, Object value); +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/InvalidConfigurationException.java b/core/src/main/java/com/boydti/fawe/configuration/InvalidConfigurationException.java new file mode 100644 index 00000000..aaf5dd1a --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/InvalidConfigurationException.java @@ -0,0 +1,45 @@ +package com.boydti.fawe.configuration; + +/** + * Exception thrown when attempting to load an invalid {@link com.boydti.fawe.configuration.Configuration} + */ +@SuppressWarnings("serial") +public class InvalidConfigurationException extends Exception { + + /** + * Creates a new instance of InvalidConfigurationException without a + * message or cause. + */ + public InvalidConfigurationException() {} + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message. + * + * @param msg The details of the exception. + */ + public InvalidConfigurationException(final String msg) { + super(msg); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified cause. + * + * @param cause The cause of the exception. + */ + public InvalidConfigurationException(final Throwable cause) { + super(cause); + } + + /** + * Constructs an instance of InvalidConfigurationException with the + * specified message and cause. + * + * @param cause The cause of the exception. + * @param msg The details of the exception. + */ + public InvalidConfigurationException(final String msg, final Throwable cause) { + super(msg, cause); + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/MemoryConfiguration.java b/core/src/main/java/com/boydti/fawe/configuration/MemoryConfiguration.java new file mode 100644 index 00000000..d4360ea2 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/MemoryConfiguration.java @@ -0,0 +1,93 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.Configuration; +import com.boydti.fawe.configuration.ConfigurationSection; +import com.boydti.fawe.configuration.MemoryConfigurationOptions; +import com.boydti.fawe.configuration.MemorySection; +import java.util.Map; + +/** + * This is a {@link com.boydti.fawe.configuration.Configuration} implementation that does not save or load + * from any source, and stores all values in memory only. + * This is useful for temporary Configurations for providing defaults. + */ +public class MemoryConfiguration extends MemorySection implements Configuration { + protected Configuration defaults; + protected MemoryConfigurationOptions options; + + /** + * Creates an empty {@link com.boydti.fawe.configuration.MemoryConfiguration} with no default values. + */ + public MemoryConfiguration() {} + + /** + * Creates an empty {@link com.boydti.fawe.configuration.MemoryConfiguration} using the specified {@link + * com.boydti.fawe.configuration.Configuration} as a source for all default values. + * + * @param defaults Default value provider + * @throws IllegalArgumentException Thrown if defaults is null + */ + public MemoryConfiguration(final Configuration defaults) { + this.defaults = defaults; + } + + @Override + public void addDefault(final String path, final Object value) { + if (path == null) { + throw new NullPointerException("Path may not be null"); + } + if (defaults == null) { + defaults = new com.boydti.fawe.configuration.MemoryConfiguration(); + } + + defaults.set(path, value); + } + + @Override + public void addDefaults(final Map defaults) { + if (defaults == null) { + throw new NullPointerException("Defaults may not be null"); + } + + for (final Map.Entry entry : defaults.entrySet()) { + addDefault(entry.getKey(), entry.getValue()); + } + } + + @Override + public void addDefaults(final Configuration defaults) { + if (defaults == null) { + throw new NullPointerException("Defaults may not be null"); + } + + addDefaults(defaults.getValues(true)); + } + + @Override + public void setDefaults(final Configuration defaults) { + if (defaults == null) { + throw new NullPointerException("Defaults may not be null"); + } + + this.defaults = defaults; + } + + @Override + public Configuration getDefaults() { + return defaults; + } + + @Override + public ConfigurationSection getParent() { + return null; + } + + @Override + public MemoryConfigurationOptions options() { + if (options == null) { + options = new MemoryConfigurationOptions(this); + } + + return options; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/MemoryConfigurationOptions.java b/core/src/main/java/com/boydti/fawe/configuration/MemoryConfigurationOptions.java new file mode 100644 index 00000000..3d08a362 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/MemoryConfigurationOptions.java @@ -0,0 +1,30 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.ConfigurationOptions; + +/** + * Various settings for controlling the input and output of a {@link + * com.boydti.fawe.configuration.MemoryConfiguration} + */ +public class MemoryConfigurationOptions extends ConfigurationOptions { + protected MemoryConfigurationOptions(final MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public MemoryConfiguration configuration() { + return (MemoryConfiguration) super.configuration(); + } + + @Override + public com.boydti.fawe.configuration.MemoryConfigurationOptions copyDefaults(final boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public com.boydti.fawe.configuration.MemoryConfigurationOptions pathSeparator(final char value) { + super.pathSeparator(value); + return this; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/MemorySection.java b/core/src/main/java/com/boydti/fawe/configuration/MemorySection.java new file mode 100644 index 00000000..a6f7a4ee --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/MemorySection.java @@ -0,0 +1,847 @@ +package com.boydti.fawe.configuration; + +import com.boydti.fawe.configuration.ConfigurationSection; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A type of {@link com.boydti.fawe.configuration.ConfigurationSection} that is stored in memory. + */ +public class MemorySection implements com.boydti.fawe.configuration.ConfigurationSection { + + protected final Map map = new LinkedHashMap<>(); + private final Configuration root; + private final com.boydti.fawe.configuration.ConfigurationSection parent; + private final String path; + private final String fullPath; + + /** + * Creates an empty MemorySection for use as a root {@link com.boydti.fawe.configuration.Configuration} + * section. + *

+ * Note that calling this without being yourself a {@link com.boydti.fawe.configuration.Configuration} + * will throw an exception! + * + * @throws IllegalStateException Thrown if this is not a {@link + * com.boydti.fawe.configuration.Configuration} root. + */ + protected MemorySection() { + if (!(this instanceof Configuration)) { + throw new IllegalStateException("Cannot construct a root MemorySection when not a Configuration"); + } + + this.path = ""; + this.fullPath = ""; + this.parent = null; + this.root = (Configuration) this; + } + + /** + * Creates an empty MemorySection with the specified parent and path. + * + * @param parent Parent section that contains this own section. + * @param path Path that you may access this section from via the root + * {@link com.boydti.fawe.configuration.Configuration}. + * @throws IllegalArgumentException Thrown is parent or path is null, or + * if parent contains no root Configuration. + */ + protected MemorySection(com.boydti.fawe.configuration.ConfigurationSection parent, String path) { + if (parent == null) { + throw new NullPointerException("Parent may not be null"); + } + if (path == null) { + throw new NullPointerException("Path may not be null"); + } + + this.path = path; + this.parent = parent; + this.root = parent.getRoot(); + + if (this.root == null) { + throw new NullPointerException("Path may not be orphaned"); + } + + this.fullPath = createPath(parent, path); + } + + public static double toDouble(Object obj, double def) { + if (obj instanceof Number) { + return ((Number) obj).doubleValue(); + } + if (obj instanceof String) { + try { + return Double.parseDouble((String) obj); + } catch (NumberFormatException ignored) { + } + } else if (obj instanceof List) { + List val = (List) obj; + if (!val.isEmpty()) { + return toDouble(val.get(0), def); + } + } + return def; + } + + public static int toInt(Object obj, int def) { + if (obj instanceof Number) { + return ((Number) obj).intValue(); + } + if (obj instanceof String) { + try { + return Integer.parseInt((String) obj); + } catch (NumberFormatException ignored) { + } + } else if (obj instanceof List) { + List val = (List) obj; + if (!val.isEmpty()) { + return toInt(val.get(0), def); + } + } + return def; + } + + public static long toLong(Object obj, long def) { + if (obj instanceof Number) { + return ((Number) obj).longValue(); + } + if (obj instanceof String) { + try { + return Long.parseLong((String) obj); + } catch (NumberFormatException ignored) { + } + } else if (obj instanceof List) { + List val = (List) obj; + if (!val.isEmpty()) { + return toLong(val.get(0), def); + } + } + return def; + } + + /** + * Creates a full path to the given {@link com.boydti.fawe.configuration.ConfigurationSection} from its + * root {@link com.boydti.fawe.configuration.Configuration}. + *

+ * You may use this method for any given {@link com.boydti.fawe.configuration.ConfigurationSection}, not + * only {@link com.boydti.fawe.configuration.MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @return Full path of the section from its root. + */ + public static String createPath(com.boydti.fawe.configuration.ConfigurationSection section, String key) { + return createPath(section, key, (section == null) ? null : section.getRoot()); + } + + /** + * Creates a relative path to the given {@link com.boydti.fawe.configuration.ConfigurationSection} from + * the given relative section. + *

+ * You may use this method for any given {@link com.boydti.fawe.configuration.ConfigurationSection}, not + * only {@link com.boydti.fawe.configuration.MemorySection}. + * + * @param section Section to create a path for. + * @param key Name of the specified section. + * @param relativeTo Section to create the path relative to. + * @return Full path of the section from its root. + */ + public static String createPath(com.boydti.fawe.configuration.ConfigurationSection section, String key, com.boydti.fawe.configuration.ConfigurationSection relativeTo) { + if (section == null) { + throw new NullPointerException("Cannot create path without a section"); + } + Configuration root = section.getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create path without a root"); + } + char separator = root.options().pathSeparator(); + + StringBuilder builder = new StringBuilder(); + for (com.boydti.fawe.configuration.ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) { + if (builder.length() > 0) { + builder.insert(0, separator); + } + + builder.insert(0, parent.getName()); + } + + if ((key != null) && !key.isEmpty()) { + if (builder.length() > 0) { + builder.append(separator); + } + + builder.append(key); + } + + return builder.toString(); + } + + @Override + public Set getKeys(boolean deep) { + Set result = new LinkedHashSet<>(); + + Configuration root = getRoot(); + if ((root != null) && root.options().copyDefaults()) { + com.boydti.fawe.configuration.ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.addAll(defaults.getKeys(deep)); + } + } + + mapChildrenKeys(result, this, deep); + + return result; + } + + @Override + public Map getValues(boolean deep) { + Map result = new LinkedHashMap<>(); + + Configuration root = getRoot(); + if ((root != null) && root.options().copyDefaults()) { + com.boydti.fawe.configuration.ConfigurationSection defaults = getDefaultSection(); + + if (defaults != null) { + result.putAll(defaults.getValues(deep)); + } + } + + mapChildrenValues(result, this, deep); + + return result; + } + + @Override + public boolean contains(String path) { + return get(path) != null; + } + + @Override + public boolean isSet(String path) { + Configuration root = getRoot(); + if (root == null) { + return false; + } + if (root.options().copyDefaults()) { + return contains(path); + } + return get(path, null) != null; + } + + @Override + public String getCurrentPath() { + return this.fullPath; + } + + @Override + public String getName() { + return this.path; + } + + @Override + public Configuration getRoot() { + return this.root; + } + + @Override + public com.boydti.fawe.configuration.ConfigurationSection getParent() { + return this.parent; + } + + @Override + public void addDefault(String path, Object value) { + if (path == null) { + throw new NullPointerException("Path cannot be null"); + } + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot add default without root"); + } + if (root == this) { + throw new UnsupportedOperationException("Unsupported addDefault(String, Object) implementation"); + } + root.addDefault(createPath(this, path), value); + } + + @Override + public com.boydti.fawe.configuration.ConfigurationSection getDefaultSection() { + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + + if (defaults != null) { + if (defaults.isConfigurationSection(getCurrentPath())) { + return defaults.getConfigurationSection(getCurrentPath()); + } + } + + return null; + } + + @Override + public void set(String path, Object value) { + if (path == null) { + throw new NullPointerException("Cannot set to an empty path"); + } + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot use section without a root"); + } + + char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + com.boydti.fawe.configuration.ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + com.boydti.fawe.configuration.ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + if (value == null) { + this.map.remove(key); + } else { + this.map.put(key, value); + } + } else { + section.set(key, value); + } + } + + @Override + public Object get(String path) { + return get(path, getDefault(path)); + } + + @Override + public Object get(String path, Object def) { + if (path == null) { + throw new NullPointerException("Path cannot be null"); + } + + if (path.isEmpty()) { + return this; + } + + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot access section without a root"); + } + + char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1; + int i2; + com.boydti.fawe.configuration.ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + section = section.getConfigurationSection(path.substring(i2, i1)); + if (section == null) { + return def; + } + } + + String key = path.substring(i2); + if (section == this) { + Object result = this.map.get(key); + if (result == null) { + return def; + } else { + return result; + } + } + return section.get(key, def); + } + + @Override + public com.boydti.fawe.configuration.ConfigurationSection createSection(String path) { + if (path == null) { + throw new NullPointerException("Cannot create section at empty path"); + } + Configuration root = getRoot(); + if (root == null) { + throw new IllegalStateException("Cannot create section without a root"); + } + + char separator = root.options().pathSeparator(); + // i1 is the leading (higher) index + // i2 is the trailing (lower) index + int i1 = -1, i2; + com.boydti.fawe.configuration.ConfigurationSection section = this; + while ((i1 = path.indexOf(separator, i2 = i1 + 1)) != -1) { + String node = path.substring(i2, i1); + com.boydti.fawe.configuration.ConfigurationSection subSection = section.getConfigurationSection(node); + if (subSection == null) { + section = section.createSection(node); + } else { + section = subSection; + } + } + + String key = path.substring(i2); + if (section == this) { + com.boydti.fawe.configuration.ConfigurationSection result = new com.boydti.fawe.configuration.MemorySection(this, key); + this.map.put(key, result); + return result; + } + return section.createSection(key); + } + + @Override + public com.boydti.fawe.configuration.ConfigurationSection createSection(String path, Map map) { + com.boydti.fawe.configuration.ConfigurationSection section = createSection(path); + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof Map) { + section.createSection(entry.getKey().toString(), (Map) entry.getValue()); + } else { + section.set(entry.getKey().toString(), entry.getValue()); + } + } + + return section; + } + + // Primitives + @Override + public String getString(String path) { + Object def = getDefault(path); + return getString(path, def != null ? def.toString() : null); + } + + @Override + public String getString(String path, String def) { + Object val = get(path, def); + if (val != null) { + return val.toString(); + } else { + return def; + } + } + + @Override + public boolean isString(String path) { + Object val = get(path); + return val instanceof String; + } + + @Override + public int getInt(String path) { + Object def = getDefault(path); + return getInt(path, toInt(def, 0)); + } + + @Override + public int getInt(String path, int def) { + Object val = get(path, def); + return toInt(val, def); + } + + @Override + public boolean isInt(String path) { + Object val = get(path); + return val instanceof Integer; + } + + @Override + public boolean getBoolean(String path) { + Object def = getDefault(path); + if (def instanceof Boolean) { + return getBoolean(path, (Boolean) def); + } else { + return getBoolean(path, false); + } + } + + @Override + public boolean getBoolean(String path, boolean def) { + Object val = get(path, def); + if (val instanceof Boolean) { + return (Boolean) val; + } else { + return def; + } + } + + @Override + public boolean isBoolean(String path) { + Object val = get(path); + return val instanceof Boolean; + } + + @Override + public double getDouble(String path) { + Object def = getDefault(path); + return getDouble(path, toDouble(def, 0)); + } + + @Override + public double getDouble(String path, double def) { + Object val = get(path, def); + return toDouble(val, def); + } + + @Override + public boolean isDouble(String path) { + Object val = get(path); + return val instanceof Double; + } + + @Override + public long getLong(String path) { + Object def = getDefault(path); + return getLong(path, toLong(def, 0)); + } + + @Override + public long getLong(String path, long def) { + Object val = get(path, def); + return toLong(val, def); + } + + @Override + public boolean isLong(String path) { + Object val = get(path); + return val instanceof Long; + } + + // Java + @Override + public List getList(String path) { + Object def = getDefault(path); + return getList(path, def instanceof List ? (List) def : null); + } + + @Override + public List getList(String path, List def) { + Object val = get(path, def); + return (List) ((val instanceof List) ? val : def); + } + + @Override + public boolean isList(String path) { + Object val = get(path); + return val instanceof List; + } + + @Override + public List getStringList(String path) { + final List list = getList(path); + + if (list == null) { + return new ArrayList<>(0); + } + + final List result = new ArrayList<>(); + + for (final Object object : list) { + if ((object instanceof String) || (isPrimitiveWrapper(object))) { + result.add(String.valueOf(object)); + } + } + + return result; + } + + @Override + public List getIntegerList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Integer) { + result.add((Integer) object); + } else if (object instanceof String) { + try { + result.add(Integer.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((int) (Character) object); + } else if (object instanceof Number) { + result.add(((Number) object).intValue()); + } + } + + return result; + } + + @Override + public List getBooleanList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Boolean) { + result.add((Boolean) object); + } else if (object instanceof String) { + if (Boolean.TRUE.toString().equals(object)) { + result.add(true); + } else if (Boolean.FALSE.toString().equals(object)) { + result.add(false); + } + } + } + + return result; + } + + @Override + public List getDoubleList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Double) { + result.add((Double) object); + } else if (object instanceof String) { + try { + result.add(Double.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((double) (Character) object); + } else if (object instanceof Number) { + result.add(((Number) object).doubleValue()); + } + } + + return result; + } + + @Override + public List getFloatList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Float) { + result.add((Float) object); + } else if (object instanceof String) { + try { + result.add(Float.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((float) (Character) object); + } else if (object instanceof Number) { + result.add(((Number) object).floatValue()); + } + } + + return result; + } + + @Override + public List getLongList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Long) { + result.add((Long) object); + } else if (object instanceof String) { + try { + result.add(Long.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((long) (Character) object); + } else if (object instanceof Number) { + result.add(((Number) object).longValue()); + } + } + + return result; + } + + @Override + public List getByteList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Byte) { + result.add((Byte) object); + } else if (object instanceof String) { + try { + result.add(Byte.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((byte) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).byteValue()); + } + } + + return result; + } + + @Override + public List getCharacterList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Character) { + result.add((Character) object); + } else if (object instanceof String) { + String str = (String) object; + + if (str.length() == 1) { + result.add(str.charAt(0)); + } + } else if (object instanceof Number) { + result.add((char) ((Number) object).intValue()); + } + } + + return result; + } + + @Override + public List getShortList(String path) { + List list = getList(path); + + List result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Short) { + result.add((Short) object); + } else if (object instanceof String) { + try { + result.add(Short.valueOf((String) object)); + } catch (NumberFormatException ignored) { + } + } else if (object instanceof Character) { + result.add((short) ((Character) object).charValue()); + } else if (object instanceof Number) { + result.add(((Number) object).shortValue()); + } + } + + return result; + } + + @Override + public List> getMapList(String path) { + List list = getList(path); + List> result = new ArrayList<>(); + + for (Object object : list) { + if (object instanceof Map) { + result.add((Map) object); + } + } + + return result; + } + + @Override + public com.boydti.fawe.configuration.ConfigurationSection getConfigurationSection(String path) { + Object val = get(path, null); + if (val != null) { + return (val instanceof com.boydti.fawe.configuration.ConfigurationSection) ? (com.boydti.fawe.configuration.ConfigurationSection) val : null; + } + + val = get(path, getDefault(path)); + return (val instanceof com.boydti.fawe.configuration.ConfigurationSection) ? createSection(path) : null; + } + + @Override + public boolean isConfigurationSection(String path) { + Object val = get(path); + return val instanceof com.boydti.fawe.configuration.ConfigurationSection; + } + + protected boolean isPrimitiveWrapper(Object input) { + return (input instanceof Integer) + || (input instanceof Boolean) + || (input instanceof Character) + || (input instanceof Byte) + || (input instanceof Short) + || (input instanceof Double) + || (input instanceof Long) + || (input instanceof Float); + } + + protected Object getDefault(String path) { + if (path == null) { + throw new NullPointerException("Path may not be null"); + } + + Configuration root = getRoot(); + Configuration defaults = root == null ? null : root.getDefaults(); + return (defaults == null) ? null : defaults.get(createPath(this, path)); + } + + protected void mapChildrenKeys(Set output, com.boydti.fawe.configuration.ConfigurationSection section, boolean deep) { + if (section instanceof com.boydti.fawe.configuration.MemorySection) { + com.boydti.fawe.configuration.MemorySection sec = (com.boydti.fawe.configuration.MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.add(createPath(section, entry.getKey(), this)); + + if (deep && (entry.getValue() instanceof com.boydti.fawe.configuration.ConfigurationSection)) { + com.boydti.fawe.configuration.ConfigurationSection subsection = (com.boydti.fawe.configuration.ConfigurationSection) entry.getValue(); + mapChildrenKeys(output, subsection, deep); + } + } + } else { + Set keys = section.getKeys(deep); + + for (String key : keys) { + output.add(createPath(section, key, this)); + } + } + } + + protected void mapChildrenValues(Map output, com.boydti.fawe.configuration.ConfigurationSection section, boolean deep) { + if (section instanceof com.boydti.fawe.configuration.MemorySection) { + com.boydti.fawe.configuration.MemorySection sec = (com.boydti.fawe.configuration.MemorySection) section; + + for (Map.Entry entry : sec.map.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + + if (entry.getValue() instanceof com.boydti.fawe.configuration.ConfigurationSection) { + if (deep) { + mapChildrenValues(output, (ConfigurationSection) entry.getValue(), deep); + } + } + } + } else { + Map values = section.getValues(deep); + + for (Map.Entry entry : values.entrySet()) { + output.put(createPath(section, entry.getKey(), this), entry.getValue()); + } + } + } + + @Override + public String toString() { + Configuration root = getRoot(); + return getClass().getSimpleName() + "[path='" + getCurrentPath() + "', root='" + (root == null ? null : root.getClass().getSimpleName()) + + "']"; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/FileConfiguration.java b/core/src/main/java/com/boydti/fawe/configuration/file/FileConfiguration.java new file mode 100644 index 00000000..4c0ad7cf --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/FileConfiguration.java @@ -0,0 +1,219 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.Configuration; +import com.boydti.fawe.configuration.InvalidConfigurationException; +import com.boydti.fawe.configuration.MemoryConfiguration; +import com.boydti.fawe.configuration.file.FileConfigurationOptions; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; + +/** + * This is a base class for all File based implementations of {@link + * com.boydti.fawe.configuration.Configuration} + */ +public abstract class FileConfiguration extends MemoryConfiguration { + + /** + * Creates an empty {@link com.boydti.fawe.configuration.file.FileConfiguration} with no default values. + */ + public FileConfiguration() { + } + + /** + * Creates an empty {@link com.boydti.fawe.configuration.file.FileConfiguration} using the specified {@link + * com.boydti.fawe.configuration.Configuration} as a source for all default values. + * + * @param defaults Default value provider + */ + public FileConfiguration(Configuration defaults) { + super(defaults); + } + + /** + * Saves this {@link com.boydti.fawe.configuration.file.FileConfiguration} to the specified location. + *

+ * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

+ * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws java.io.IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(File file) throws IOException { + if (file == null) { + throw new NullPointerException("File cannot be null"); + } + file.getParentFile().mkdirs(); + + String data = saveToString(); + + try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { + writer.write(data); + } + } + + /** + * Saves this {@link com.boydti.fawe.configuration.file.FileConfiguration} to the specified location. + *

+ * If the file does not exist, it will be created. If already exists, it + * will be overwritten. If it cannot be overwritten or created, an + * exception will be thrown. + *

+ * This method will save using the system default encoding, or possibly + * using UTF8. + * + * @param file File to save to. + * @throws java.io.IOException Thrown when the given file cannot be written to for + * any reason. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void save(String file) throws IOException { + if (file == null) { + throw new NullPointerException("File cannot be null"); + } + + save(new File(file)); + } + + /** + * Saves this {@link com.boydti.fawe.configuration.file.FileConfiguration} to a string, and returns it. + * + * @return String containing this configuration. + */ + public abstract String saveToString(); + + /** + * Loads this {@link com.boydti.fawe.configuration.file.FileConfiguration} from the specified location. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

+ * If the file cannot be loaded for any reason, an exception will be + * thrown. + *

+ * + * @param file File to load from. + * @throws java.io.FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws java.io.IOException Thrown when the given file cannot be read. + * @throws com.boydti.fawe.configuration.InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(File file) throws IOException, InvalidConfigurationException { + if (file == null) { + throw new NullPointerException("File cannot be null"); + } + + FileInputStream stream = new FileInputStream(file); + + load(new InputStreamReader(stream, StandardCharsets.UTF_8)); + } + + /** + * Loads this {@link com.boydti.fawe.configuration.file.FileConfiguration} from the specified reader. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given stream. + * + * @param reader the reader to load from + * @throws java.io.IOException thrown when underlying reader throws an IOException + * @throws com.boydti.fawe.configuration.InvalidConfigurationException thrown when the reader does not + * represent a valid Configuration + * @throws IllegalArgumentException thrown when reader is null + */ + public void load(Reader reader) throws IOException, InvalidConfigurationException { + + StringBuilder builder = new StringBuilder(); + + try (BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader)) { + String line; + + while ((line = input.readLine()) != null) { + builder.append(line); + builder.append('\n'); + } + } + + loadFromString(builder.toString()); + } + + /** + * Loads this {@link com.boydti.fawe.configuration.file.FileConfiguration} from the specified location. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given file. + *

+ * If the file cannot be loaded for any reason, an exception will be + * thrown. + * + * @param file File to load from. + * @throws java.io.FileNotFoundException Thrown when the given file cannot be + * opened. + * @throws java.io.IOException Thrown when the given file cannot be read. + * @throws com.boydti.fawe.configuration.InvalidConfigurationException Thrown when the given file is not + * a valid Configuration. + * @throws IllegalArgumentException Thrown when file is null. + */ + public void load(String file) throws IOException, InvalidConfigurationException { + if (file == null) { + throw new NullPointerException("File cannot be null"); + } + + load(new File(file)); + } + + /** + * Loads this {@link com.boydti.fawe.configuration.file.FileConfiguration} from the specified string, as + * opposed to from file. + *

+ * All the values contained within this configuration will be removed, + * leaving only settings and defaults, and the new values will be loaded + * from the given string. + *

+ * If the string is invalid in any way, an exception will be thrown. + * + * @param contents Contents of a Configuration to load. + * @throws com.boydti.fawe.configuration.InvalidConfigurationException Thrown if the specified string is + * invalid. + * @throws IllegalArgumentException Thrown if contents is null. + */ + public abstract void loadFromString(String contents) throws InvalidConfigurationException; + + /** + * Compiles the header for this {@link com.boydti.fawe.configuration.file.FileConfiguration} and returns the + * result. + *

+ * This will use the header from {@link #options()} -> {@link + * com.boydti.fawe.configuration.file.FileConfigurationOptions#header()}, respecting the rules of {@link + * com.boydti.fawe.configuration.file.FileConfigurationOptions#copyHeader()} if set. + * + * @return Compiled header + */ + protected abstract String buildHeader(); + + @Override + public FileConfigurationOptions options() { + if (this.options == null) { + this.options = new FileConfigurationOptions(this); + } + + return (FileConfigurationOptions) this.options; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/FileConfigurationOptions.java b/core/src/main/java/com/boydti/fawe/configuration/file/FileConfigurationOptions.java new file mode 100644 index 00000000..58eaa1b6 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/FileConfigurationOptions.java @@ -0,0 +1,119 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.MemoryConfiguration; +import com.boydti.fawe.configuration.MemoryConfigurationOptions; + +/** + * Various settings for controlling the input and output of a {@link + * com.boydti.fawe.configuration.file.FileConfiguration} + */ +public class FileConfigurationOptions extends MemoryConfigurationOptions { + private String header = null; + private boolean copyHeader = true; + + protected FileConfigurationOptions(final MemoryConfiguration configuration) { + super(configuration); + } + + @Override + public com.boydti.fawe.configuration.file.FileConfiguration configuration() { + return (com.boydti.fawe.configuration.file.FileConfiguration) super.configuration(); + } + + @Override + public com.boydti.fawe.configuration.file.FileConfigurationOptions copyDefaults(final boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public com.boydti.fawe.configuration.file.FileConfigurationOptions pathSeparator(final char value) { + super.pathSeparator(value); + return this; + } + + /** + * Gets the header that will be applied to the top of the saved output. + *

+ * This header will be commented out and applied directly at the top of + * the generated output of the {@link com.boydti.fawe.configuration.file.FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

+ * Null is a valid value which will indicate that no header is to be + * applied. The default value is null. + * + * @return Header + */ + public String header() { + return header; + } + + /** + * Sets the header that will be applied to the top of the saved output. + *

+ * This header will be commented out and applied directly at the top of + * the generated output of the {@link com.boydti.fawe.configuration.file.FileConfiguration}. It is not + * required to include a newline at the end of the header as it will + * automatically be applied, but you may include one if you wish for extra + * spacing. + *

+ * Null is a valid value which will indicate that no header is to be + * applied. + * + * @param value New header + * @return This object, for chaining + */ + public com.boydti.fawe.configuration.file.FileConfigurationOptions header(final String value) { + header = value; + return this; + } + + /** + * Gets whether or not the header should be copied from a default source. + *

+ * If this is true, if a default {@link com.boydti.fawe.configuration.file.FileConfiguration} is passed to + * {@link + * com.boydti.fawe.configuration.file.FileConfiguration#setDefaults(com.boydti.fawe.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

+ * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

+ * Defaults to true. + * + * @return Whether or not to copy the header + */ + public boolean copyHeader() { + return copyHeader; + } + + /** + * Sets whether or not the header should be copied from a default source. + *

+ * If this is true, if a default {@link com.boydti.fawe.configuration.file.FileConfiguration} is passed to + * {@link + * com.boydti.fawe.configuration.file.FileConfiguration#setDefaults(com.boydti.fawe.configuration.Configuration)} + * then upon saving it will use the header from that config, instead of + * the one provided here. + *

+ * If no default is set on the configuration, or the default is not of + * type FileConfiguration, or that config has no header ({@link #header()} + * returns null) then the header specified in this configuration will be + * used. + *

+ * Defaults to true. + * + * @param value Whether or not to copy the header + * @return This object, for chaining + */ + public com.boydti.fawe.configuration.file.FileConfigurationOptions copyHeader(final boolean value) { + copyHeader = value; + + return this; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfiguration.java b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfiguration.java new file mode 100644 index 00000000..a3b6647d --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfiguration.java @@ -0,0 +1,229 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.Configuration; +import com.boydti.fawe.configuration.ConfigurationSection; +import com.boydti.fawe.configuration.InvalidConfigurationException; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Map; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; + +/** + * An implementation of {@link com.boydti.fawe.configuration.Configuration} which saves all files in Yaml. + * Note that this implementation is not synchronized. + */ +public class YamlConfiguration extends FileConfiguration { + protected static final String COMMENT_PREFIX = "# "; + protected static final String BLANK_CONFIG = "{}\n"; + private final DumperOptions yamlOptions = new DumperOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(); + private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + + /** + * Creates a new {@link com.boydti.fawe.configuration.file.YamlConfiguration}, loading from the given file. + *

+ * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + *

+ * The encoding used may follow the system dependent default. + * + * @param file Input file + * @return Resulting configuration + * @throws IllegalArgumentException Thrown if file is null + */ + public static com.boydti.fawe.configuration.file.YamlConfiguration loadConfiguration(final File file) { + if (file == null) { + throw new NullPointerException("File cannot be null"); + } + + final com.boydti.fawe.configuration.file.YamlConfiguration config = new com.boydti.fawe.configuration.file.YamlConfiguration(); + + try { + config.load(file); + } catch (InvalidConfigurationException | IOException ex) { + try { + file.getAbsolutePath(); + File dest = new File(file.getAbsolutePath() + "_broken"); + int i = 0; + while (dest.exists()) { + dest = new File(file.getAbsolutePath() + "_broken_" + i++); + } + Files.copy(file.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING); + System.out.println("&dCould not read: &7" + file); + System.out.println("&dRenamed to: &7" + dest.getName()); + System.out.println("&c============ Full stacktrace ============"); + ex.printStackTrace(); + System.out.println("&c========================================="); + } catch (final IOException e) { + e.printStackTrace(); + } + } + + return config; + } + + /** + * Creates a new {@link com.boydti.fawe.configuration.file.YamlConfiguration}, loading from the given reader. + *

+ * Any errors loading the Configuration will be logged and then ignored. + * If the specified input is not a valid config, a blank config will be + * returned. + * + * @param reader input + * @return resulting configuration + * @throws IllegalArgumentException Thrown if stream is null + */ + public static com.boydti.fawe.configuration.file.YamlConfiguration loadConfiguration(final Reader reader) { + if (reader == null) { + throw new NullPointerException("Reader cannot be null"); + } + + final com.boydti.fawe.configuration.file.YamlConfiguration config = new com.boydti.fawe.configuration.file.YamlConfiguration(); + + try { + config.load(reader); + } catch (final IOException | InvalidConfigurationException ex) { + System.out.println("Cannot load configuration from stream"); + ex.printStackTrace(); + } + + return config; + } + + @Override + public String saveToString() { + yamlOptions.setIndent(options().indent()); + yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + final String header = buildHeader(); + String dump = yaml.dump(getValues(false)); + + if (dump.equals(BLANK_CONFIG)) { + dump = ""; + } + + return header + dump; + } + + @Override + public void loadFromString(final String contents) throws InvalidConfigurationException { + if (contents == null) { + throw new NullPointerException("Contents cannot be null"); + } + + Map input; + try { + input = (Map) yaml.load(contents); + } catch (final YAMLException e) { + throw new InvalidConfigurationException(e); + } catch (final ClassCastException e) { + throw new InvalidConfigurationException("Top level is not a Map."); + } + + final String header = parseHeader(contents); + if (!header.isEmpty()) { + options().header(header); + } + + if (input != null) { + convertMapsToSections(input, this); + } + } + + protected void convertMapsToSections(final Map input, final ConfigurationSection section) { + for (final Map.Entry entry : input.entrySet()) { + final String key = entry.getKey().toString(); + final Object value = entry.getValue(); + + if (value instanceof Map) { + convertMapsToSections((Map) value, section.createSection(key)); + } else { + section.set(key, value); + } + } + } + + protected String parseHeader(final String input) { + final String[] lines = input.split("\r?\n", -1); + final StringBuilder result = new StringBuilder(); + boolean readingHeader = true; + boolean foundHeader = false; + + for (int i = 0; (i < lines.length) && readingHeader; i++) { + final String line = lines[i]; + + if (line.startsWith(COMMENT_PREFIX)) { + if (i > 0) { + result.append("\n"); + } + + if (line.length() > COMMENT_PREFIX.length()) { + result.append(line.substring(COMMENT_PREFIX.length())); + } + + foundHeader = true; + } else if (foundHeader && line.isEmpty()) { + result.append("\n"); + } else if (foundHeader) { + readingHeader = false; + } + } + + return result.toString(); + } + + @Override + protected String buildHeader() { + final String header = options().header(); + + if (options().copyHeader()) { + final Configuration def = getDefaults(); + + if (def != null && def instanceof FileConfiguration) { + final FileConfiguration filedefaults = (FileConfiguration) def; + final String defaultsHeader = filedefaults.buildHeader(); + + if ((defaultsHeader != null) && !defaultsHeader.isEmpty()) { + return defaultsHeader; + } + } + } + + if (header == null) { + return ""; + } + + final StringBuilder builder = new StringBuilder(); + final String[] lines = header.split("\r?\n", -1); + boolean startedHeader = false; + + for (int i = lines.length - 1; i >= 0; i--) { + builder.insert(0, "\n"); + + if (startedHeader || !lines[i].isEmpty()) { + builder.insert(0, lines[i]); + builder.insert(0, COMMENT_PREFIX); + startedHeader = true; + } + } + + return builder.toString(); + } + + @Override + public YamlConfigurationOptions options() { + if (options == null) { + options = new YamlConfigurationOptions(this); + } + + return (YamlConfigurationOptions) options; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfigurationOptions.java b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfigurationOptions.java new file mode 100644 index 00000000..d30195ce --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConfigurationOptions.java @@ -0,0 +1,76 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.file.FileConfigurationOptions; +import com.boydti.fawe.configuration.file.YamlConfiguration; + +/** + * Various settings for controlling the input and output of a {@link + * com.boydti.fawe.configuration.file.YamlConfiguration} + */ +public class YamlConfigurationOptions extends FileConfigurationOptions { + private int indent = 2; + + protected YamlConfigurationOptions(final YamlConfiguration configuration) { + super(configuration); + } + + @Override + public YamlConfiguration configuration() { + return (YamlConfiguration) super.configuration(); + } + + @Override + public com.boydti.fawe.configuration.file.YamlConfigurationOptions copyDefaults(final boolean value) { + super.copyDefaults(value); + return this; + } + + @Override + public com.boydti.fawe.configuration.file.YamlConfigurationOptions pathSeparator(final char value) { + super.pathSeparator(value); + return this; + } + + @Override + public com.boydti.fawe.configuration.file.YamlConfigurationOptions header(final String value) { + super.header(value); + return this; + } + + @Override + public com.boydti.fawe.configuration.file.YamlConfigurationOptions copyHeader(final boolean value) { + super.copyHeader(value); + return this; + } + + /** + * Gets how much spaces should be used to indent each line. + *

+ * The minimum value this may be is 2, and the maximum is 9. + * + * @return How much to indent by + */ + public int indent() { + return indent; + } + + /** + * Sets how much spaces should be used to indent each line. + *

+ * The minimum value this may be is 2, and the maximum is 9. + * + * @param value New indent + * @return This object, for chaining + */ + public com.boydti.fawe.configuration.file.YamlConfigurationOptions indent(final int value) { + if (value < 2) { + throw new IllegalArgumentException("Indent must be at least 2 characters"); + } + if (value > 9) { + throw new IllegalArgumentException("Indent cannot be greater than 9 characters"); + } + + indent = value; + return this; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/YamlConstructor.java b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConstructor.java new file mode 100644 index 00000000..6e21b325 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/YamlConstructor.java @@ -0,0 +1,47 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.serialization.ConfigurationSerialization; +import java.util.LinkedHashMap; +import java.util.Map; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.Tag; + +public class YamlConstructor extends SafeConstructor { + + public YamlConstructor() { + yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); + } + + private class ConstructCustomObject extends ConstructYamlMap { + @Override + public Object construct(final Node node) { + if (node.isTwoStepsConstruction()) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + + final Map raw = (Map) super.construct(node); + + if (raw.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { + final Map typed = new LinkedHashMap<>(raw.size()); + for (final Map.Entry entry : raw.entrySet()) { + typed.put(entry.getKey().toString(), entry.getValue()); + } + + try { + return ConfigurationSerialization.deserializeObject(typed); + } catch (final IllegalArgumentException ex) { + throw new YAMLException("Could not deserialize object", ex); + } + } + + return raw; + } + + @Override + public void construct2ndStep(final Node node, final Object object) { + throw new YAMLException("Unexpected referential mapping structure. Node: " + node); + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/file/YamlRepresenter.java b/core/src/main/java/com/boydti/fawe/configuration/file/YamlRepresenter.java new file mode 100644 index 00000000..8811e851 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/file/YamlRepresenter.java @@ -0,0 +1,38 @@ +package com.boydti.fawe.configuration.file; + +import com.boydti.fawe.configuration.ConfigurationSection; +import com.boydti.fawe.configuration.serialization.ConfigurationSerializable; +import com.boydti.fawe.configuration.serialization.ConfigurationSerialization; +import java.util.LinkedHashMap; +import java.util.Map; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.representer.Representer; + +public class YamlRepresenter extends Representer { + + public YamlRepresenter() { + this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection()); + this.multiRepresenters.put(ConfigurationSerializable.class, new RepresentConfigurationSerializable()); + } + + private class RepresentConfigurationSection extends RepresentMap { + + @Override + public Node representData(Object data) { + return super.representData(((ConfigurationSection) data).getValues(false)); + } + } + + private class RepresentConfigurationSerializable extends RepresentMap { + + @Override + public Node representData(Object data) { + ConfigurationSerializable serializable = (ConfigurationSerializable) data; + Map values = new LinkedHashMap<>(); + values.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, ConfigurationSerialization.getAlias(serializable.getClass())); + values.putAll(serializable.serialize()); + + return super.representData(values); + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerializable.java b/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerializable.java new file mode 100644 index 00000000..2b1b42f8 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerializable.java @@ -0,0 +1,35 @@ +package com.boydti.fawe.configuration.serialization; + +import java.util.Map; + +/** + * Represents an object that may be serialized. + *

+ * These objects MUST implement one of the following, in addition to the + * methods as defined by this interface: + *

    + *
  • A static method "deserialize" that accepts a single {@link java.util.Map}< + * {@link String}, {@link Object}> and returns the class.
  • + *
  • A static method "valueOf" that accepts a single {@link java.util.Map}<{@link + * String}, {@link Object}> and returns the class.
  • + *
  • A constructor that accepts a single {@link java.util.Map}<{@link String}, + * {@link Object}>.
  • + *
+ * In addition to implementing this interface, you must register the class + * with {@link com.boydti.fawe.configuration.serialization.ConfigurationSerialization#registerClass(Class)}. + * + * @see com.boydti.fawe.configuration.serialization.DelegateDeserialization + * @see com.boydti.fawe.configuration.serialization.SerializableAs + */ +public interface ConfigurationSerializable { + + /** + * Creates a Map representation of this class. + *

+ * This class must provide a method to restore this class, as defined in + * the {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} interface javadoc. + * + * @return Map containing the current state of this class + */ + Map serialize(); +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerialization.java b/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerialization.java new file mode 100644 index 00000000..a5e15dee --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/serialization/ConfigurationSerialization.java @@ -0,0 +1,264 @@ +package com.boydti.fawe.configuration.serialization; + +import com.boydti.fawe.configuration.serialization.DelegateDeserialization; +import com.boydti.fawe.configuration.serialization.SerializableAs; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility class for storing and retrieving classes for {@link com.boydti.fawe.configuration.Configuration}. + */ +public class ConfigurationSerialization { + + public static final String SERIALIZED_TYPE_KEY = "=="; + private static final Map> aliases = + new HashMap>(); + private final Class clazz; + + protected ConfigurationSerialization(Class clazz) { + this.clazz = clazz; + } + + /** + * Attempts to deserialize the given arguments into a new instance of the + * given class. + * + *

The class must implement {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable.

+ * + *

If a new instance could not be made, an example being the class not + * fully implementing the interface, null will be returned.

+ * + * @param args Arguments for deserialization + * @param clazz Class to deserialize into + * @return New instance of the specified class + */ + public static ConfigurationSerializable deserializeObject(Map args, Class clazz) { + return new com.boydti.fawe.configuration.serialization.ConfigurationSerialization(clazz).deserialize(args); + } + + /** + * Attempts to deserialize the given arguments into a new instance of the + * + * given class. + *

+ * The class must implement {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable}, including + * the extra methods as specified in the javadoc of + * ConfigurationSerializable.

+ * + *

+ * If a new instance could not be made, an example being the class not + * fully implementing the interface, null will be returned.

+ * + * @param args Arguments for deserialization + * @return New instance of the specified class + */ + public static ConfigurationSerializable deserializeObject(Map args) { + Class clazz = null; + + if (args.containsKey(SERIALIZED_TYPE_KEY)) { + try { + String alias = (String) args.get(SERIALIZED_TYPE_KEY); + + if (alias == null) { + throw new IllegalArgumentException("Cannot have null alias"); + } + clazz = getClassByAlias(alias); + if (clazz == null) { + throw new IllegalArgumentException("Specified class does not exist ('" + alias + "')"); + } + } catch (ClassCastException ex) { + ex.fillInStackTrace(); + throw ex; + } + } else { + throw new IllegalArgumentException("Args doesn't contain type key ('" + SERIALIZED_TYPE_KEY + "')"); + } + + return new com.boydti.fawe.configuration.serialization.ConfigurationSerialization(clazz).deserialize(args); + } + + /** + * Registers the given {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} class by its + * alias. + * + * @param clazz Class to register + */ + public static void registerClass(Class clazz) { + com.boydti.fawe.configuration.serialization.DelegateDeserialization delegate = clazz.getAnnotation(com.boydti.fawe.configuration.serialization.DelegateDeserialization.class); + + if (delegate == null) { + registerClass(clazz, getAlias(clazz)); + registerClass(clazz, clazz.getName()); + } + } + + /** + * Registers the given alias to the specified {@link + * com.boydti.fawe.configuration.serialization.ConfigurationSerializable} class. + * + * @param clazz Class to register + * @param alias Alias to register as + * @see com.boydti.fawe.configuration.serialization.SerializableAs + */ + public static void registerClass(Class clazz, String alias) { + aliases.put(alias, clazz); + } + + /** + * Unregisters the specified alias to a {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} + * + * @param alias Alias to unregister + */ + public static void unregisterClass(String alias) { + aliases.remove(alias); + } + + /** + * Unregisters any aliases for the specified {@link + * com.boydti.fawe.configuration.serialization.ConfigurationSerializable} class. + * + * @param clazz Class to unregister + */ + public static void unregisterClass(Class clazz) { + while (aliases.values().remove(clazz)) { + } + } + + /** + * Attempts to get a registered {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} class by + * its alias. + * + * @param alias Alias of the serializable + * @return Registered class, or null if not found + */ + public static Class getClassByAlias(String alias) { + return aliases.get(alias); + } + + /** + * Gets the correct alias for the given {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} + * class. + * + * @param clazz Class to get alias for + * @return Alias to use for the class + */ + public static String getAlias(Class clazz) { + com.boydti.fawe.configuration.serialization.DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class); + + if (delegate != null) { + if ((delegate.value() == null) || (delegate.value() == clazz)) { + delegate = null; + } else { + return getAlias(delegate.value()); + } + } + + SerializableAs alias = clazz.getAnnotation(SerializableAs.class); + + if (alias != null) { + return alias.value(); + } + + return clazz.getName(); + } + + protected Method getMethod(String name, boolean isStatic) { + try { + Method method = this.clazz.getDeclaredMethod(name, Map.class); + + if (!ConfigurationSerializable.class.isAssignableFrom(method.getReturnType())) { + return null; + } + if (Modifier.isStatic(method.getModifiers()) != isStatic) { + return null; + } + + return method; + } catch (NoSuchMethodException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + protected Constructor getConstructor() { + try { + return this.clazz.getConstructor(Map.class); + } catch (NoSuchMethodException ex) { + return null; + } catch (SecurityException ex) { + return null; + } + } + + protected ConfigurationSerializable deserializeViaMethod(Method method, Map args) { + try { + ConfigurationSerializable result = (ConfigurationSerializable) method.invoke(null, args); + + if (result == null) { + Logger.getLogger(com.boydti.fawe.configuration.serialization.ConfigurationSerialization.class.getName()).log(Level.SEVERE, + "Could not call method '" + method.toString() + "' of " + this.clazz + " for deserialization: method returned null"); + } else { + return result; + } + } catch (Throwable ex) { + Logger.getLogger(com.boydti.fawe.configuration.serialization.ConfigurationSerialization.class.getName()) + .log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + this.clazz + + " for deserialization", + ex instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + protected ConfigurationSerializable deserializeViaCtor(Constructor ctor, Map args) { + try { + return ctor.newInstance(args); + } catch (Throwable ex) { + Logger.getLogger(com.boydti.fawe.configuration.serialization.ConfigurationSerialization.class.getName()) + .log(Level.SEVERE, "Could not call constructor '" + ctor.toString() + "' of " + this.clazz + + " for deserialization", + ex instanceof InvocationTargetException ? ex.getCause() : ex); + } + + return null; + } + + public ConfigurationSerializable deserialize(Map args) { + if (args == null) { + throw new NullPointerException("Args must not be null"); + } + ConfigurationSerializable result = null; + Method method = getMethod("deserialize", true); + + if (method != null) { + result = deserializeViaMethod(method, args); + } + + if (result == null) { + method = getMethod("valueOf", true); + + if (method != null) { + result = deserializeViaMethod(method, args); + } + } + + if (result == null) { + Constructor constructor = getConstructor(); + + if (constructor != null) { + result = deserializeViaCtor(constructor, args); + } + } + + return result; + } +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/serialization/DelegateDeserialization.java b/core/src/main/java/com/boydti/fawe/configuration/serialization/DelegateDeserialization.java new file mode 100644 index 00000000..2d033ea5 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/serialization/DelegateDeserialization.java @@ -0,0 +1,23 @@ +package com.boydti.fawe.configuration.serialization; + +import com.boydti.fawe.configuration.serialization.ConfigurationSerializable; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applies to a {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} that will delegate all + * deserialization to another {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DelegateDeserialization { + /** + * Which class should be used as a delegate for this classes + * deserialization + * + * @return Delegate class + */ + Class value(); +} diff --git a/core/src/main/java/com/boydti/fawe/configuration/serialization/SerializableAs.java b/core/src/main/java/com/boydti/fawe/configuration/serialization/SerializableAs.java new file mode 100644 index 00000000..a51c7d7e --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/configuration/serialization/SerializableAs.java @@ -0,0 +1,34 @@ +package com.boydti.fawe.configuration.serialization; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Represents an "alias" that a {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} may be + * stored as. + * If this is not present on a {@link com.boydti.fawe.configuration.serialization.ConfigurationSerializable} class, it + * will use the fully qualified name of the class. + *

+ * This value will be stored in the configuration so that the configuration + * deserialization can determine what type it is. + *

+ * Using this annotation on any other class than a {@link + * com.boydti.fawe.configuration.serialization.ConfigurationSerializable} will have no effect. + * + * @see com.boydti.fawe.configuration.serialization.ConfigurationSerialization#registerClass(Class, String) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface SerializableAs { + /** + * This is the name your class will be stored and retrieved as. + *

+ * This name MUST be unique. We recommend using names such as + * "MyPluginThing" instead of "Thing". + * + * @return Name to serialize the class as. + */ + String value(); +} diff --git a/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java b/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java index 71892015..319c7877 100644 --- a/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java +++ b/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java @@ -1,10 +1,10 @@ package com.boydti.fawe.object; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.history.changeset.ChangeSet; public class EditSessionWrapper { @@ -26,7 +26,7 @@ public class EditSessionWrapper { return minY; } - public Extent getHistoryExtent(final Extent parent, final ChangeSet set, final FawePlayer player) { + public Extent getHistoryExtent(final Extent parent, FaweChangeSet set, final FawePlayer player) { return new HistoryExtent(parent, set); } } diff --git a/core/src/main/java/com/boydti/fawe/object/FaweChangeSet.java b/core/src/main/java/com/boydti/fawe/object/FaweChangeSet.java deleted file mode 100644 index daaddc1b..00000000 --- a/core/src/main/java/com/boydti/fawe/object/FaweChangeSet.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.boydti.fawe.object; - -import java.util.ArrayDeque; -import java.util.Iterator; - -import com.sk89q.worldedit.history.change.BlockChange; -import com.sk89q.worldedit.history.change.Change; -import com.sk89q.worldedit.history.changeset.ChangeSet; - -public class FaweChangeSet implements ChangeSet { - - private final ArrayDeque changes = new ArrayDeque<>(); - - @Override - public void add(final Change change) { - if (change.getClass() == BlockChange.class) { - final BlockChange bc = (BlockChange) change; - bc.getCurrent(); - // BaseBlock previous = bc.getPrevious(); - // BlockVector pos = bc.getPosition(); - // int x = pos.getBlockX(); - // int y = pos.getBlockY(); - // int z = pos.getBlockZ(); - this.changes.add(bc); - } else { - this.changes.add(change); - } - } - - @Override - public Iterator backwardIterator() { - return this.changes.descendingIterator(); - } - - @Override - public Iterator forwardIterator() { - return this.changes.iterator(); - } - - @Override - public int size() { - return this.changes.size(); - } -} diff --git a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java index 0a5c076d..6484a888 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -11,18 +11,25 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; +import java.util.concurrent.ConcurrentHashMap; public abstract class FawePlayer { public final T parent; private LocalSession session; + /** + * The metadata map. + */ + private ConcurrentHashMap meta; + public static FawePlayer wrap(final Object obj) { return Fawe.imp().wrap(obj); } public FawePlayer(final T parent) { this.parent = parent; + Fawe.get().register(this); } public abstract String getName(); @@ -84,4 +91,51 @@ public abstract class FawePlayer { public boolean hasWorldEditBypass() { return this.hasPermission("fawe.bypass"); } + + /** + * Set some session only metadata for the player + * @param key + * @param value + */ + public void setMeta(String key, Object value) { + if (this.meta == null) { + this.meta = new ConcurrentHashMap<>(); + } + this.meta.put(key, value); + } + + /** + * Get the metadata for a key. + * @param + * @param key + * @return + */ + public T getMeta(String key) { + if (this.meta != null) { + return (T) this.meta.get(key); + } + return null; + } + + public T getMeta(String key, T def) { + if (this.meta != null) { + T value = (T) this.meta.get(key); + return value == null ? def : value; + } + return def; + } + + /** + * Delete the metadata for a key. + * - metadata is session only + * - deleting other plugin's metadata may cause issues + * @param key + */ + public Object deleteMeta(String key) { + return this.meta == null ? null : this.meta.remove(key); + } + + public void unregister() { + Fawe.get().unregister(getName()); + } } diff --git a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java index 989902b0..a4e68a1c 100644 --- a/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/HistoryExtent.java @@ -1,12 +1,7 @@ package com.boydti.fawe.object; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nullable; - +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; @@ -14,28 +9,32 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.history.change.BlockChange; import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkNotNull; /** * Stores changes to a {@link ChangeSet}. */ public class HistoryExtent extends AbstractDelegateExtent { - private final ChangeSet changeSet; + private final com.boydti.fawe.object.changeset.FaweChangeSet changeSet; /** * Create a new instance. * * @param extent the extent * @param changeSet the change set - * @param thread */ - public HistoryExtent(final Extent extent, final ChangeSet changeSet) { + public HistoryExtent(final Extent extent, final FaweChangeSet changeSet) { super(extent); checkNotNull(changeSet); this.changeSet = changeSet; @@ -53,95 +52,15 @@ public class HistoryExtent extends AbstractDelegateExtent { final int id_p = previous.getId(); final int id_b = block.getId(); if (id_p == id_b) { - switch (id_p) { - case 0: - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - case 25: - case 30: - case 32: - case 37: - case 39: - case 40: - case 41: - case 42: - case 45: - case 46: - case 47: - case 48: - case 49: - case 51: - case 52: - case 54: - case 56: - case 57: - case 58: - case 60: - case 61: - case 62: - case 7: - case 8: - case 9: - case 10: - case 11: - case 73: - case 74: - case 78: - case 79: - case 80: - case 81: - case 82: - case 83: - case 84: - case 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 117: - case 121: - case 122: - case 123: - case 124: - case 129: - case 133: - case 138: - case 137: - case 140: - case 165: - case 166: - case 169: - case 170: - case 172: - case 173: - case 174: - case 176: - case 177: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: + if (FaweCache.hasData(id_p)) { + if (previous.getData() == block.getData()) { return false; - default: - if (block.getData() == previous.getData()) { - return false; - } + } + } else { + return false; } } - this.changeSet.add(new BlockChange(location.toBlockVector(), previous, block)); + this.changeSet.add(location, previous, block); return true; } return false; diff --git a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java index b63b6fe9..27647609 100644 --- a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java @@ -1,12 +1,13 @@ package com.boydti.fawe.object; +import com.boydti.fawe.object.changeset.FaweChangeSet; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.history.change.Change; import java.util.ArrayList; import java.util.Iterator; -import com.sk89q.worldedit.history.change.Change; -import com.sk89q.worldedit.history.changeset.ChangeSet; - -public class NullChangeSet implements ChangeSet { +public class NullChangeSet implements FaweChangeSet { @Override public void add(Change change) {} @@ -25,5 +26,10 @@ public class NullChangeSet implements ChangeSet { public int size() { return 0; } - + + @Override + public void flush() {} + + @Override + public void add(Vector location, BaseBlock from, BaseBlock to) {} } diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedHistory.java deleted file mode 100644 index 09be8f8f..00000000 --- a/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedHistory.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.boydti.fawe.object.changeset; - -import com.sk89q.worldedit.history.changeset.BlockOptimizedHistory; - -/** - * History optimized for speed - * - Low CPU usage - * - High memory usage - * - No disk usage - */ -public class CPUOptimizedHistory extends BlockOptimizedHistory { - -} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java index f41afeb7..010ecd69 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/DiskStorageHistory.java @@ -1,18 +1,5 @@ package com.boydti.fawe.object.changeset; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; @@ -25,12 +12,25 @@ import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NamedTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.history.change.BlockChange; import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.changeset.ChangeSet; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; /** * Store the change on disk @@ -39,7 +39,7 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; * - Minimal memory usage * - Slow */ -public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { +public class DiskStorageHistory implements ChangeSet, FaweChangeSet { private final File bdFile; private final File nbtfFile; @@ -108,7 +108,7 @@ public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { if ((change instanceof BlockChange)) { add((BlockChange) change); } else { - System.out.print("[FAWE] Does not support " + change + " yet! (Please bug Empire92)"); + Fawe.debug("[FAWE] Does not support " + change + " yet! (Please bug Empire92)"); } } @@ -136,29 +136,18 @@ public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { e.printStackTrace(); } } - - public void add(EntityCreate change) { - - } - - public void add(EntityRemove change) { + @Override + public void add(Vector location, BaseBlock from, BaseBlock to) { + add(location.getBlockX(), location.getBlockY(), location.getBlockZ(), from, to); } - public void add(BlockChange change) { + public void add(int x, int y, int z, BaseBlock from, BaseBlock to) { try { - BlockVector loc = change.getPosition(); - int x = loc.getBlockX(); - int y = loc.getBlockY(); - int z = loc.getBlockZ(); - - BaseBlock from = change.getPrevious(); - BaseBlock to = change.getCurrent(); - int idfrom = from.getId(); int combinedFrom = (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4)); CompoundTag nbtFrom = FaweCache.hasNBT(idfrom) ? from.getNbtData() : null; - + int idTo = to.getId(); int combinedTo = (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4)); CompoundTag nbtTo = FaweCache.hasNBT(idTo) ? to.getNbtData() : null; @@ -177,7 +166,7 @@ public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { //to stream.write((combinedTo) & 0xff); stream.write(((combinedTo) >> 8) & 0xff); - + if (nbtFrom != null && MainUtil.isValidTag(nbtFrom)) { Map value = ReflectionUtils.getMap(nbtFrom.getValue()); value.put("x", new IntTag(x)); @@ -186,7 +175,7 @@ public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { NBTOutputStream nbtos = getNBTFOS(x, y, z); nbtos.writeNamedTag(osNBTFI.getAndIncrement() + "", nbtFrom); } - + if (nbtTo != null && MainUtil.isValidTag(nbtTo)) { Map value = ReflectionUtils.getMap(nbtTo.getValue()); value.put("x", new IntTag(x)); @@ -199,6 +188,28 @@ public class DiskStorageHistory implements ChangeSet, FlushableChangeSet { e.printStackTrace(); } } + + public void add(EntityCreate change) { + // TODO + } + + public void add(EntityRemove change) { + // TODO + } + + public void add(BlockChange change) { + try { + BlockVector loc = change.getPosition(); + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + BaseBlock from = change.getPrevious(); + BaseBlock to = change.getCurrent(); + add(x, y, z, from, to); + } catch (Exception e) { + e.printStackTrace(); + } + } private OutputStream getBAOS(int x, int y, int z) throws IOException { if (osBD != null) { diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java new file mode 100644 index 00000000..315afda8 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -0,0 +1,11 @@ +package com.boydti.fawe.object.changeset; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.history.changeset.ChangeSet; + +public interface FaweChangeSet extends ChangeSet { + void flush(); + + void add(Vector location, BaseBlock from, BaseBlock to); +} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java deleted file mode 100644 index 652d15c9..00000000 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FlushableChangeSet.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.boydti.fawe.object.changeset; - -public interface FlushableChangeSet { - public void flush(); -} diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java index 3bc956e5..19732264 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/MemoryOptimizedHistory.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object.changeset; +import com.sk89q.worldedit.Vector; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -33,7 +34,7 @@ import com.sk89q.worldedit.history.changeset.ChangeSet; * - High CPU usage * - Low memory usage */ -public class MemoryOptimizedHistory implements ChangeSet, FlushableChangeSet { +public class MemoryOptimizedHistory implements ChangeSet, FaweChangeSet { private ArrayDeque fromTags; private ArrayDeque toTags; @@ -53,67 +54,74 @@ public class MemoryOptimizedHistory implements ChangeSet, FlushableChangeSet { size = new AtomicInteger(); } + @Override + public void add(Vector location, BaseBlock from, BaseBlock to) { + add(location.getBlockX(), location.getBlockY(), location.getBlockZ(), from, to); + } + + public void add(int x, int y, int z, BaseBlock from, BaseBlock to) { + try { + int idfrom = from.getId(); + int combinedFrom = (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4)); + CompoundTag nbtFrom = FaweCache.hasNBT(idfrom) ? from.getNbtData() : null; + + int idTo = to.getId(); + int combinedTo = (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4)); + CompoundTag nbtTo = FaweCache.hasNBT(idTo) ? to.getNbtData() : null; + OutputStream stream = getBAOS(x, y, z); + //x + stream.write((x - ox) & 0xff); + stream.write(((x - ox) >> 8) & 0xff); + //z + stream.write((z - oz) & 0xff); + stream.write(((z - oz) >> 8) & 0xff); + //y + stream.write((byte) y); + //from + stream.write((combinedFrom) & 0xff); + stream.write(((combinedFrom) >> 8) & 0xff); + //to + stream.write((combinedTo) & 0xff); + stream.write(((combinedTo) >> 8) & 0xff); + + if (nbtFrom != null && MainUtil.isValidTag(nbtFrom)) { + Map value = ReflectionUtils.getMap(nbtFrom.getValue()); + value.put("x", new IntTag(x)); + value.put("y", new IntTag(y)); + value.put("z", new IntTag(z)); + if (fromTags == null) { + fromTags = new ArrayDeque<>(); + } + fromTags.add(nbtFrom); + } + + if (nbtTo != null && MainUtil.isValidTag(nbtTo)) { + Map value = ReflectionUtils.getMap(nbtTo.getValue()); + value.put("x", new IntTag(x)); + value.put("y", new IntTag(y)); + value.put("z", new IntTag(z)); + if (toTags == null) { + toTags = new ArrayDeque<>(); + } + toTags.add(nbtTo); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + @Override public void add(Change arg) { size.incrementAndGet(); if ((arg instanceof BlockChange)) { - try { BlockChange change = (BlockChange) arg; BlockVector loc = change.getPosition(); int x = loc.getBlockX(); int y = loc.getBlockY(); int z = loc.getBlockZ(); - BaseBlock from = change.getPrevious(); BaseBlock to = change.getCurrent(); - - int idfrom = from.getId(); - int combinedFrom = (FaweCache.hasData(idfrom) ? ((idfrom << 4) + from.getData()) : (idfrom << 4)); - CompoundTag nbtFrom = FaweCache.hasNBT(idfrom) ? from.getNbtData() : null; - - int idTo = to.getId(); - int combinedTo = (FaweCache.hasData(idTo) ? ((idTo << 4) + to.getData()) : (idTo << 4)); - CompoundTag nbtTo = FaweCache.hasNBT(idTo) ? to.getNbtData() : null; - OutputStream stream = getBAOS(x, y, z); - //x - stream.write((x - ox) & 0xff); - stream.write(((x - ox) >> 8) & 0xff); - //z - stream.write((z - oz) & 0xff); - stream.write(((z - oz) >> 8) & 0xff); - //y - stream.write((byte) y); - //from - stream.write((combinedFrom) & 0xff); - stream.write(((combinedFrom) >> 8) & 0xff); - //to - stream.write((combinedTo) & 0xff); - stream.write(((combinedTo) >> 8) & 0xff); - - if (nbtFrom != null && MainUtil.isValidTag(nbtFrom)) { - Map value = ReflectionUtils.getMap(nbtFrom.getValue()); - value.put("x", new IntTag(x)); - value.put("y", new IntTag(y)); - value.put("z", new IntTag(z)); - if (fromTags == null) { - fromTags = new ArrayDeque<>(); - } - fromTags.add(nbtFrom); - } - - if (nbtTo != null && MainUtil.isValidTag(nbtTo)) { - Map value = ReflectionUtils.getMap(nbtTo.getValue()); - value.put("x", new IntTag(x)); - value.put("y", new IntTag(y)); - value.put("z", new IntTag(z)); - if (toTags == null) { - toTags = new ArrayDeque<>(); - } - toTags.add(nbtTo); - } - } catch (Exception e) { - e.printStackTrace(); - } + add(x, y, z, from, to); } else { if (entities == null) { entities = new ArrayDeque<>(); diff --git a/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java b/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java index c348abaa..8aaaf91e 100644 --- a/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java +++ b/core/src/main/java/com/boydti/fawe/regions/general/PlotSquaredFeature.java @@ -9,6 +9,7 @@ import com.intellectualcrafters.plot.object.Plot; import com.intellectualcrafters.plot.object.PlotId; import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.RegionWrapper; +import com.sk89q.worldedit.BlockVector; import java.util.HashSet; public class PlotSquaredFeature extends FaweMaskManager { @@ -48,14 +49,14 @@ public class PlotSquaredFeature extends FaweMaskManager { RegionWrapper region = plot.getLargestRegion(); HashSet regions = plot.getRegions(); - final Location pos1 = new Location(world, region.minX, 0, region.minZ); - final Location pos2 = new Location(world, region.maxX, 256, region.maxZ); + final BlockVector pos1 = new BlockVector(region.minX, 0, region.minZ); + final BlockVector pos2 = new BlockVector(region.maxX, 256, region.maxZ); final HashSet faweRegions = new HashSet(); for (final com.intellectualcrafters.plot.object.RegionWrapper current : regions) { faweRegions.add(new com.boydti.fawe.object.RegionWrapper(current.minX, current.maxX, current.minZ, current.maxZ)); } - return new BukkitMask(pos1, pos2) { + return new FaweMask(pos1, pos2) { @Override public String getName() { return "PLOT^2:" + id; diff --git a/core/src/main/java/com/boydti/fawe/util/MainUtil.java b/core/src/main/java/com/boydti/fawe/util/MainUtil.java index 7cde68a3..fb22cadf 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -1,18 +1,14 @@ package com.boydti.fawe.util; -import java.io.File; -import java.util.Map.Entry; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - import com.boydti.fawe.Fawe; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.EndTag; import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.Tag; +import java.io.File; +import java.util.Map.Entry; public class MainUtil { /* @@ -20,7 +16,7 @@ public class MainUtil { * e.g. sending messages */ public static void sendMessage(final FawePlayer player, String message) { - message = ChatColor.translateAlternateColorCodes('&', message); + message = BBC.color(message); if (player == null) { Fawe.debug(message); } else { @@ -29,7 +25,7 @@ public class MainUtil { } public static void sendAdmin(final String s) { - for (final Player player : Bukkit.getOnlinePlayers()) { + for (final FawePlayer player : Fawe.imp().getPlayers()) { if (player.hasPermission("fawe.admin")) { player.sendMessage(s); } diff --git a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java index c796afbd..248c3f72 100644 --- a/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java +++ b/core/src/main/java/com/boydti/fawe/util/ReflectionUtils.java @@ -9,7 +9,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; - import org.bukkit.Bukkit; import org.bukkit.Server; @@ -32,7 +31,7 @@ public class ReflectionUtils { */ private static boolean forge = false; /** check server version and class names */ - static { + public static void init() { if (Bukkit.getServer() != null) { if (Bukkit.getVersion().contains("MCPC") || Bukkit.getVersion().contains("Forge")) { forge = true; @@ -221,6 +220,9 @@ public class ReflectionUtils { * @throws RuntimeException if no class found */ public static RefClass getRefClass(final String... classes) throws RuntimeException { + if (preClassM == null) { + init(); + } for (String className : classes) { try { className = className.replace("{cb}", preClassB).replace("{nms}", preClassM).replace("{nm}", "net.minecraft"); diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index a1308b77..54ad9906 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -19,25 +19,6 @@ package com.sk89q.worldedit; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.regions.Regions.asFlatRegion; -import static com.sk89q.worldedit.regions.Regions.maximumBlockY; -import static com.sk89q.worldedit.regions.Regions.minimumBlockY; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.BBC; @@ -47,6 +28,7 @@ import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.NullChangeSet; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.changeset.DiskStorageHistory; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.NullExtent; @@ -130,6 +112,24 @@ import com.sk89q.worldedit.util.collection.DoubleArrayList; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.regions.Regions.asFlatRegion; +import static com.sk89q.worldedit.regions.Regions.maximumBlockY; +import static com.sk89q.worldedit.regions.Regions.minimumBlockY; /** * An {@link Extent} that handles history, {@link BlockBag}s, change limits, @@ -152,7 +152,7 @@ public class EditSession implements Extent { } protected final World world; - private final ChangeSet changeSet; + private final FaweChangeSet changeSet; private final EditSessionWrapper wrapper; private MultiStageReorder reorderExtent; private @Nullable Extent changeSetExtent; diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index 0f23d63e..da6cee2d 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -19,18 +19,7 @@ package com.sk89q.worldedit; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.annotation.Nullable; - -import com.boydti.fawe.object.changeset.FlushableChangeSet; +import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; import com.sk89q.jchronic.utils.Span; @@ -57,6 +46,16 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.snapshot.Snapshot; +import java.util.Calendar; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.annotation.Nullable; + + +import static com.google.common.base.Preconditions.checkNotNull; /** * Stores session information. @@ -200,8 +199,8 @@ public class LocalSession { history.remove(historyPointer); } ChangeSet set = editSession.getChangeSet(); - if (set instanceof FlushableChangeSet) { - ((FlushableChangeSet) set).flush(); + if (set instanceof FaweChangeSet) { + ((FaweChangeSet) set).flush(); } history.add(editSession); while (history.size() > MAX_HISTORY_SIZE) { diff --git a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 6775741b..e99cf99a 100644 --- a/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -19,16 +19,6 @@ package com.sk89q.worldedit.extension.platform; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldedit.util.command.composition.LegacyCommandAdapter.adapt; - -import java.io.File; -import java.io.IOException; -import java.util.logging.FileHandler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.regex.Pattern; - import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.google.common.base.Joiner; @@ -88,6 +78,16 @@ import com.sk89q.worldedit.util.formatting.ColorCodeBuilder; import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; import com.sk89q.worldedit.util.logging.DynamicStreamHandler; import com.sk89q.worldedit.util.logging.LogFormat; +import java.io.File; +import java.io.IOException; +import java.util.logging.FileHandler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.util.command.composition.LegacyCommandAdapter.adapt; /** * Handles the registration and invocation of commands. @@ -271,12 +271,12 @@ public final class CommandManager { if (editSession != null) { session.remember(editSession); editSession.flushQueue(); - long time = System.currentTimeMillis() - start; + final long time = System.currentTimeMillis() - start; if (time > 5 && editSession.size() != 0) { SetQueue.IMP.addTask(new Runnable() { @Override public void run() { - actor.print("[FAWE] Action complete"); + actor.print("[FAWE] Action completed in " + (time / 1000d) + " seconds"); } }); } diff --git a/forge/.gradle/gradle.log b/forge/.gradle/gradle.log index e69de29b..f84dc905 100644 --- a/forge/.gradle/gradle.log +++ b/forge/.gradle/gradle.log @@ -0,0 +1,93 @@ +################################################# + ForgeGradle 2.1-SNAPSHOT-unknown + https://github.com/MinecraftForge/ForgeGradle +################################################# + Powered by MCP unknown + http://modcoderpack.com + by: Searge, ProfMobius, Fesh0r, + R4wk, ZeuX, IngisKahn, bspkrs +################################################# +Version string 'unspecified' does not match SemVer specification +You should try SemVer : http://semver.org/ +:forge:deobfCompileDummyTask +:core:compileJava +:forge:deobfProvidedDummyTask +:forge:extractDependencyATs SKIPPED +:forge:extractMcpDatawarning: [options] bootstrap class path not set in conjunction with -source 1.7 + SKIPPED +:forge:extractMcpMappings SKIPPED +:forge:genSrgs SKIPPED +:forge:getVersionJson +:forge:downloadServer SKIPPED +:forge:splitServerJarNote: Some input files use or override a deprecated API. +Note: Recompile with -Xlint:deprecation for details. +Note: Some input files use unchecked or unsafe operations. +Note: Recompile with -Xlint:unchecked for details. +1 warning + +:core:processResources UP-TO-DATE +:core:classes +:core:jar +:forge:splitServerJar SKIPPED +:forge:deobfMcMCP SKIPPED +:forge:sourceApiJava UP-TO-DATE +:forge:compileApiJava UP-TO-DATE +:forge:processApiResources UP-TO-DATE +:forge:apiClasses UP-TO-DATE +:forge:sourceMainJava UP-TO-DATE +:forge:compileJavaC:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'modid()' in type 'Mod': class file for net.minecraftforge.fml.common.Mod not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'name()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'version()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptableRemoteVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'dependencies()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptedMinecraftVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'value()' in type 'Instance': class file for net.minecraftforge.fml.common.Mod$Instance not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'serverSide()' in type 'SidedProxy': class file for net.minecraftforge.fml.common.SidedProxy not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'clientSide()' in type 'SidedProxy' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'modid()' in type 'Mod': class file for net.minecraftforge.fml.common.Mod not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'name()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'version()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptableRemoteVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'dependencies()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptedMinecraftVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'value()' in type 'Instance': class file for net.minecraftforge.fml.common.Mod$Instance not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'serverSide()' in type 'SidedProxy': class file for net.minecraftforge.fml.common.SidedProxy not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'clientSide()' in type 'SidedProxy' +Note: Writing plugin metadata to file:/C:/Users/Jesse/Desktop/OTHER/GitHub/FastAsyncWorldEdit/forge/build/classes/main/mcmod.info +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'modid()' in type 'Mod': class file for net.minecraftforge.fml.common.Mod not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'name()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'version()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptableRemoteVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'dependencies()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'acceptedMinecraftVersions()' in type 'Mod' +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'value()' in type 'Instance': class file for net.minecraftforge.fml.common.Mod$Instance not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'serverSide()' in type 'SidedProxy': class file for net.minecraftforge.fml.common.SidedProxy not found +C:\Users\Jesse\.gradle\caches\modules-2\files-2.1\com.sk89q.worldedit\worldedit-forge-mc1.8.9\6.1.1\dffd7e1882eba256eb2132fe315682c1d26522b1\worldedit-forge-mc1.8.9-6.1.1.jar(com/sk89q/worldedit/forge/ForgeWorldEdit.class): warning: Cannot find annotation method 'clientSide()' in type 'SidedProxy' +Note: Some input files use or override a deprecated API. +Note: Recompile with -Xlint:deprecation for details. +Note: Some input files use unchecked or unsafe operations. +Note: Recompile with -Xlint:unchecked for details. +27 warnings + +:forge:processResources UP-TO-DATE +:forge:classes +:forge:jar +:forge:sourceTestJava UP-TO-DATE +:forge:compileTestJava UP-TO-DATE +:forge:processTestResources UP-TO-DATE +:forge:testClasses UP-TO-DATE +:forge:test UP-TO-DATE +:forge:reobfJar +:forge:shadowJar +:forge:reobfShadowJar +:forge:extractRangemapReplacedMain +C:\Users\Jesse\Desktop\OTHER\GitHub\FastAsyncWorldEdit\forge\build\sources\main\java +:forge:retromapReplacedMain UP-TO-DATE +:forge:sourceJar UP-TO-DATE +:forge:assemble +:forge:check UP-TO-DATE +:forge:build + +BUILD SUCCESSFUL + +Total time: 38.081 secs diff --git a/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java b/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java index 33354baa..209b9eab 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java +++ b/forge/src/main/java/com/boydti/fawe/forge/FaweSponge.java @@ -3,44 +3,36 @@ package com.boydti.fawe.forge; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.config.BBC; +import com.boydti.fawe.forge.v0.SpongeEditSessionWrapper_0; +import com.boydti.fawe.forge.v1_8.SpongeQueue_1_8; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.regions.FaweMaskManager; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.TaskManager; -import com.google.inject.Inject; -import com.intellectualcrafters.plot.config.C; -import com.intellectualcrafters.plot.config.Settings; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.forge.ForgeWorldEdit; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import org.mcstats.Metrics; -import org.slf4j.Logger; -import org.spongepowered.api.Game; -import org.spongepowered.api.Server; -import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.game.state.GameAboutToStartServerEvent; -import org.spongepowered.api.plugin.Plugin; -import org.spongepowered.api.plugin.PluginContainer; -import org.spongepowered.api.profile.GameProfileManager; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.text.serializer.TextSerializers; -@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.3.4") + +import static org.spongepowered.api.Sponge.getGame; + public class FaweSponge implements IFawe { - public PluginContainer plugin; + + public final SpongeMain plugin; + public FaweSponge instance; - @Inject - private Logger logger; - @Inject - private Game game; - private Server server; - - private GameProfileManager resolver; - private ForgeWorldEdit worldedit; public ForgeWorldEdit getWorldEditPlugin() { @@ -50,46 +42,21 @@ public class FaweSponge implements IFawe { return this.worldedit; } - public Game getGame() { - return this.game; - } - - public Server getServer() { - return this.server; - } - - public GameProfileManager getResolver() { - if (this.resolver == null) { - this.resolver = this.game.getServer().getGameProfileManager(); - } - return this.resolver; - } - - @Listener - public void onServerAboutToStart(GameAboutToStartServerEvent event) { - debug("FAWE: Server init"); + public FaweSponge(SpongeMain plugin) { instance = this; - plugin = this.game.getPluginManager().fromInstance(this).get(); - this.server = this.game.getServer(); + this.plugin = plugin; try { Fawe.set(this); } catch (final Throwable e) { e.printStackTrace(); - this.getServer().shutdown(); } + TaskManager.IMP.later(() -> SpongeUtil.initBiomeCache(), 0); } @Override public void debug(String message) { - message = C.format(message, C.replacements); - if (!Settings.CONSOLE_COLOR) { - message = message.replaceAll('\u00a7' + "[a-z|0-9]", ""); - } - if (this.server == null) { - this.logger.info(message); - return; - } - this.server.getConsole().sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(BBC.color(message))); + message = BBC.color(message); + Sponge.getServer().getConsole().sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(BBC.color(message))); } @Override @@ -99,57 +66,83 @@ public class FaweSponge implements IFawe { @Override public void setupCommand(String label, FaweCommand cmd) { - + getGame().getCommandManager().register(plugin, new SpongeCommand(cmd), label); } @Override public FawePlayer wrap(Object obj) { - return null; + if (obj.getClass() == String.class) { + String name = (String) obj; + FawePlayer existing = Fawe.get().getCachedPlayer(name); + if (existing != null) { + return existing; + } + Player player = Sponge.getServer().getPlayer(name).orElseGet(null); + return player != null ? new SpongePlayer(player) : null; + } else if (obj instanceof Player) { + Player player = (Player) obj; + FawePlayer existing = Fawe.get().getCachedPlayer(player.getName()); + return existing != null ? existing : new SpongePlayer(player); + } else { + return null; + } } @Override public void setupWEListener() { - + // Do nothing } @Override public void setupVault() { - + debug("[FAWE] Permission hook not implemented yet!"); } @Override public TaskManager getTaskManager() { - return null; + return new SpongeTaskMan(plugin); } @Override public int[] getVersion() { - return new int[0]; + debug("[FAWE] Checking minecraft version: Sponge: "); + String version = Sponge.getGame().getPlatform().getMinecraftVersion().getName(); + String[] split = version.split("\\."); + return new int[]{Integer.parseInt(split[0]), Integer.parseInt(split[1]), split.length == 3 ? Integer.parseInt(split[2]) : 0}; } @Override public FaweQueue getQueue() { - return null; + return new SpongeQueue_1_8(); } @Override public EditSessionWrapper getEditSessionWrapper(EditSession session) { - return null; + return new SpongeEditSessionWrapper_0(session); } @Override public Collection getMaskManagers() { - return null; + return new ArrayList<>(); } @Override public void startMetrics() { try { - Metrics metrics = new Metrics(this.game, this.plugin); + Metrics metrics = new Metrics(Sponge.getGame(), Sponge.getPluginManager().fromInstance(plugin).get()); metrics.start(); - debug(C.PREFIX.s() + "&6Metrics enabled."); + debug("[FAWE] &6Metrics enabled."); } catch (IOException e) { - debug(C.PREFIX.s() + "&cFailed to load up metrics."); + debug("[FAWE] &cFailed to load up metrics."); } } + + @Override + public Set getPlayers() { + HashSet players = new HashSet<>(); + for (Player player : Sponge.getServer().getOnlinePlayers()) { + players.add(wrap(player)); + } + return players; + } } diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java b/forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java new file mode 100644 index 00000000..40ce644a --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/SpongeMain.java @@ -0,0 +1,57 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.google.inject.Inject; +import org.slf4j.Logger; +import org.spongepowered.api.Game; +import org.spongepowered.api.Server; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.game.state.GamePreInitializationEvent; +import org.spongepowered.api.event.network.ClientConnectionEvent; +import org.spongepowered.api.plugin.Plugin; +import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.profile.GameProfileManager; + +@Plugin(id = "com.boydti.fawe", name = "FastAsyncWorldEdit", description = "Lagless WorldEdit, Area restrictions, Memory mangement, Block logging", url = "https://github.com/boy0001/FastAsyncWorldedit", version = "3.3.5") +public class SpongeMain { + public PluginContainer plugin; + + @Inject + private Logger logger; + + @Inject + private Game game; + private Server server; + + private GameProfileManager resolver; + + public Game getGame() { + return this.game; + } + + public Server getServer() { + return this.server; + } + + public GameProfileManager getResolver() { + if (this.resolver == null) { + this.resolver = this.game.getServer().getGameProfileManager(); + } + return this.resolver; + } + + + @Listener + public void onGamePreInit(GamePreInitializationEvent event) { + plugin = this.game.getPluginManager().fromInstance(this).get(); + this.server = this.game.getServer(); + new FaweSponge(this); + } + + @Listener + public void onQuit(ClientConnectionEvent.Disconnect event) { + Player player = event.getTargetEntity(); + Fawe.get().unregister(player.getName()); + } +} diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java b/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java index 9dfc85a3..46e19315 100644 --- a/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java +++ b/forge/src/main/java/com/boydti/fawe/forge/SpongePlayer.java @@ -29,12 +29,13 @@ public class SpongePlayer extends FawePlayer { @Override public boolean hasPermission(final String perm) { - return this.parent.hasPermission(perm); + Object meta = getMeta(perm); + return meta instanceof Boolean ? (boolean) meta : this.parent.hasPermission(perm); } @Override public void setPermission(final String perm, final boolean flag) { - throw new UnsupportedOperationException("WIP NOT IMPLEMENTED YET TODO"); // TODO FIXME + setMeta(perm, flag); } @Override @@ -58,4 +59,8 @@ public class SpongePlayer extends FawePlayer { return (com.sk89q.worldedit.entity.Player) Fawe. imp().getWorldEditPlugin().wrap((EntityPlayerMP) this.parent); } + @Override + public boolean hasWorldEditBypass() { + return hasPermission("fawe.bypass") || getMeta("fawe.bypass") == Boolean.TRUE; + } } diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java b/forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java new file mode 100644 index 00000000..e592c8ee --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/SpongeTaskMan.java @@ -0,0 +1,72 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.util.TaskManager; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicInteger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.scheduler.Task; + +public class SpongeTaskMan extends TaskManager { + + private final SpongeMain plugin; + + public SpongeTaskMan(SpongeMain plugin) { + this.plugin = plugin; + } + + private final AtomicInteger i = new AtomicInteger(); + + private final HashMap tasks = new HashMap<>(); + + @Override + public int repeat(Runnable runnable, int interval) { + int val = this.i.incrementAndGet(); + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + Task.Builder built = builder.delayTicks(interval).intervalTicks(interval).execute(runnable); + Task task = built.submit(plugin); + this.tasks.put(val, task); + return val; + } + + @Override + public int repeatAsync(Runnable runnable, int interval) { + int val = this.i.incrementAndGet(); + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + Task.Builder built = builder.delayTicks(interval).async().intervalTicks(interval).execute(runnable); + Task task = built.submit(plugin); + this.tasks.put(val, task); + return val; + } + + @Override + public void async(Runnable runnable) { + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + builder.async().execute(runnable).submit(plugin); + } + + @Override + public void task(Runnable runnable) { + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + builder.execute(runnable).submit(plugin); + } + + @Override + public void later(Runnable runnable, int delay) { + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + builder.delayTicks(delay).execute(runnable).submit(plugin); + } + + @Override + public void laterAsync(Runnable runnable, int delay) { + Task.Builder builder = Sponge.getGame().getScheduler().createTaskBuilder(); + builder.async().delayTicks(delay).execute(runnable).submit(plugin); + } + + @Override + public void cancel(int i) { + Task task = this.tasks.remove(i); + if (task != null) { + task.cancel(); + } + } +} \ No newline at end of file diff --git a/forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java b/forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java new file mode 100644 index 00000000..11999824 --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/SpongeUtil.java @@ -0,0 +1,75 @@ +package com.boydti.fawe.forge; + +import com.boydti.fawe.Fawe; +import com.sk89q.worldedit.world.biome.BiomeData; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.world.biome.BiomeGenBase; +import org.spongepowered.api.world.biome.BiomeType; +import org.spongepowered.api.world.biome.BiomeTypes; + +/** + * Created by Jesse on 4/2/2016. + */ +public class SpongeUtil { + private static BiomeType[] biomes; + private static HashMap biomeMap; + public static Map biomeData; + + public static void initBiomeCache() { + try { + Class clazz = Class.forName("com.sk89q.worldedit.forge.ForgeBiomeRegistry"); + Field bdf = clazz.getDeclaredField("biomeData"); + bdf.setAccessible(true); + biomeData = (Map) bdf.get(null); + biomes = new BiomeType[256]; + biomeMap = new HashMap<>(); + int lastId = 0; + loop: + for (Map.Entry entry : biomeData.entrySet()) { + int id = entry.getKey(); + BiomeData data = entry.getValue(); + String name = data.getName().toUpperCase().replaceAll(" ", "_").replaceAll("[+]", "_PLUS"); + if (name.endsWith("_M") || name.contains("_M_")) { + name = name.replaceAll("_M", "_MOUNTAINS"); + } + if (name.endsWith("_F") || name.contains("_F_")) { + name = name.replaceAll("_F", "_FOREST"); + } + try { + biomes[id] = (BiomeType) BiomeTypes.class.getField(name).get(null); + biomeMap.put(biomes[id].getId(), id); + lastId = id; + } + catch (Throwable e) { + Field[] fields = BiomeTypes.class.getDeclaredFields(); + for (Field field : fields) { + if (field.getName().replaceAll("_", "").equals(name.replaceAll("_", ""))) { + biomes[id] = (BiomeType) field.get(null); + biomeMap.put(biomes[id].getId(), id); + lastId = id; + continue loop; + } + } + Fawe.debug("Unknown biome: " + name); + biomes[id] = biomes[lastId]; + biomeMap.put(biomes[lastId].getId(), lastId); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public static BiomeType getBiome(String biome) { + if (biomes == null) { + initBiomeCache(); + } + return biomes[biomeMap.get(biome.toUpperCase())]; + } + + public static BiomeType getBiome(int index) { + return (BiomeType) BiomeGenBase.getBiome(index); + } +} diff --git a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java b/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java new file mode 100644 index 00000000..3c94c214 --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeEditSessionWrapper_0.java @@ -0,0 +1,13 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.object.EditSessionWrapper; +import com.sk89q.worldedit.EditSession; + +/** + * Created by Jesse on 4/2/2016. + */ +public class SpongeEditSessionWrapper_0 extends EditSessionWrapper { + public SpongeEditSessionWrapper_0(EditSession session) { + super(session); + } +} diff --git a/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java b/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java new file mode 100644 index 00000000..d832bde6 --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/v0/SpongeQueue_0.java @@ -0,0 +1,142 @@ +package com.boydti.fawe.forge.v0; + +import com.boydti.fawe.object.ChunkLoc; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.SetQueue; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.world.Chunk; +import org.spongepowered.api.world.World; + +/** + * Created by Jesse on 4/2/2016. + */ +public abstract class SpongeQueue_0 extends FaweQueue { + + /** + * Map of chunks in the queue + */ + private final ConcurrentHashMap> blocks = new ConcurrentHashMap<>(); + + @Override + public boolean isChunkLoaded(String worldName, int x, int z) { + World world = Sponge.getServer().getWorld(worldName).get(); + Chunk chunk = world.getChunk(x << 4, 0, z << 4).orElse(null); + return chunk != null && chunk.isLoaded(); + } + + @Override + public void addTask(String world, int x, int y, int z, Runnable runnable) { + // TODO Auto-generated method stub + final ChunkLoc wrap = new ChunkLoc(world, x >> 4, z >> 4); + FaweChunk result = this.blocks.get(wrap); + if (result == null) { + throw new IllegalArgumentException("Task must be accompanied by a block change or manually adding to queue!"); + } + result.addTask(runnable); + } + + @Override + public boolean setBlock(final String world, int x, final int y, int z, final short id, final byte data) { + if ((y > 255) || (y < 0)) { + return false; + } + final ChunkLoc wrap = new ChunkLoc(world, x >> 4, z >> 4); + x = x & 15; + z = z & 15; + FaweChunk result = this.blocks.get(wrap); + if (result == null) { + result = this.getChunk(wrap); + result.setBlock(x, y, z, id, data); + final FaweChunk previous = this.blocks.put(wrap, result); + if (previous == null) { + return true; + } + this.blocks.put(wrap, previous); + result = previous; + } + result.setBlock(x, y, z, id, data); + return true; + } + + @Override + public boolean setBiome(final String world, int x, int z, final BaseBiome biome) { + final ChunkLoc wrap = new ChunkLoc(world, x >> 4, z >> 4); + x = x & 15; + z = z & 15; + FaweChunk result = this.blocks.get(wrap); + if (result == null) { + result = this.getChunk(wrap); + final FaweChunk previous = this.blocks.put(wrap, result); + if (previous != null) { + this.blocks.put(wrap, previous); + result = previous; + } + } + result.setBiome(x, z, biome); + return true; + } + + @Override + public FaweChunk next() { + try { + if (this.blocks.size() == 0) { + return null; + } + final Iterator>> iter = this.blocks.entrySet().iterator(); + final FaweChunk toReturn = iter.next().getValue(); + if (SetQueue.IMP.isWaiting()) { + return null; + } + iter.remove(); + this.execute(toReturn); + return toReturn; + } catch (final Throwable e) { + e.printStackTrace(); + return null; + } + } + + private final ArrayDeque> toUpdate = new ArrayDeque<>(); + + public boolean execute(final FaweChunk fc) { + if (fc == null) { + return false; + } + // Load chunk + final Chunk chunk = fc.getChunk(); + chunk.loadChunk(true); + // Set blocks / entities / biome + if (!this.setComponents(fc)) { + return false; + } + fc.executeTasks(); + return true; + } + + @Override + public void clear() { + this.blocks.clear(); + } + + @Override + public void setChunk(final FaweChunk chunk) { + this.blocks.put(chunk.getChunkLoc(), (FaweChunk) chunk); + } + + public abstract Collection> sendChunk(final Collection> fcs); + + public abstract boolean setComponents(final FaweChunk fc); + + @Override + public abstract FaweChunk getChunk(final ChunkLoc wrap); + + @Override + public abstract boolean fixLighting(final FaweChunk fc, final boolean fixAll); +} diff --git a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java b/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java new file mode 100644 index 00000000..e09e5f13 --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeChunk_1_8.java @@ -0,0 +1,228 @@ +package com.boydti.fawe.forge.v1_8; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.object.ChunkLoc; +import com.boydti.fawe.object.FaweChunk; +import com.sk89q.worldedit.world.biome.BaseBiome; +import java.util.Arrays; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.world.Chunk; + +public class SpongeChunk_1_8 extends FaweChunk { + + public char[][] ids; + public short[] count; + public short[] air; + public short[] relight; + public int[][] biomes; + public Chunk chunk; + + public SpongeChunk_1_8(ChunkLoc chunk) { + super(chunk); + this.ids = new char[16][]; + this.count = new short[16]; + this.air = new short[16]; + this.relight = new short[16]; + } + + @Override + public Chunk getChunk() { + if (this.chunk == null) { + ChunkLoc cl = getChunkLoc(); + this.chunk = Sponge.getServer().getWorld(cl.world).get().getChunk(cl.x, 0, cl.z).get(); + } + return this.chunk; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getCount(int i) { + return this.count[i]; + } + + public int getAir(int i) { + return this.air[i]; + } + + public void setCount(int i, short value) { + this.count[i] = value; + } + + /** + * Get the number of block changes in a specified section. + * @param i + * @return + */ + public int getRelight(int i) { + return this.relight[i]; + } + + public int getTotalCount() { + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.count[i]; + } + return total; + } + + public int getTotalRelight() { + if (getTotalCount() == 0) { + Arrays.fill(this.count, (short) 1); + Arrays.fill(this.relight, Short.MAX_VALUE); + return Short.MAX_VALUE; + } + int total = 0; + for (int i = 0; i < 16; i++) { + total += this.relight[i]; + } + return total; + } + + /** + * Get the raw data for a section. + * @param i + * @return + */ + public char[] getIdArray(int i) { + return this.ids[i]; + } + + @Override + public void setBlock(int x, int y, int z, int id, byte data) { + int i = FaweCache.CACHE_I[y][x][z]; + int j = FaweCache.CACHE_J[y][x][z]; + char[] vs = this.ids[i]; + if (vs == null) { + vs = this.ids[i] = new char[4096]; + this.count[i]++; + } else if (vs[j] == 0) { + this.count[i]++; + } + switch (id) { + case 0: + this.air[i]++; + vs[j] = (char) 1; + return; + case 10: + case 11: + case 39: + case 40: + case 51: + case 74: + case 89: + case 122: + case 124: + case 138: + case 169: + this.relight[i]++; + case 2: + case 4: + case 13: + case 14: + case 15: + case 20: + case 21: + case 22: + case 30: + case 32: + case 37: + case 41: + case 42: + case 45: + case 46: + case 47: + case 48: + case 49: + case 55: + case 56: + case 57: + case 58: + case 60: + case 7: + case 8: + case 9: + case 73: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 85: + case 87: + case 88: + case 101: + case 102: + case 103: + case 110: + case 112: + case 113: + case 121: + case 129: + case 133: + case 165: + case 166: + case 170: + case 172: + case 173: + case 174: + case 181: + case 182: + case 188: + case 189: + case 190: + case 191: + case 192: + vs[j] = (char) (id << 4); + return; + case 130: + case 76: + case 62: + this.relight[i]++; + case 54: + case 146: + case 61: + case 65: + case 68: + case 50: + if (data < 2) { + data = 2; + } + default: + vs[j] = (char) ((id << 4) + data); + return; + } + } + + @Override + public void setBiome(int x, int z, BaseBiome biome) { + if (this.biomes == null) { + this.biomes = new int[16][]; + } + int[] index = this.biomes[x]; + if (index == null) { + index = this.biomes[x] = new int[16]; + } + index[z] = biome.getId(); + } + + @Override + public FaweChunk clone() { + SpongeChunk_1_8 toReturn = new SpongeChunk_1_8(getChunkLoc()); + toReturn.air = this.air.clone(); + toReturn.count = this.count.clone(); + toReturn.relight = this.relight.clone(); + toReturn.ids = new char[this.ids.length][]; + for (int i = 0; i < this.ids.length; i++) { + char[] matrix = this.ids[i]; + if (matrix != null) { + toReturn.ids[i] = new char[matrix.length]; + System.arraycopy(matrix, 0, toReturn.ids[i], 0, matrix.length); + } + } + return toReturn; + } +} diff --git a/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java b/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java new file mode 100644 index 00000000..ef5b36f4 --- /dev/null +++ b/forge/src/main/java/com/boydti/fawe/forge/v1_8/SpongeQueue_1_8.java @@ -0,0 +1,304 @@ +package com.boydti.fawe.forge.v1_8; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.forge.SpongeUtil; +import com.boydti.fawe.forge.v0.SpongeQueue_0; +import com.boydti.fawe.object.ChunkLoc; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.util.TaskManager; +import com.flowpowered.math.vector.Vector3i; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import net.minecraft.block.Block; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ClassInheritanceMultiMap; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.world.Chunk; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +public class SpongeQueue_1_8 extends SpongeQueue_0 { + + @Override + public Collection> sendChunk(Collection> fcs) { + if (fcs.isEmpty()) { + return fcs; + } + for (FaweChunk chunk : fcs) { + sendChunk(chunk); + } + fcs.clear(); + return fcs; + } + + public void sendChunk(FaweChunk fc) { + fixLighting(fc, Settings.FIX_ALL_LIGHTING); + Chunk chunk = fc.getChunk(); + if (!chunk.isLoaded()) { + return; + } + World world = chunk.getWorld(); + Vector3i pos = chunk.getBlockMin(); + int cx = pos.getX() >> 4; + int cz = pos.getZ() >> 4; + for (Player player : Sponge.getServer().getOnlinePlayers()) { + if (!player.getWorld().equals(world)) { + continue; + } + int view = player.getViewDistance(); + EntityPlayerMP nmsPlayer = (EntityPlayerMP) player; + Location loc = player.getLocation(); + int px = loc.getBlockX() >> 4; + int pz = loc.getBlockZ() >> 4; + int dx = Math.abs(cx - (loc.getBlockX() >> 4)); + int dz = Math.abs(cz - (loc.getBlockZ() >> 4)); + if ((dx > view) || (dz > view)) { + continue; + } + NetHandlerPlayServer con = nmsPlayer.playerNetServerHandler; + net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) chunk; + con.sendPacket(new S21PacketChunkData(nmsChunk, false, 65535)); + // Try sending true, 0 first + // Try bulk chunk packet + } + } + + @Override + public boolean setComponents(FaweChunk fc) { + SpongeChunk_1_8 fs = (SpongeChunk_1_8) fc; + Chunk spongeChunk = fc.getChunk(); + net.minecraft.world.World nmsWorld = (net.minecraft.world.World) spongeChunk.getWorld(); + ChunkLoc wrapper = fc.getChunkLoc(); + spongeChunk.loadChunk(true); + try { + boolean flag = !nmsWorld.provider.getHasNoSky(); + // Sections + net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) spongeChunk; + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + Map tiles = nmsChunk.getTileEntityMap(); + ClassInheritanceMultiMap[] entities = nmsChunk.getEntityLists(); + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Entry tile = iterator.next(); + BlockPos pos = tile.getKey(); + int lx = pos.getX() & 15; + int ly = pos.getY(); + int lz = pos.getZ() & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + int k = FaweCache.CACHE_J[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + if (array[k] != 0) { + iterator.remove(); + } + } + // Trim entities + for (int i = 0; i < 16; i++) { + if ((entities[i] != null) && (fs.getCount(i) >= 4096)) { + entities[i] = new ClassInheritanceMultiMap<>(Entity.class); + } + } + // Efficiently merge sections + for (int j = 0; j < sections.length; j++) { + if (fs.getCount(j) == 0) { + continue; + } + char[] newArray = fs.getIdArray(j); + if (newArray == null) { + continue; + } + ExtendedBlockStorage section = sections[j]; + if ((section == null) || (fs.getCount(j) >= 4096)) { + section = new ExtendedBlockStorage(j << 4, flag); + section.setData(newArray); + sections[j] = section; + continue; + } + char[] currentArray = section.getData(); + boolean fill = true; + for (int k = 0; k < newArray.length; k++) { + char n = newArray[k]; + switch (n) { + case 0: + fill = false; + continue; + case 1: + fill = false; + currentArray[k] = 0; + continue; + default: + currentArray[k] = n; + continue; + } + } + if (fill) { + fs.setCount(j, Short.MAX_VALUE); + } + } + // Clear + } catch (Throwable e) { + e.printStackTrace(); + } + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + spongeChunk.setBiome(x, z, SpongeUtil.getBiome(biome)); + } + } + } + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + sendChunk(fs); + } + }, 1); + return true; + } + + /** + * This should be overridden by any specialized queues. + * @param wrap + */ + @Override + public SpongeChunk_1_8 getChunk(ChunkLoc wrap) { + return new SpongeChunk_1_8(wrap); + } + + @Override + public boolean fixLighting(FaweChunk pc, boolean fixAll) { + try { + SpongeChunk_1_8 bc = (SpongeChunk_1_8) pc; + Chunk spongeChunk = bc.getChunk(); + net.minecraft.world.chunk.Chunk nmsChunk = (net.minecraft.world.chunk.Chunk) spongeChunk; + if (!spongeChunk.isLoaded()) { + if (!spongeChunk.loadChunk(false)) { + return false; + } + } else { + spongeChunk.unloadChunk(); + spongeChunk.loadChunk(false); + } + nmsChunk.generateSkylightMap(); + if (bc.getTotalRelight() == 0 && !fixAll) { + return true; + } + ExtendedBlockStorage[] sections = nmsChunk.getBlockStorageArray(); + net.minecraft.world.World nmsWorld = nmsChunk.getWorld(); + ChunkLoc loc = pc.getChunkLoc(); + + int X = loc.x << 4; + int Z = loc.z << 4; + + + for (int j = 0; j < sections.length; j++) { + ExtendedBlockStorage section = sections[j]; + if (section == null) { + continue; + } + if ((bc.getRelight(j) == 0 && !fixAll) || bc.getCount(j) == 0 || (bc.getCount(j) >= 4096 && bc.getAir(j) == 0)) { + continue; + } + char[] array = section.getData(); + int l = PseudoRandom.random.random(2); + for (int k = 0; k < array.length; k++) { + int i = array[k]; + if (i < 16) { + continue; + } + short id = (short) (i >> 4); + switch (id) { // Lighting + default: + if (!fixAll) { + continue; + } + if ((k & 1) == l) { + l = 1 - l; + continue; + } + case 10: + case 11: + case 39: + case 40: + case 50: + case 51: + case 62: + case 74: + case 76: + case 89: + case 122: + case 124: + case 130: + case 138: + case 169: + int x = FaweCache.CACHE_X[j][k]; + int y = FaweCache.CACHE_Y[j][k]; + int z = FaweCache.CACHE_Z[j][k]; + if (isSurrounded(sections, x, y, z)) { + continue; + } + BlockPos pos = new BlockPos(X + x, y, Z + z); + nmsWorld.checkLight(pos); + } + } + } + return true; + } catch (Throwable e) { + e.printStackTrace(); + } + return false; + } + + public boolean isSurrounded(ExtendedBlockStorage[] sections, int x, int y, int z) { + return isSolid(getId(sections, x, y + 1, z)) + && isSolid(getId(sections, x + 1, y - 1, z)) + && isSolid(getId(sections, x - 1, y, z)) + && isSolid(getId(sections, x, y, z + 1)) + && isSolid(getId(sections, x, y, z - 1)); + } + + public boolean isSolid(int i) { + return i != 0 && Block.getBlockById(i).isOpaqueCube(); + } + + public int getId(ExtendedBlockStorage[] sections, int x, int y, int z) { + if (x < 0 || x > 15 || z < 0 || z > 15) { + return 1; + } + if (y < 0 || y > 255) { + return 1; + } + int i = FaweCache.CACHE_I[y][x][z]; + ExtendedBlockStorage section = sections[i]; + if (section == null) { + return 0; + } + char[] array = section.getData(); + int j = FaweCache.CACHE_J[y][x][z]; + return array[j] >> 4; + } +} diff --git a/pom.xml b/pom.xml index 8b550179..2e6f5938 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ UTF-8 FastAsyncWorldEdit - 3.3.4 + 3.3.5 FastAsyncWorldEdit jar