From b5a8eb2176424a54a9a9cd326f3e28579293c8ec Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Fri, 13 May 2016 03:46:38 +1000 Subject: [PATCH] Various update to 1.9.4 progress notifications lighting fixes optimizations Only stable for bukkit 1.8/1.9 --- .../com/boydti/fawe/bukkit/BukkitPlayer.java | 25 + .../com/boydti/fawe/bukkit/FaweBukkit.java | 2 +- .../fawe/bukkit/logging/LoggingChangeSet.java | 1 + .../bukkit/v0/BukkitEditSessionWrapper_0.java | 7 +- .../boydti/fawe/bukkit/v0/BukkitQueue_0.java | 36 +- .../fawe/bukkit/v1_8/BukkitMain_18.java | 2 +- .../fawe/bukkit/v1_8/BukkitQueue18R3.java | 245 ++++--- bukkit18/src/main/resources/plugin.yml | 4 +- bukkit19/build.gradle | 2 +- .../fawe/bukkit/v1_9/BukkitChunk_1_9.java | 14 +- .../fawe/bukkit/v1_9/BukkitMain_19.java | 1 - .../fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java | 598 ++++++++---------- .../fawe/bukkit/v1_9/FaweAdapter_1_9.java | 373 +++++++++++ bukkit19/src/main/resources/plugin.yml | 4 +- core/src/main/java/com/boydti/fawe/Fawe.java | 2 +- .../main/java/com/boydti/fawe/FaweAPI.java | 128 ++-- .../main/java/com/boydti/fawe/FaweCache.java | 50 +- .../java/com/boydti/fawe/command/Cancel.java | 2 +- .../com/boydti/fawe/command/FixLighting.java | 26 +- .../com/boydti/fawe/command/Rollback.java | 3 +- .../main/java/com/boydti/fawe/config/BBC.java | 4 +- .../java/com/boydti/fawe/config/Settings.java | 49 +- .../boydti/fawe/example/CharFaweChunk.java | 20 +- .../boydti/fawe/example/MappedFaweQueue.java | 63 +- .../fawe/example/NMSMappedFaweQueue.java | 12 +- .../java/com/boydti/fawe/object/BytePair.java | 8 + .../fawe/object/EditSessionWrapper.java | 4 +- .../com/boydti/fawe/object/FaweChunk.java | 105 ++- .../com/boydti/fawe/object/FaweCommand.java | 35 +- .../com/boydti/fawe/object/FawePlayer.java | 20 +- .../com/boydti/fawe/object/NullChangeSet.java | 5 + .../object/brush/AbstractDelegateBrush.java | 4 + .../boydti/fawe/object/brush/HeightBrush.java | 126 +--- .../brush/heightmap/ArrayHeightMap.java | 8 +- .../object/brush/heightmap/HeightMap.java | 31 - .../brush/heightmap/ScalableHeightMap.java | 152 +++++ .../object/change/MutableChunkChange.java | 44 ++ .../object/change/MutableEntityChange.java | 34 + .../changeset/CPUOptimizedChangeSet.java | 72 +++ .../object/changeset/DiskStorageHistory.java | 41 +- .../fawe/object/changeset/FaweChangeSet.java | 201 +++--- .../object/changeset/FaweStreamChangeSet.java | 50 +- .../changeset/MemoryOptimizedHistory.java | 151 +++-- .../object/extent/FastWorldEditExtent.java | 1 + .../fawe/object/extent/ProcessedWEExtent.java | 4 +- .../progress/DefaultProgressTracker.java | 76 +++ .../java/com/boydti/fawe/util/FaweQueue.java | 28 +- .../java/com/boydti/fawe/util/MainUtil.java | 33 +- .../java/com/boydti/fawe/util/SetQueue.java | 5 +- .../java/com/boydti/fawe/util/StringMan.java | 8 + .../com/boydti/fawe/util/TaskManager.java | 3 +- .../java/com/boydti/fawe/util/WEManager.java | 65 +- .../boydti/fawe/wrappers/PlayerWrapper.java | 2 +- .../boydti/fawe/wrappers/WorldWrapper.java | 38 +- .../java/com/sk89q/worldedit/EditSession.java | 67 +- .../com/sk89q/worldedit/LocalSession.java | 41 +- .../command/composition/SelectionCommand.java | 2 +- .../extension/platform/CommandManager.java | 45 +- pom.xml | 7 + .../java/com/boydti/fawe/SpongeCommand.java | 4 +- .../fawe/sponge/v1_8/SpongeQueue_ALL.java | 2 +- 61 files changed, 2135 insertions(+), 1060 deletions(-) create mode 100644 bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/FaweAdapter_1_9.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/AbstractDelegateBrush.java delete mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java create mode 100644 core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java create mode 100644 core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedChangeSet.java create mode 100644 core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java index f0a964fd..b277fd53 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/BukkitPlayer.java @@ -3,14 +3,18 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; +import java.lang.reflect.Method; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; +import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; public class BukkitPlayer extends FawePlayer { + private static ConsoleCommandSender console; + public BukkitPlayer(final Player parent) { super(parent); } @@ -49,6 +53,27 @@ public class BukkitPlayer extends FawePlayer { } } + + @Override + public void resetTitle() { + sendTitle("",""); + } + + public void sendTitle(String title, String sub) { + try { + Method methodSendTitle = Player.class.getDeclaredMethod("sendTitle", String.class, String.class); + methodSendTitle.invoke(parent, ChatColor.GOLD + title, ChatColor.GOLD + sub); + return; + } catch (Throwable ignore) {} + if (console == null) { + console = Bukkit.getConsoleSender(); + Bukkit.getServer().dispatchCommand(console, "gamerule sendCommandFeedback false"); + Bukkit.getServer().dispatchCommand(console, "title " + getName() + " times 0 60 20"); + } + Bukkit.getServer().dispatchCommand(console, "title " + getName() + " subtitle [{\"text\":\"" + sub + "\",\"color\":\"gold\"}]"); + Bukkit.getServer().dispatchCommand(console, "title " + getName() + " title [{\"text\":\"" + title + "\",\"color\":\"gold\"}]"); + } + @Override public void sendMessage(final String message) { this.parent.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index c21af63e..fae13b97 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -195,7 +195,7 @@ public class FaweBukkit implements IFawe, Listener { debug("====== NO NMS BLOCK PLACER FOUND ======"); debug("FAWE couldn't find a fast block placer"); debug("Bukkit version: " + Bukkit.getVersion()); - debug("Supported NMS versions: 1.8, 1.9"); + debug("Supported NMS versions: 1.8.8, 1.9.4"); debug("Fallback placer: " + BukkitQueue_All.class); debug("======================================="); hasNMS = false; diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/logging/LoggingChangeSet.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/logging/LoggingChangeSet.java index 63236b7a..0649638d 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/logging/LoggingChangeSet.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/logging/LoggingChangeSet.java @@ -19,6 +19,7 @@ public class LoggingChangeSet extends FaweChangeSet { private final String name; public LoggingChangeSet(FawePlayer player, FaweChangeSet parent, IBlocksHubApi api) { + super(parent.getWorld()); this.parent = parent; this.name = player.getName(); this.api = api; diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java index 7b4e638d..f4c85b15 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitEditSessionWrapper_0.java @@ -22,12 +22,11 @@ public class BukkitEditSessionWrapper_0 extends EditSessionWrapper { } @Override - public Extent getHistoryExtent(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer player) { + public FaweChangeSet wrapChangeSet(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer player) { if (this.hook != null) { // If we are doing logging, use a custom logging ChangeSet - set = hook.getLoggingChangeSet(session, limit, parent, set, queue, player); + return hook.getLoggingChangeSet(session, limit, parent, set, queue, player); } - // Now return the normal history extent - return super.getHistoryExtent(session, limit, parent, set, queue, player); + return set; } } diff --git a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java index afac82cf..5392e4f9 100644 --- a/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java +++ b/bukkit0/src/main/java/com/boydti/fawe/bukkit/v0/BukkitQueue_0.java @@ -1,5 +1,6 @@ package com.boydti.fawe.bukkit.v0; +import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.NMSMappedFaweQueue; @@ -14,6 +15,10 @@ import com.sk89q.worldedit.world.biome.BaseBiome; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; @@ -21,18 +26,26 @@ import org.bukkit.block.Block; public abstract class BukkitQueue_0 extends NMSMappedFaweQueue { - public final BukkitImplAdapter adapter; + public Object adapter; public Method methodToNative; public Method methodFromNative; public BukkitQueue_0(final String world) { super(world); + setupAdapter(null); + } + + public void setupAdapter(BukkitImplAdapter adapter) { try { WorldEditPlugin instance = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); Field fieldAdapter = WorldEditPlugin.class.getDeclaredField("bukkitAdapter"); fieldAdapter.setAccessible(true); - this.adapter = (BukkitImplAdapter) fieldAdapter.get(instance); - for (Method method : adapter.getClass().getDeclaredMethods()) { + if ((this.adapter = adapter) != null) { + fieldAdapter.set(instance, adapter); + } else { + this.adapter = fieldAdapter.get(instance); + } + for (Method method : this.adapter.getClass().getDeclaredMethods()) { switch (method.getName()) { case "toNative": methodToNative = method; @@ -45,7 +58,12 @@ public abstract class BukkitQueue_0 extends NMSMa } } } catch (Throwable e) { - throw new RuntimeException(e); + e.printStackTrace(); + Fawe.debug("====== NO NATIVE WORLDEDIT ADAPTER ======"); + Fawe.debug("Try updating WorldEdit: "); + Fawe.debug(" - http://builds.enginehub.org/job/worldedit?branch=master"); + Fawe.debug("See also: http://wiki.sk89q.com/wiki/WorldEdit/Bukkit_adapters"); + Fawe.debug("========================================="); } } @@ -69,8 +87,14 @@ public abstract class BukkitQueue_0 extends NMSMa return world.regenerateChunk(x, z); } + @Override - public boolean fixLighting(FaweChunk fc, boolean fixAll) { + public CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTIONS sections, Map tiles, Collection[] entities, Set createdEntities, boolean all) throws Exception { + return fs; + } + + @Override + public boolean fixLighting(FaweChunk fc, RelightMode mode) { // Not implemented return true; } @@ -121,7 +145,7 @@ public abstract class BukkitQueue_0 extends NMSMa final Chunk chunk = fs.getChunk(); chunk.load(true); final World world = chunk.getWorld(); - char[][] sections = fs.getIdArrays(); + char[][] sections = fs.getCombinedIdArrays(); boolean done = false; boolean more = false; // Efficiently merge sections diff --git a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitMain_18.java b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitMain_18.java index 13e21240..efbf9a87 100644 --- a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitMain_18.java +++ b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitMain_18.java @@ -2,11 +2,11 @@ package com.boydti.fawe.bukkit.v1_8; import com.boydti.fawe.bukkit.ABukkitMain; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; -import com.boydti.fawe.bukkit.v1_8.BukkitQueue18R3; import com.boydti.fawe.object.EditSessionWrapper; import com.sk89q.worldedit.EditSession; public class BukkitMain_18 extends ABukkitMain { + @Override public BukkitQueue_0 getQueue(String world) { return new BukkitQueue18R3(world); diff --git a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java index 8e8788fb..a3d4ddfa 100644 --- a/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java +++ b/bukkit18/src/main/java/com/boydti/fawe/bukkit/v1_8/BukkitQueue18R3.java @@ -21,16 +21,19 @@ import com.sk89q.worldedit.internal.Constants; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.UUID; import net.minecraft.server.v1_8_R3.BlockPosition; import net.minecraft.server.v1_8_R3.ChunkCoordIntPair; import net.minecraft.server.v1_8_R3.ChunkSection; import net.minecraft.server.v1_8_R3.Entity; +import net.minecraft.server.v1_8_R3.EntityPlayer; import net.minecraft.server.v1_8_R3.EntityTypes; import net.minecraft.server.v1_8_R3.NBTTagCompound; -import net.minecraft.server.v1_8_R3.NBTTagInt; +import net.minecraft.server.v1_8_R3.NibbleArray; import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk; import net.minecraft.server.v1_8_R3.PlayerConnection; import net.minecraft.server.v1_8_R3.TileEntity; @@ -95,6 +98,71 @@ public class BukkitQueue18R3 extends BukkitQueue_0 tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + Collection[] entities = (Collection[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getChunk(fs.getX(), fs.getZ()); + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ChunkSection section = sections[layer]; + if (section != null) { + idPrevious[layer] = section.getIdArray().clone(); + short solid = 0; + for (int combined : idPrevious[layer]) { + if (combined > 1) { + solid++; + } + } + previous.count[layer] = solid; + previous.air[layer] = (short) (4096 - solid); + } + } + } + previous.ids = idPrevious; + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + tile.b(tag); // readTileEntityIntoTag + BlockPosition pos = entry.getKey(); + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag); + } + } + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && !createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.locX) & 15); + int z = ((int) Math.round(ent.locZ) & 15); + int y = (int) Math.round(ent.locY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityTypes.b(ent); + if (id != null) { + NBTTagCompound tag = new NBTTagCompound(); + ent.e(tag); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } + @Override public boolean setComponents(FaweChunk fc, RunnableVal changeTask) { CharFaweChunk fs = (CharFaweChunk) fc; @@ -108,54 +176,70 @@ public class BukkitQueue18R3 extends BukkitQueue_0 tiles = nmsChunk.getTileEntities(); Collection[] entities = nmsChunk.getEntitySlices(); - // Run change task if applicable - if (changeTask != null) { - CharFaweChunk previous = (CharFaweChunk) getChunk(fc.getX(), fc.getZ()); - char[][] idPrevious = new char[16][]; - for (int layer = 0; layer < sections.length; layer++) { - if (fs.getCount(layer) != 0) { - ChunkSection section = sections[layer]; - if (section != null) { - idPrevious[layer] = section.getIdArray().clone(); + // Remove entities + for (int i = 0; i < 16; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i].clear(); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; } - } - } - previous.ids = idPrevious; - for (Map.Entry entry : tiles.entrySet()) { - TileEntity tile = entry.getValue(); - NBTTagCompound tag = new NBTTagCompound(); - tile.b(tag); // ReadTileIntoTag - BlockPosition pos = entry.getKey(); - CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); - previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag); - } - for (Collection entityList : entities) { - for (Entity ent : entityList) { - int x = ((int) Math.round(ent.locX) & 15); - int z = ((int) Math.round(ent.locZ) & 15); - int y = (int) Math.round(ent.locY); - int i = FaweCache.CACHE_I[y][x][z]; - char[] array = fs.getIdArray(i); + int x = ((int) Math.round(entity.locX) & 15); + int z = ((int) Math.round(entity.locZ) & 15); + int y = (int) Math.round(entity.locY); if (array == null) { continue; } int j = FaweCache.CACHE_J[y][x][z]; if (array[j] != 0) { - String id = EntityTypes.b(ent); - if (id != null) { - NBTTagCompound tag = new NBTTagCompound(); - ent.e(tag); - CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); - Map map = ReflectionUtils.getMap(nativeTag.getValue()); - map.put("Id", new StringTag(id)); - previous.setEntity(nativeTag); - } + nmsWorld.removeEntity(entity); } } } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = nativeTag.getValue(); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + Entity entity = EntityTypes.createEntityByName(id, nmsWorld); + if (entity != null) { + if (nativeTag != null) { + NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(adapter, nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + createdEntities.add(entity.getUniqueID()); + } + } + // Run change task if applicable + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); changeTask.run(previous); } - // Trim tiles Set> entryset = tiles.entrySet(); Iterator> iterator = entryset.iterator(); @@ -175,31 +259,17 @@ public class BukkitQueue18R3 extends BukkitQueue_0= 4096) { - entities[i].clear(); - } else { - char[] array = fs.getIdArray(i); + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { Collection ents = new ArrayList<>(entities[i]); for (Entity entity : ents) { - int x = ((int) Math.round(entity.locX) & 15); - int z = ((int) Math.round(entity.locZ) & 15); - int y = (int) Math.round(entity.locY); - if (array == null) { - continue; - } - int j = FaweCache.CACHE_J[y][x][z]; - if (array[j] != 0) { + if (entsToRemove.contains(entity.getUniqueID())) { nmsWorld.removeEntity(entity); } } } } - // Set blocks for (int j = 0; j < sections.length; j++) { if (fs.getCount(j) == 0) { @@ -256,53 +326,19 @@ public class BukkitQueue18R3 extends BukkitQueue_0 tilesToSpawn = fs.getTiles(); - BlockPosition.MutableBlockPosition mutablePos = new BlockPosition.MutableBlockPosition(0, 0, 0); int bx = fs.getX() << 4; int bz = fs.getZ() << 4; for (Map.Entry entry : tilesToSpawn.entrySet()) { CompoundTag nativeTag = entry.getValue(); BytePair pair = entry.getKey(); - mutablePos.c(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos - TileEntity tileEntity = nmsWorld.getTileEntity(mutablePos); + BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); - tag.set("x", new NBTTagInt(mutablePos.getX())); - tag.set("y", new NBTTagInt(mutablePos.getY())); - tag.set("z", new NBTTagInt(mutablePos.getZ())); tileEntity.a(tag); // ReadTagIntoTile } } - // Set entities - Set entitiesToSpawn = fs.getEntities(); - for (CompoundTag nativeTag : entitiesToSpawn) { - Map entityTagMap = nativeTag.getValue(); - StringTag idTag = (StringTag) entityTagMap.get("Id"); - ListTag posTag = (ListTag) entityTagMap.get("Pos"); - ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); - if (idTag == null || posTag == null || rotTag == null) { - Fawe.debug("Unknown entity tag: " + nativeTag); - continue; - } - double x = posTag.getDouble(0); - double y = posTag.getDouble(1); - double z = posTag.getDouble(2); - float yaw = rotTag.getFloat(0); - float pitch = rotTag.getFloat(1); - String id = idTag.getValue(); - Entity entity = EntityTypes.createEntityByName(id, nmsWorld); - if (entity != null) { - if (nativeTag != null) { - NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(adapter, nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - entity.f(tag); - } - entity.setLocation(x, y, z, yaw, pitch); - nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); - } - } } catch (Throwable e) { e.printStackTrace(); } @@ -339,8 +375,8 @@ public class BukkitQueue18R3 extends BukkitQueue_0> 4; int pz = loc.z >> 4; - int dx = Math.abs(cx - (loc.x >> 4)); - int dz = Math.abs(cz - (loc.z >> 4)); + int dx = Math.abs(cx - (px)); + int dz = Math.abs(cz - (pz)); if ((dx > view) || (dz > view)) { continue; } @@ -350,7 +386,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0 fc = (CharFaweChunk) chunk; CraftChunk craftChunk = (CraftChunk) fc.getChunk(); @@ -358,11 +394,22 @@ public class BukkitQueue18R3 extends BukkitQueue_0= 4096 && fc.getAir(j) == 0)) { + if ((fc.getRelight(j) == 0 && mode == RelightMode.MINIMAL) || (fc.getCount(j) == 0 && mode != RelightMode.ALL) || (fc.getCount(j) >= 4096 && fc.getAir(j) == 0)) { continue; } char[] array = section.getIdArray(); @@ -387,7 +434,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0> 4); switch (id) { // Lighting default: - if (!fixAll) { + if (mode == RelightMode.MINIMAL) { continue; } if ((k & 1) == l) { diff --git a/bukkit18/src/main/resources/plugin.yml b/bukkit18/src/main/resources/plugin.yml index 56591a6a..50b71af8 100644 --- a/bukkit18/src/main/resources/plugin.yml +++ b/bukkit18/src/main/resources/plugin.yml @@ -21,9 +21,9 @@ commands: fawe: description: (FAWE) Reload the plugin aliases: [/fawe,/fawereload] - wrg: + select: description: (FAWE) Select your current WorldEdit Region. - aliases: [/wrg,wer,/wer,worldeditregion,/worldeditregion,/region] + aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region] frb: description: (FAWE) Rollback an edit aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] diff --git a/bukkit19/build.gradle b/bukkit19/build.gradle index e91426b5..65bc7a70 100644 --- a/bukkit19/build.gradle +++ b/bukkit19/build.gradle @@ -1,6 +1,6 @@ dependencies { compile project(':bukkit0') - compile 'org.bukkit.craftbukkit:CraftBukkit:1.9.2' + compile 'org.bukkit.craftbukkit.v1_9R2:craftbukkitv1_9R2:1.9.4' } apply plugin: 'com.github.johnrengelman.shadow' diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java index 3854f6e3..5d29437e 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitChunk_1_9.java @@ -5,12 +5,12 @@ import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.util.FaweQueue; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import net.minecraft.server.v1_9_R1.Block; -import net.minecraft.server.v1_9_R1.DataBits; -import net.minecraft.server.v1_9_R1.DataPalette; -import net.minecraft.server.v1_9_R1.DataPaletteBlock; -import net.minecraft.server.v1_9_R1.DataPaletteGlobal; -import net.minecraft.server.v1_9_R1.IBlockData; +import net.minecraft.server.v1_9_R2.Block; +import net.minecraft.server.v1_9_R2.DataBits; +import net.minecraft.server.v1_9_R2.DataPalette; +import net.minecraft.server.v1_9_R2.DataPaletteBlock; +import net.minecraft.server.v1_9_R2.DataPaletteGlobal; +import net.minecraft.server.v1_9_R2.IBlockData; import org.bukkit.Chunk; public class BukkitChunk_1_9 extends CharFaweChunk { @@ -101,7 +101,7 @@ public class BukkitChunk_1_9 extends CharFaweChunk { if (sectionPalettes != null) { return; } - char[][] arrays = getIdArrays(); + char[][] arrays = getCombinedIdArrays(); IBlockData lastBlock = null; char lastChar = Character.MAX_VALUE; for (int layer = 0; layer < 16; layer++) { diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitMain_19.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitMain_19.java index b941b28f..f1947e25 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitMain_19.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitMain_19.java @@ -2,7 +2,6 @@ package com.boydti.fawe.bukkit.v1_9; import com.boydti.fawe.bukkit.ABukkitMain; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; -import com.boydti.fawe.bukkit.v1_9.BukkitQueue_1_9_R1; import com.boydti.fawe.object.EditSessionWrapper; import com.sk89q.worldedit.EditSession; diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java index 0c232c3b..1b451f61 100644 --- a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/BukkitQueue_1_9_R1.java @@ -4,44 +4,50 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.BytePair; import com.boydti.fawe.object.FaweChunk; -import com.boydti.fawe.object.FawePlayer; -import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.RunnableVal; -import com.boydti.fawe.util.MemUtil; -import com.sk89q.worldedit.LocalSession; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.internal.Constants; import java.lang.reflect.Constructor; 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 net.minecraft.server.v1_9_R1.Block; -import net.minecraft.server.v1_9_R1.BlockPosition; -import net.minecraft.server.v1_9_R1.Blocks; -import net.minecraft.server.v1_9_R1.ChunkSection; -import net.minecraft.server.v1_9_R1.DataBits; -import net.minecraft.server.v1_9_R1.DataPalette; -import net.minecraft.server.v1_9_R1.DataPaletteBlock; -import net.minecraft.server.v1_9_R1.IBlockData; -import net.minecraft.server.v1_9_R1.TileEntity; -import org.bukkit.Bukkit; +import java.util.Set; +import java.util.UUID; +import net.minecraft.server.v1_9_R2.Block; +import net.minecraft.server.v1_9_R2.BlockPosition; +import net.minecraft.server.v1_9_R2.Blocks; +import net.minecraft.server.v1_9_R2.ChunkSection; +import net.minecraft.server.v1_9_R2.DataBits; +import net.minecraft.server.v1_9_R2.DataPalette; +import net.minecraft.server.v1_9_R2.DataPaletteBlock; +import net.minecraft.server.v1_9_R2.Entity; +import net.minecraft.server.v1_9_R2.EntityPlayer; +import net.minecraft.server.v1_9_R2.EntityTypes; +import net.minecraft.server.v1_9_R2.IBlockData; +import net.minecraft.server.v1_9_R2.NBTTagCompound; +import net.minecraft.server.v1_9_R2.NibbleArray; +import net.minecraft.server.v1_9_R2.TileEntity; import org.bukkit.Chunk; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Biome; -import org.bukkit.craftbukkit.v1_9_R1.CraftChunk; -import org.bukkit.entity.Entity; +import org.bukkit.craftbukkit.v1_9_R2.CraftChunk; import org.bukkit.entity.Player; -import org.bukkit.generator.BlockPopulator; -import org.bukkit.generator.ChunkGenerator; +import org.bukkit.event.entity.CreatureSpawnEvent; public class BukkitQueue_1_9_R1 extends BukkitQueue_0 { @@ -53,9 +59,16 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0= 4096) && (bc.getAir(j) == 0))) { + if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0))) { continue; } final char[] array = bc.getIdArray(j); @@ -136,7 +159,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 changeTask) { - // TODO change task - { - // blah, stuff + public CharFaweChunk getPrevious(CharFaweChunk fs, ChunkSection[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + Collection[] entities = (Collection[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getChunk(fs.getX(), fs.getZ()); + // Copy blocks + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ChunkSection section = sections[layer]; + if (section != null) { + short solid = 0; + char[] previousLayer = idPrevious[layer] = new char[4096]; + DataPaletteBlock blocks = section.getBlocks(); + for (int j = 0; j < 4096; j++) { + int x = FaweCache.CACHE_X[0][j]; + int y = FaweCache.CACHE_Y[0][j]; + int z = FaweCache.CACHE_Z[0][j]; + IBlockData ibd = blocks.a(x, y, z); + Block block = ibd.getBlock(); + int combined = Block.getId(block); + if (FaweCache.hasData(combined)) { + combined = (combined << 4) + block.toLegacyData(ibd); + } else { + combined = combined << 4; + } + if (combined > 1) { + solid++; + } + previousLayer[j] = (char) combined; + } + previous.count[layer] = solid; + previous.air[layer] = (short) (4096 - solid); + } + } } + previous.ids = idPrevious; + // Copy tiles + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + tile.save(tag); // readTagIntoEntity + BlockPosition pos = entry.getKey(); + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag); + } + } + // Copy entities + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && !createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.locX) & 15); + int z = ((int) Math.round(ent.locZ) & 15); + int y = (int) Math.round(ent.locY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityTypes.b(ent); + if (id != null) { + NBTTagCompound tag = new NBTTagCompound(); + ent.e(tag); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } - final BukkitChunk_1_9 fs = (BukkitChunk_1_9) pc; + @Override + public boolean setComponents(final FaweChunk fc, RunnableVal changeTask) { + final BukkitChunk_1_9 fs = (BukkitChunk_1_9) fc; final Chunk chunk = (Chunk) fs.getChunk(); final World world = chunk.getWorld(); chunk.load(true); try { final boolean flag = world.getEnvironment() == Environment.NORMAL; - - // Sections - net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle(); - net.minecraft.server.v1_9_R1.World w = c.world; - ChunkSection[] sections = c.getSections(); - - Class clazzChunk = c.getClass(); + net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); + net.minecraft.server.v1_9_R2.World nmsWorld = nmsChunk.world; + ChunkSection[] sections = nmsChunk.getSections(); + Class clazzChunk = nmsChunk.getClass(); final Field ef = clazzChunk.getDeclaredField("entitySlices"); - final Collection[] entities = (Collection[]) ef.get(c); - - // Trim tiles - boolean removed = false; - Map tiles = c.getTileEntities(); - if (fs.getTotalCount() >= 65536) { - tiles.clear(); - removed = true; - } else { - Iterator> iter = tiles.entrySet().iterator(); - while (iter.hasNext()) { - Entry tile = iter.next(); - BlockPosition pos = tile.getKey(); - final int lx = pos.getX() & 15; - final int ly = pos.getY(); - final int lz = pos.getZ() & 15; - final int j = FaweCache.CACHE_I[ly][lx][lz]; - final int k = FaweCache.CACHE_J[ly][lx][lz]; - final char[] array = fs.getIdArray(j); - if (array == null) { - continue; - } - if (array[k] != 0) { - removed = true; - iter.remove(); - } - } - } - - // Trim entities - for (int i = 0; i < 16; i++) { - if ((entities[i] != null) && (fs.getCount(i) >= 4096)) { + final Collection[] entities = (Collection[]) ef.get(nmsChunk); + Map tiles = nmsChunk.getTileEntities(); + // Remove entities + for (int i = 0; i < entities.length; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { entities[i].clear(); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; + } + int x = ((int) Math.round(entity.locX) & 15); + int z = ((int) Math.round(entity.locZ) & 15); + int y = (int) Math.round(entity.locY); + if (array == null || y < 0 || y > 255) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + nmsWorld.removeEntity(entity); + } + } } } - // Efficiently merge sections + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entsToRemove.contains(entity.getUniqueID())) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = ReflectionUtils.getMap(nativeTag.getValue()); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + Entity entity = EntityTypes.createEntityByName(id, nmsWorld); + if (entity != null) { + UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + createdEntities.add(entity.getUniqueID()); + } + } + // Change task? + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); + changeTask.run(previous); + } + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + BlockPosition 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]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lx][lz]; + if (array[k] != 0) { + iterator.remove(); + } + } + // Set blocks for (int j = 0; j < sections.length; j++) { int count = fs.getCount(j); if (count == 0) { @@ -367,7 +527,38 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 tilesToSpawn = fs.getTiles(); + int bx = fs.getX() << 4; + int bz = fs.getZ() << 4; + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); + tileEntity.a(tag); // ReadTagIntoTile + } + } } catch (Throwable e) { e.printStackTrace(); } @@ -392,117 +583,9 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 - * - You can disable this in the conifg
- * - Will try to free up some memory
- * - Clears the queue
- * - Clears worldedit history
- * - Clears entities
- * - Unloads chunks in vacant worlds
- * - Unloads non visible chunks
- */ - @Override - public void saveMemory() { - super.saveMemory(); - // Clear the queue - super.clear(); - ArrayDeque toUnload = new ArrayDeque<>(); - final int distance = Bukkit.getViewDistance() + 2; - HashMap> players = new HashMap<>(); - for (final Player player : Bukkit.getOnlinePlayers()) { - // Clear history - final FawePlayer fp = FawePlayer.wrap(player); - final LocalSession s = fp.getSession(); - if (s != null) { - s.clearHistory(); - s.setClipboard(null); - } - final Location loc = player.getLocation(); - final World worldObj = loc.getWorld(); - final String world = worldObj.getName(); - HashMap map = players.get(world); - if (map == null) { - map = new HashMap<>(); - players.put(world, map); - } - final IntegerPair origin = new IntegerPair(loc.getBlockX() >> 4, loc.getBlockZ() >> 4); - Integer val = map.get(origin); - int check; - if (val != null) { - if (val == distance) { - continue; - } - check = distance - val; - } else { - check = distance; - map.put(origin, distance); - } - for (int x = -distance; x <= distance; x++) { - if ((x >= check) || (-x >= check)) { - continue; - } - for (int z = -distance; z <= distance; z++) { - if ((z >= check) || (-z >= check)) { - continue; - } - final int weight = distance - Math.max(Math.abs(x), Math.abs(z)); - final IntegerPair chunk = new IntegerPair(x + origin.x, z + origin.z); - val = map.get(chunk); - if ((val == null) || (val < weight)) { - map.put(chunk, weight); - } - } - } - } - Fawe.get().getWorldEdit().clearSessions(); - for (final World world : Bukkit.getWorlds()) { - final String name = world.getName(); - final HashMap map = players.get(name); - if ((map == null) || (map.size() == 0)) { - final boolean save = world.isAutoSave(); - world.setAutoSave(false); - for (final Chunk chunk : world.getLoadedChunks()) { - this.unloadChunk(name, chunk); - } - world.setAutoSave(save); - continue; - } - final Chunk[] chunks = world.getLoadedChunks(); - for (final Chunk chunk : chunks) { - final int x = chunk.getX(); - final int z = chunk.getZ(); - if (!map.containsKey(new IntegerPair(x, z))) { - toUnload.add(chunk); - } else if (chunk.getEntities().length > 4096) { - for (final Entity ent : chunk.getEntities()) { - ent.remove(); - } - } - } - } - // GC again - System.gc(); - System.gc(); - // If still critical memory - int free = MemUtil.calculateMemory(); - if (free <= 1) { - for (final Chunk chunk : toUnload) { - this.unloadChunk(chunk.getWorld().getName(), chunk); - } - } else if (free == Integer.MAX_VALUE) { - for (final Chunk chunk : toUnload) { - chunk.unload(true, false); - } - } else { - return; - } - toUnload = null; - players = null; - } - + @Deprecated public boolean unloadChunk(final String world, final Chunk chunk) { - net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle(); + net.minecraft.server.v1_9_R2.Chunk c = ((CraftChunk) chunk).getHandle(); c.mustSave = false; if (chunk.isLoaded()) { chunk.unload(false, false); @@ -510,133 +593,6 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0 clazz = world.getClass(); - final Field generator = clazz.getDeclaredField("generator"); - generator.setAccessible(true); - generator.set(world, newGen); - - final Field wf = clazz.getDeclaredField("world"); - wf.setAccessible(true); - final Object w = wf.get(world); - final Class clazz2 = w.getClass().getSuperclass(); - final Field generator2 = clazz2.getDeclaredField("generator"); - generator2.set(w, newGen); - - return gen; - } catch (final Exception e) { - e.printStackTrace(); - } - return null; - } - - public List setPopulator(final World world, final List newPop) { - try { - final List pop = world.getPopulators(); - final Field populators = world.getClass().getDeclaredField("populators"); - populators.setAccessible(true); - populators.set(world, newPop); - return pop; - } catch (final Exception e) { - e.printStackTrace(); - } - return null; - } - - public void setEntitiesAndTiles(final Chunk chunk, final List[] entities, final Map tiles) { - try { - final Class clazz = chunk.getClass(); - final Method handle = clazz.getMethod("getHandle"); - final Object c = handle.invoke(chunk); - final Class clazz2 = c.getClass(); - - if (tiles.size() > 0) { - final Field tef = clazz2.getDeclaredField("tileEntities"); - final Map te = (Map) tef.get(c); - final Method put = te.getClass().getMethod("putAll", Map.class); - put.invoke(te, tiles); - } - - final Field esf = clazz2.getDeclaredField("entitySlices"); - esf.setAccessible(true); - esf.set(c, entities); - } catch (final Exception e) { - e.printStackTrace(); - } - } - - public Object getProvider(final World world) { - try { - // Provider 1 - final Class clazz = world.getClass(); - final Field wf = clazz.getDeclaredField("world"); - wf.setAccessible(true); - final Object w = wf.get(world); - final Field provider = w.getClass().getSuperclass().getDeclaredField("chunkProvider"); - provider.setAccessible(true); - // ChunkProviderServer - final Class clazz2 = w.getClass(); - final Field wpsf = clazz2.getDeclaredField("chunkProviderServer"); - // Store old provider server - final Object worldProviderServer = wpsf.get(w); - // Store the old provider - final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider"); - return cp.get(worldProviderServer); - } catch (final Exception e) { - e.printStackTrace(); - } - return null; - } - - public Object setProvider(final World world, Object newProvider) { - try { - // Provider 1 - final Class clazz = world.getClass(); - final Field wf = clazz.getDeclaredField("world"); - wf.setAccessible(true); - final Object w = wf.get(world); - // ChunkProviderServer - final Class clazz2 = w.getClass(); - final Field wpsf = clazz2.getDeclaredField("chunkProviderServer"); - // Store old provider server - final Object worldProviderServer = wpsf.get(w); - // Store the old provider - final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider"); - final Object oldProvider = cp.get(worldProviderServer); - // Provider 2 - final Class clazz3 = worldProviderServer.getClass(); - final Field provider2 = clazz3.getDeclaredField("chunkProvider"); - // If the provider needs to be calculated - if (newProvider == null) { - Method k; - try { - k = clazz2.getDeclaredMethod("k"); - } catch (final Throwable e) { - try { - k = clazz2.getDeclaredMethod("j"); - } catch (final Throwable e2) { - e2.printStackTrace(); - return null; - } - } - k.setAccessible(true); - final Object tempProviderServer = k.invoke(w); - newProvider = cp.get(tempProviderServer); - // Restore old provider - wpsf.set(w, worldProviderServer); - } - // Set provider for provider server - provider2.set(worldProviderServer, newProvider); - // Return the previous provider - return oldProvider; - } catch (final Exception e) { - e.printStackTrace(); - } - return null; - } - @Override public FaweChunk getChunk(int x, int z) { return new BukkitChunk_1_9(this, x, z); diff --git a/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/FaweAdapter_1_9.java b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/FaweAdapter_1_9.java new file mode 100644 index 00000000..65f39edf --- /dev/null +++ b/bukkit19/src/main/java/com/boydti/fawe/bukkit/v1_9/FaweAdapter_1_9.java @@ -0,0 +1,373 @@ +package com.boydti.fawe.bukkit.v1_9; + +import com.google.common.base.Preconditions; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.internal.Constants; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +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 net.minecraft.server.v1_9_R2.BiomeBase; +import net.minecraft.server.v1_9_R2.BlockPosition; +import net.minecraft.server.v1_9_R2.EntityTypes; +import net.minecraft.server.v1_9_R2.NBTBase; +import net.minecraft.server.v1_9_R2.NBTTagByte; +import net.minecraft.server.v1_9_R2.NBTTagByteArray; +import net.minecraft.server.v1_9_R2.NBTTagCompound; +import net.minecraft.server.v1_9_R2.NBTTagDouble; +import net.minecraft.server.v1_9_R2.NBTTagEnd; +import net.minecraft.server.v1_9_R2.NBTTagFloat; +import net.minecraft.server.v1_9_R2.NBTTagInt; +import net.minecraft.server.v1_9_R2.NBTTagIntArray; +import net.minecraft.server.v1_9_R2.NBTTagList; +import net.minecraft.server.v1_9_R2.NBTTagLong; +import net.minecraft.server.v1_9_R2.NBTTagShort; +import net.minecraft.server.v1_9_R2.NBTTagString; +import net.minecraft.server.v1_9_R2.TileEntity; +import net.minecraft.server.v1_9_R2.World; +import net.minecraft.server.v1_9_R2.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_9_R2.CraftServer; +import org.bukkit.craftbukkit.v1_9_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_9_R2.block.CraftBlock; +import org.bukkit.craftbukkit.v1_9_R2.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent; + +public final class FaweAdapter_1_9 implements BukkitImplAdapter +{ + private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + private final Field nbtListTagListField; + private final Method nbtCreateTagMethod; + + public FaweAdapter_1_9() + throws NoSuchFieldException, NoSuchMethodException + { + CraftServer.class.cast(Bukkit.getServer()); + + this.nbtListTagListField = NBTTagList.class.getDeclaredField("list"); + this.nbtListTagListField.setAccessible(true); + + this.nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", new Class[] { Byte.TYPE }); + this.nbtCreateTagMethod.setAccessible(true); + } + + private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity) + { + tileEntity.a(tag); + } + + private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag) + { + tileEntity.save(tag); + } + + @Nullable + private static String getEntityId(net.minecraft.server.v1_9_R2.Entity entity) + { + return EntityTypes.b(entity); + } + + @Nullable + private static net.minecraft.server.v1_9_R2.Entity createEntityFromId(String id, World world) + { + return EntityTypes.createEntityByName(id, world); + } + + private static void readTagIntoEntity(NBTTagCompound tag, net.minecraft.server.v1_9_R2.Entity entity) + { + entity.f(tag); + } + + private static void readEntityIntoTag(net.minecraft.server.v1_9_R2.Entity entity, NBTTagCompound tag) + { + entity.e(tag); + } + + public int getBlockId(Material material) + { + return material.getId(); + } + + public Material getMaterial(int id) + { + return Material.getMaterial(id); + } + + public int getBiomeId(Biome biome) + { + BiomeBase mcBiome = CraftBlock.biomeToBiomeBase(biome); + return mcBiome != null ? BiomeBase.a(mcBiome) : 0; + } + + public Biome getBiome(int id) + { + BiomeBase mcBiome = BiomeBase.getBiome(id); + return CraftBlock.biomeBaseToBiome(mcBiome); + } + + public BaseBlock getBlock(Location location) + { + Preconditions.checkNotNull(location); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + Block bukkitBlock = location.getBlock(); + BaseBlock block = new BaseBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); + + TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z)); + if (te != null) + { + NBTTagCompound tag = new NBTTagCompound(); + readTileEntityIntoTag(te, tag); + block.setNbtData((CompoundTag)toNative(tag)); + } + return block; + } + + public boolean setBlock(Location location, BaseBlock block, boolean notifyAndLight) + { + Preconditions.checkNotNull(location); + Preconditions.checkNotNull(block); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + boolean changed = location.getBlock().setTypeIdAndData(block.getId(), (byte)block.getData(), notifyAndLight); + + CompoundTag nativeTag = block.getNbtData(); + if (nativeTag != null) + { + TileEntity tileEntity = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z)); + if (tileEntity != null) + { + NBTTagCompound tag = (NBTTagCompound)fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + readTagIntoTileEntity(tag, tileEntity); + } + } + return changed; + } + + public BaseEntity getEntity(org.bukkit.entity.Entity entity) + { + Preconditions.checkNotNull(entity); + + CraftEntity craftEntity = (CraftEntity)entity; + net.minecraft.server.v1_9_R2.Entity mcEntity = craftEntity.getHandle(); + + String id = getEntityId(mcEntity); + if (id != null) + { + NBTTagCompound tag = new NBTTagCompound(); + readEntityIntoTag(mcEntity, tag); + return new BaseEntity(id, (CompoundTag)toNative(tag)); + } + return null; + } + + @Nullable + public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) + { + Preconditions.checkNotNull(location); + Preconditions.checkNotNull(state); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + WorldServer worldServer = craftWorld.getHandle(); + + net.minecraft.server.v1_9_R2.Entity createdEntity = createEntityFromId(state.getTypeId(), craftWorld.getHandle()); + if (createdEntity != null) + { + CompoundTag nativeTag = state.getNbtData(); + if (nativeTag != null) + { + NBTTagCompound tag = (NBTTagCompound)fromNative(nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + readTagIntoEntity(tag, createdEntity); + } + createdEntity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + worldServer.addEntity(createdEntity, CreatureSpawnEvent.SpawnReason.CUSTOM); + return createdEntity.getBukkitEntity(); + } + return null; + } + + private Tag toNative(NBTBase foreign) + { + if (foreign == null) { + return null; + } + if ((foreign instanceof NBTTagCompound)) + { + Map values = new HashMap(); + Set foreignKeys = ((NBTTagCompound)foreign).c(); + for (String str : foreignKeys) + { + NBTBase base = ((NBTTagCompound)foreign).get(str); + values.put(str, toNative(base)); + } + return new CompoundTag(values); + } + if ((foreign instanceof NBTTagByte)) { + return new ByteTag(((NBTTagByte)foreign).f()); + } + if ((foreign instanceof NBTTagByteArray)) { + return new ByteArrayTag(((NBTTagByteArray)foreign).c()); + } + if ((foreign instanceof NBTTagDouble)) { + return new DoubleTag(((NBTTagDouble)foreign).g()); + } + if ((foreign instanceof NBTTagFloat)) { + return new FloatTag(((NBTTagFloat)foreign).h()); + } + if ((foreign instanceof NBTTagInt)) { + return new IntTag(((NBTTagInt)foreign).d()); + } + if ((foreign instanceof NBTTagIntArray)) { + return new IntArrayTag(((NBTTagIntArray)foreign).c()); + } + if ((foreign instanceof NBTTagList)) { + try + { + return toNativeList((NBTTagList)foreign); + } + catch (Throwable e) + { + this.logger.log(Level.WARNING, "Failed to convert NBTTagList", e); + return new ListTag(ByteTag.class, new ArrayList()); + } + } + if ((foreign instanceof NBTTagLong)) { + return new LongTag(((NBTTagLong)foreign).c()); + } + if ((foreign instanceof NBTTagShort)) { + return new ShortTag(((NBTTagShort)foreign).e()); + } + if ((foreign instanceof NBTTagString)) { + return new StringTag(((NBTTagString)foreign).a_()); + } + if ((foreign instanceof NBTTagEnd)) { + return new EndTag(); + } + throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); + } + + private ListTag toNativeList(NBTTagList foreign) + throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException + { + List values = new ArrayList(); + int type = foreign.d(); + + List foreignList = (List)this.nbtListTagListField.get(foreign); + for (int i = 0; i < foreign.size(); i++) + { + NBTBase element = (NBTBase)foreignList.get(i); + values.add(toNative(element)); + } + Class cls = NBTConstants.getClassFromType(type); + return new ListTag(cls, values); + } + + private NBTBase fromNative(Tag foreign) + { + if (foreign == null) { + return null; + } + Map.Entry entry; + if ((foreign instanceof CompoundTag)) + { + NBTTagCompound tag = new NBTTagCompound(); + for (Iterator localIterator = ((CompoundTag)foreign) + .getValue().entrySet().iterator(); localIterator.hasNext();) + { + entry = (Map.Entry)localIterator.next(); + + tag.set((String)entry.getKey(), fromNative((Tag)entry.getValue())); + } + return tag; + } + if ((foreign instanceof ByteTag)) { + return new NBTTagByte(((ByteTag)foreign).getValue().byteValue()); + } + if ((foreign instanceof ByteArrayTag)) { + return new NBTTagByteArray(((ByteArrayTag)foreign).getValue()); + } + if ((foreign instanceof DoubleTag)) { + return new NBTTagDouble(((DoubleTag)foreign).getValue().doubleValue()); + } + if ((foreign instanceof FloatTag)) { + return new NBTTagFloat(((FloatTag)foreign).getValue().floatValue()); + } + if ((foreign instanceof IntTag)) { + return new NBTTagInt(((IntTag)foreign).getValue().intValue()); + } + if ((foreign instanceof IntArrayTag)) { + return new NBTTagIntArray(((IntArrayTag)foreign).getValue()); + } + if ((foreign instanceof ListTag)) + { + NBTTagList tag = new NBTTagList(); + ListTag foreignList = (ListTag)foreign; + for (Tag t : foreignList.getValue()) { + tag.add(fromNative(t)); + } + return tag; + } + if ((foreign instanceof LongTag)) { + return new NBTTagLong(((LongTag)foreign).getValue().longValue()); + } + if ((foreign instanceof ShortTag)) { + return new NBTTagShort(((ShortTag)foreign).getValue().shortValue()); + } + if ((foreign instanceof StringTag)) { + return new NBTTagString(((StringTag)foreign).getValue()); + } + if ((foreign instanceof EndTag)) { + try + { + return (NBTBase)this.nbtCreateTagMethod.invoke(null, new Object[] { Byte.valueOf((byte) 0) }); + } + catch (Exception e) + { + return null; + } + } + throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); + } +} diff --git a/bukkit19/src/main/resources/plugin.yml b/bukkit19/src/main/resources/plugin.yml index d77c8ac4..91d1012b 100644 --- a/bukkit19/src/main/resources/plugin.yml +++ b/bukkit19/src/main/resources/plugin.yml @@ -21,9 +21,9 @@ commands: fawe: description: (FAWE) Reload the plugin aliases: [/fawe,/fawereload] - wrg: + select: description: (FAWE) Select your current WorldEdit Region. - aliases: [/wrg,wer,/wer,worldeditregion,/worldeditregion,/region] + aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region] frb: description: (FAWE) Rollback an edit aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] diff --git a/core/src/main/java/com/boydti/fawe/Fawe.java b/core/src/main/java/com/boydti/fawe/Fawe.java index aa1df426..0078d932 100644 --- a/core/src/main/java/com/boydti/fawe/Fawe.java +++ b/core/src/main/java/com/boydti/fawe/Fawe.java @@ -200,7 +200,7 @@ public class Fawe { this.IMP.setupCommand("wea", new Wea()); this.IMP.setupCommand("fixlighting", new FixLighting()); this.IMP.setupCommand("stream", new Stream()); - this.IMP.setupCommand("wrg", new WorldEditRegion()); + this.IMP.setupCommand("select", new WorldEditRegion()); this.IMP.setupCommand("fawe", new Reload()); this.IMP.setupCommand("frb", new Rollback()); this.IMP.setupCommand("fcancel", new Cancel()); diff --git a/core/src/main/java/com/boydti/fawe/FaweAPI.java b/core/src/main/java/com/boydti/fawe/FaweAPI.java index 95ca3f78..27d7ab09 100644 --- a/core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -18,6 +18,7 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; @@ -39,6 +40,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.zip.GZIPInputStream; +import javax.annotation.Nonnull; import org.bukkit.Chunk; import org.bukkit.Location; @@ -51,6 +53,31 @@ import org.bukkit.Location; */ public class FaweAPI { + /** + * Get a new EditSessionfor a player
+ * - The EditSession can be used from another thread
+ * - FAWE will handle + * @see com.boydti.fawe.object.FawePlayer#wrap(Object) + * @param player + * @return + */ + public EditSession getNewEditSession(@Nonnull FawePlayer player) { + if (player == null) { + throw new IllegalArgumentException("Player may not be null"); + } + return player.getNewEditSession(); + } + + /** + * Get a new non-player EditSession + * @see #getWorld(String) + * @param world + * @return + */ + public EditSession getNewEditSession(World world) { + return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1); + } + /** * The TaskManager has some useful methods for doing things asynchronously * @return TaskManager @@ -133,7 +160,7 @@ public class FaweAPI { * Cancel the edit with the following extent
* - The extent must be the one being used by an EditSession, otherwise an error may be thrown
* - Insert an extent into the EditSession using the EditSessionEvent: http://wiki.sk89q.com/wiki/WorldEdit/API/Hooking_EditSession
- * @see com.sk89q.worldedit.EditSession#getFaweExtent() To get the FaweExtent for an EditSession + * @see com.sk89q.worldedit.EditSession#getRegionExtent() To get the FaweExtent for an EditSession * @param extent * @param reason */ @@ -229,7 +256,11 @@ public class FaweAPI { Collections.sort(files, new Comparator() { @Override public int compare(File a, File b) { - long value = a.lastModified() - b.lastModified(); + String aName = a.getName(); + String bName = b.getName(); + int aI = Integer.parseInt(aName.substring(0, aName.length() - 3)); + int bI = Integer.parseInt(bName.substring(0, bName.length() - 3)); + long value = aI - bI; return value == 0 ? 0 : value < 0 ? -1 : 1; } }); @@ -292,21 +323,21 @@ public class FaweAPI { * @param world * @param x * @param z - * @param fixAll + * @param mode */ - public static void fixLighting(String world, int x, int z, final boolean fixAll) { + public static void fixLighting(String world, int x, int z, FaweQueue.RelightMode mode) { FaweQueue queue = SetQueue.IMP.getNewQueue(world, true, false); - queue.fixLighting(queue.getChunk(x, z), fixAll); + queue.fixLighting(queue.getChunk(x, z), mode); } /** * Fix the lighting in a chunk * @param chunk - * @param fixAll + * @param mode */ - public static void fixLighting(final Chunk chunk, final boolean fixAll) { + public static void fixLighting(final Chunk chunk, FaweQueue.RelightMode mode) { FaweQueue queue = SetQueue.IMP.getNewQueue(chunk.getWorld().getName(), true, false); - queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), fixAll); + queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), mode); } /** @@ -318,6 +349,7 @@ public class FaweAPI { * @param loc * @return */ + @Deprecated public static void streamSchematic(final File file, final Location loc) { final FaweLocation fl = new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); streamSchematic(file, fl); @@ -331,6 +363,7 @@ public class FaweAPI { * @param loc * @return */ + @Deprecated public static void streamSchematic(final File file, final FaweLocation loc) { try { final FileInputStream is = new FileInputStream(file); @@ -347,6 +380,7 @@ public class FaweAPI { * @param url * @param loc */ + @Deprecated public static void streamSchematic(final URL url, final FaweLocation loc) { try { final ReadableByteChannel rbc = Channels.newChannel(url.openStream()); @@ -366,6 +400,7 @@ public class FaweAPI { * @param loc * @throws IOException */ + @Deprecated public static void streamSchematic(final InputStream is, final FaweLocation loc) throws IOException { final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is)); Tag tag = stream.readNamedTag().getTag(); @@ -403,79 +438,10 @@ public class FaweAPI { final int i = i2 + x; final int xx = x_offset + x; final short id = (short) (ids[i] & 0xFF); - switch (id) { - case 0: - case 2: - case 4: - case 13: - case 14: - case 15: - case 20: - case 21: - case 22: - 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 56: - case 57: - case 58: - case 60: - 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 85: - case 87: - case 88: - case 101: - case 102: - case 103: - case 110: - case 112: - case 113: - case 121: - case 122: - case 129: - case 133: - case 165: - case 166: - case 169: - case 170: - case 172: - case 173: - case 174: - case 181: - case 182: - case 188: - case 189: - case 190: - case 191: - case 192: - queue.setBlock(xx, yy, zz, id, (byte) 0); - break; - default: { - queue.setBlock(xx, yy, zz, id, datas[i]); - break; - } + if (FaweCache.hasData(id)) { + queue.setBlock(xx, yy, zz, id, datas[i]); + } else { + queue.setBlock(xx, yy, zz, id, (byte) 0); } } } diff --git a/core/src/main/java/com/boydti/fawe/FaweCache.java b/core/src/main/java/com/boydti/fawe/FaweCache.java index bdf63289..3cb1b7a6 100644 --- a/core/src/main/java/com/boydti/fawe/FaweCache.java +++ b/core/src/main/java/com/boydti/fawe/FaweCache.java @@ -6,26 +6,56 @@ import com.sk89q.worldedit.blocks.BaseBlock; public class FaweCache { /** - * y | x | z + * [ y | x | z ] => index */ public final static short[][][] CACHE_I = new short[256][16][16]; /** - * y | x | z + * [ y | x | z ] => index */ public final static short[][][] CACHE_J = new short[256][16][16]; + /** + * [ i | j ] => x + */ public final static byte[][] CACHE_X = new byte[16][4096]; + /** + * [ i | j ] => y + */ public final static short[][] CACHE_Y = new short[16][4096]; + /** + * [ i | j ] => z + */ public final static byte[][] CACHE_Z = new byte[16][4096]; + /** + * [ combined ] => id + * (combined >> 4) = id + */ public final static short[] CACHE_ID = new short[65535]; + /** + * [ combined ] => data + * (combined & 0xF) = data + */ public final static byte[] CACHE_DATA = new byte[65535]; + /** + * Immutable BaseBlock cache + * [ combined ] => block + */ public final static BaseBlock[] CACHE_BLOCK = new BaseBlock[Short.MAX_VALUE]; - // Faster than java random (since the game just needs to look random) + /** + * Faster than java random (since it just needs to look random) + */ public final static PseudoRandom RANDOM = new PseudoRandom(); + /** + * Get the cached BaseBlock object for an id/data
+ * - The block is immutable + * @param id + * @param data + * @return + */ public static BaseBlock getBlock(int id, int data) { return CACHE_BLOCK[(id << 4) + data]; } @@ -84,7 +114,12 @@ public class FaweCache { }; } } - + + /** + * Check if an id might have data + * @param id + * @return + */ public static boolean hasData(int id) { switch (id) { case 0: @@ -167,7 +202,12 @@ public class FaweCache { return true; } } - + + /** + * Check if an id might have nbt + * @param id + * @return + */ public static boolean hasNBT(int id) { switch (id) { case 54: diff --git a/core/src/main/java/com/boydti/fawe/command/Cancel.java b/core/src/main/java/com/boydti/fawe/command/Cancel.java index 12274082..217c34e4 100644 --- a/core/src/main/java/com/boydti/fawe/command/Cancel.java +++ b/core/src/main/java/com/boydti/fawe/command/Cancel.java @@ -28,7 +28,7 @@ public class Cancel extends FaweCommand { for (FaweQueue queue : queues) { Set sessions = queue.getEditSessions(); for (EditSession session : sessions) { - Actor actor = session.actor; + Actor actor = session.getActor(); if (actor == null) { continue; } diff --git a/core/src/main/java/com/boydti/fawe/command/FixLighting.java b/core/src/main/java/com/boydti/fawe/command/FixLighting.java index 952add9e..07a2e0e7 100644 --- a/core/src/main/java/com/boydti/fawe/command/FixLighting.java +++ b/core/src/main/java/com/boydti/fawe/command/FixLighting.java @@ -2,11 +2,13 @@ package com.boydti.fawe.command; import com.boydti.fawe.FaweAPI; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.SetQueue; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; public class FixLighting extends FaweCommand { @@ -21,27 +23,27 @@ public class FixLighting extends FaweCommand { return false; } final FaweLocation loc = player.getLocation(); - final Region selection = player.getSelection(); - if (selection == null) { - FaweAPI.fixLighting(loc.world, loc.x >> 4, loc.z >> 4, Settings.FIX_ALL_LIGHTING); - BBC.FIX_LIGHTING_CHUNK.send(player); - return true; - } final int cx = loc.x >> 4; final int cz = loc.z >> 4; + + Region selection = player.getSelection(); + if (selection == null) { + selection = new CuboidRegion(new Vector(cx - 8, 0, cz - 8).multiply(16), new Vector(cx + 8, 0, cz + 8).multiply(16)); + } final Vector bot = selection.getMinimumPoint(); final Vector top = selection.getMaximumPoint(); - final int minX = Math.max(cx - 8, bot.getBlockX() >> 4); - final int minZ = Math.max(cz - 8, bot.getBlockZ() >> 4); + final int minX = bot.getBlockX() >> 4; + final int minZ = bot.getBlockZ() >> 4; - final int maxX = Math.min(cx + 8, top.getBlockX() >> 4); - final int maxZ = Math.min(cz + 8, top.getBlockZ() >> 4); + final int maxX = top.getBlockX() >> 4; + final int maxZ = top.getBlockZ() >> 4; int count = 0; + FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, false); for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { - FaweAPI.fixLighting(loc.world, x, z, Settings.FIX_ALL_LIGHTING); + queue.fixLighting(queue.getChunk(x, z), FaweQueue.RelightMode.ALL); count++; } } diff --git a/core/src/main/java/com/boydti/fawe/command/Rollback.java b/core/src/main/java/com/boydti/fawe/command/Rollback.java index 43e797e0..9a8d542f 100644 --- a/core/src/main/java/com/boydti/fawe/command/Rollback.java +++ b/core/src/main/java/com/boydti/fawe/command/Rollback.java @@ -11,7 +11,6 @@ import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; -import com.boydti.fawe.util.SetQueue; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.world.World; @@ -102,7 +101,7 @@ public class Rollback extends FaweCommand { EditSession session = edit.toEditSession(null); session.undo(session); edit.deleteFiles(); - SetQueue.IMP.addTask(this); + session.getQueue().addNotifyTask(this); } }; task.run(); 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 c22c4329..d8d8467a 100644 --- a/core/src/main/java/com/boydti/fawe/config/BBC.java +++ b/core/src/main/java/com/boydti/fawe/config/BBC.java @@ -17,7 +17,7 @@ public enum BBC { * Things to note about this class: * Can use multiple arguments %s, %s1, %s2, %s3 etc */ - PREFIX("&8(&5&lFAWE&8)&7", "Info"), + PREFIX("&8(&4&lFAWE&8)&7", "Info"), SCHEMATIC_PASTING("&7The schematic is pasting. This cannot be undone.", "Info"), FIX_LIGHTING_CHUNK("&7Lighting has been fixed in your current chunk. Relog to see the affect.", "Info"), FIX_LIGHTING_SELECTION("&7Lighting has been fixed in %s0 chunks. Relog to see the affect.", "Info"), @@ -95,6 +95,8 @@ public enum BBC { SELECTOR_CUBOID_POS1("First position set to %s0 %s1.", "WorldEdit.Selector"), SELECTOR_CUBOID_POS2("Second position set to %s0 %s1.", "WorldEdit.Selector"), + PROGRESS_MESSAGE("[ Queue: %s0 | Dispatched: %s1 ]", "Progress"), + PROGRESS_DONE ("[ Took: %s0s ]", "Progress"), COMMAND_SYNTAX("&cUsage: &7%s0", "Error"), 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 b86f6afd..c9a11979 100644 --- a/core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/core/src/main/java/com/boydti/fawe/config/Settings.java @@ -14,33 +14,37 @@ import java.util.Map.Entry; public class Settings { -// public static boolean REQUIRE_SELECTION = false; -// public static boolean COMMAND_PROCESSOR = false; -// public static List WE_BLACKLIST = Arrays.asList("cs", ".s", "restore", "snapshot", "delchunks", "listchunks"); public static long MEM_FREE = 95; public static boolean ENABLE_HARD_LIMIT = true; public static boolean STORE_HISTORY_ON_DISK = false; public static boolean STORE_CLIPBOARD_ON_DISK = false; public static int DELETE_HISTORY_AFTER_DAYS = 7; + public static boolean CLEAN_HISTORY_ON_LOGOUT = true; public static int DELETE_CLIPBOARD_AFTER_DAYS = 1; - public static int COMPRESSION_LEVEL = 0; - public static int BUFFER_SIZE = 531441; public static boolean METRICS = true; public static int CHUNK_WAIT = 100; public static boolean REGION_RESTRICTIONS = true; public static int ALLOCATE = 0; public static int QUEUE_SIZE = 64; public static int QUEUE_MAX_WAIT = 1000; - public static int QUEUE_DISCARD_AFTER = 60000; + public static boolean DISPLAY_PROGRESS = false; + public static int DISPLAY_PROGRESS_INTERVAL = 1; public static List ALLOWED_3RDPARTY_EXTENTS; public static boolean EXTENT_DEBUG = true; - public static int UNSAFE_PARALLEL_THREADS = 1; public static boolean FIX_ALL_LIGHTING = true; public static boolean ASYNC_LIGHTING = true; public static int PHYSICS_PER_TICK = 500000; public static int ITEMS_PER_TICK = 50000; - public static boolean COMBINE_HISTORY_STAGE = true; + // Maybe confusing? + // - `compression: false` just uses cheaper compression, but still compresses + public static int COMPRESSION_LEVEL = 0; + public static boolean COMBINE_HISTORY_STAGE = false; + public static int PARALLEL_THREADS = 1; + + // Non configurable (yet / shouldn't be?) + public static int BUFFER_SIZE = 531441; + public static int QUEUE_DISCARD_AFTER = 60000; public static HashMap limits; @@ -77,13 +81,11 @@ public class Settings { } } final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + config.set("DOCUMENTATION","https://github.com/boy0001/FastAsyncWorldedit/wiki/Configuration"); limits = new HashMap<>(); final Map options = new HashMap<>(); -// options.put("require-selection-in-mask", REQUIRE_SELECTION); -// options.put("command-blacklist", WE_BLACKLIST); -// options.put("command-processor", COMMAND_PROCESSOR); options.put("max-memory-percent", MEM_FREE); options.put("crash-mitigation", ENABLE_HARD_LIMIT); options.put("lighting.fix-all", FIX_ALL_LIGHTING); @@ -93,18 +95,22 @@ public class Settings { options.put("history.use-disk", STORE_HISTORY_ON_DISK); options.put("history.compress", false); options.put("history.chunk-wait-ms", CHUNK_WAIT); -// options.put("history.buffer-size", BUFFER_SIZE); options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS); + options.put("history.delete-on-logout", CLEAN_HISTORY_ON_LOGOUT); options.put("region-restrictions", REGION_RESTRICTIONS); - options.put("queue.parallel-threads", UNSAFE_PARALLEL_THREADS); options.put("queue.extra-time-ms", ALLOCATE); + options.put("queue.progress.display", DISPLAY_PROGRESS); + options.put("queue.progress.interval", DISPLAY_PROGRESS_INTERVAL); options.put("queue.target-size", QUEUE_SIZE); options.put("queue.max-wait-ms", QUEUE_MAX_WAIT); -// options.put("queue.discard-after-ms", QUEUE_DISCARD_AFTER); options.put("extent.allowed-plugins", new ArrayList()); options.put("extent.debug", EXTENT_DEBUG); options.put("metrics", METRICS); + // Possibly confusing? - leave configurable since not entirely stable yet + options.put("history.combine-stages", COMBINE_HISTORY_STAGE); + options.put("queue.parallel-threads", Math.max(1, Runtime.getRuntime().availableProcessors())); + if (config.getInt("tick-limiter.physics") == 1337) { config.set("tick-limiter.physics", PHYSICS_PER_TICK); } @@ -130,21 +136,19 @@ public class Settings { FIX_ALL_LIGHTING = config.getBoolean("lighting.fix-all"); ASYNC_LIGHTING = config.getBoolean("lighting.async"); MEM_FREE = config.getInt("max-memory-percent"); -// COMMAND_PROCESSOR = config.getBoolean("command-processor"); -// REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask"); -// WE_BLACKLIST = config.getStringList("command-blacklist"); ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation"); REGION_RESTRICTIONS = config.getBoolean("region-restrictions"); METRICS = config.getBoolean("metrics"); COMPRESSION_LEVEL = config.getInt("history.compression-level", config.getBoolean("history.compress") ? 1 : 0); DELETE_HISTORY_AFTER_DAYS = config.getInt("history.delete-after-days"); - BUFFER_SIZE = config.getInt("history.buffer-size", BUFFER_SIZE); + CLEAN_HISTORY_ON_LOGOUT = config.getBoolean("history.delete-on-logout"); CHUNK_WAIT = config.getInt("history.chunk-wait-ms"); ALLOCATE = config.getInt("queue.extra-time-ms"); QUEUE_SIZE = config.getInt("queue.target-size"); QUEUE_MAX_WAIT = config.getInt("queue.max-wait-ms"); - UNSAFE_PARALLEL_THREADS = config.getInt("queue.parallel-threads"); - QUEUE_DISCARD_AFTER = config.getInt("queue.discard-after-ms", QUEUE_DISCARD_AFTER); + DISPLAY_PROGRESS = config.getBoolean("queue.progress.display"); + DISPLAY_PROGRESS_INTERVAL = config.getInt("queue.progress.interval"); + PARALLEL_THREADS = config.getInt("queue.parallel-threads", Math.max(1, Runtime.getRuntime().availableProcessors())); ALLOWED_3RDPARTY_EXTENTS = config.getStringList("extent.allowed-plugins"); EXTENT_DEBUG = config.getBoolean("extent.debug"); STORE_CLIPBOARD_ON_DISK = config.getBoolean("clipboard.use-disk"); @@ -152,6 +156,11 @@ public class Settings { PHYSICS_PER_TICK = config.getInt("tick-limiter.physics"); ITEMS_PER_TICK = config.getInt("tick-limiter.items"); + // Not usually configurable + BUFFER_SIZE = config.getInt("history.buffer-size", BUFFER_SIZE); + QUEUE_DISCARD_AFTER = config.getInt("queue.discard-after-ms", QUEUE_DISCARD_AFTER); + COMBINE_HISTORY_STAGE = config.getBoolean("history.combine-stages", COMBINE_HISTORY_STAGE); + 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/example/CharFaweChunk.java b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java index 1e8e03e8..e85ba28b 100644 --- a/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/example/CharFaweChunk.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; public abstract class CharFaweChunk extends FaweChunk { @@ -111,7 +112,7 @@ public abstract class CharFaweChunk extends FaweChunk { return this.ids[i]; } - public char[][] getIdArrays() { + public char[][] getCombinedIdArrays() { return this.ids; } @@ -153,8 +154,12 @@ public abstract class CharFaweChunk extends FaweChunk { return entities == null ? new HashSet() : entities; } + + public HashSet entities; + public HashSet entityRemoves; + @Override public void setEntity(CompoundTag tag) { if (entities == null) { @@ -163,6 +168,19 @@ public abstract class CharFaweChunk extends FaweChunk { entities.add(tag); } + @Override + public void removeEntity(UUID uuid) { + if (entityRemoves == null) { + entityRemoves = new HashSet<>(); + } + entityRemoves.add(uuid); + } + + @Override + public HashSet getEntityRemoves() { + return entityRemoves == null ? new HashSet() : entityRemoves; + } + @Override public void setBlock(final int x, final int y, final int z, final int id, byte data) { final int i = FaweCache.CACHE_I[y][x][z]; diff --git a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java index 820075a5..7bf583b7 100644 --- a/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/MappedFaweQueue.java @@ -14,6 +14,7 @@ import com.sk89q.worldedit.world.biome.BaseBiome; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingDeque; @@ -25,7 +26,15 @@ public abstract class MappedFaweQueue extends FaweQueue { * Map of chunks in the queue */ private ConcurrentHashMap blocks = new ConcurrentHashMap<>(); - private LinkedBlockingDeque chunks = new LinkedBlockingDeque<>(); + private LinkedBlockingDeque chunks = new LinkedBlockingDeque() { + @Override + public boolean add(FaweChunk o) { + if (progressTask != null) { + progressTask.run(ProgressType.QUEUE, size() + 1); + } + return super.add(o); + } + }; private ArrayDeque tasks = new ArrayDeque<>(); @Override @@ -53,6 +62,7 @@ public abstract class MappedFaweQueue extends FaweQueue { @Override public void addNotifyTask(Runnable runnable) { this.tasks.add(runnable); + size(); } public MappedFaweQueue(final String world) { @@ -72,9 +82,6 @@ public abstract class MappedFaweQueue extends FaweQueue { @Override public abstract FaweChunk getChunk(int x, int z); - @Override - public abstract boolean fixLighting(FaweChunk fc, boolean fixAll); - public abstract boolean loadChunk(WORLD world, int x, int z, boolean generate); public abstract CHUNK getCachedChunk(WORLD world, int cx, int cz); @@ -202,6 +209,33 @@ public abstract class MappedFaweQueue extends FaweQueue { lastWrappedChunk.setEntity(tag); } + @Override + public void removeEntity(int x, int y, int z, UUID uuid) { + if ((y > 255) || (y < 0)) { + return; + } + int cx = x >> 4; + int cz = z >> 4; + if (cx != lastX || cz != lastZ) { + lastX = cx; + lastZ = cz; + long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL; + lastWrappedChunk = this.blocks.get(pair); + if (lastWrappedChunk == null) { + lastWrappedChunk = this.getChunk(x >> 4, z >> 4); + lastWrappedChunk.removeEntity(uuid); + FaweChunk previous = this.blocks.put(pair, lastWrappedChunk); + if (previous == null) { + chunks.add(lastWrappedChunk); + return; + } + this.blocks.put(pair, previous); + lastWrappedChunk = previous; + } + } + lastWrappedChunk.removeEntity(uuid); + } + @Override public boolean setBiome(int x, int z, BaseBiome biome) { long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; @@ -243,15 +277,23 @@ public abstract class MappedFaweQueue extends FaweQueue { } public void runTasks() { - for (Runnable run : tasks) { - run.run(); + if (progressTask != null) { + progressTask.run(ProgressType.DONE, 1); } + ArrayDeque tmp = new ArrayDeque<>(tasks); tasks.clear(); + for (Runnable run : tmp) { + try { + run.run(); + } catch (Throwable e) { + e.printStackTrace(); + } + } } @Override public int size() { - if (chunks.size() == 0 && SetQueue.IMP.isStage(this, SetQueue.QueueStage.ACTIVE)) { + if (chunks.size() == 0 && SetQueue.IMP.getStage(this) != SetQueue.QueueStage.INACTIVE) { runTasks(); } return chunks.size(); @@ -259,11 +301,17 @@ public abstract class MappedFaweQueue extends FaweQueue { private LinkedBlockingDeque toUpdate = new LinkedBlockingDeque<>(); + private int dispatched = 0; + public boolean execute(final FaweChunk fc) { if (fc == null) { return false; } // Set blocks / entities / biome + if (progressTask != null) { + progressTask.run(ProgressType.QUEUE, chunks.size()); + progressTask.run(ProgressType.DISPATCH, ++dispatched); + } if (getChangeTask() != null) { if (!this.setComponents(fc, new RunnableVal() { @Override @@ -284,6 +332,7 @@ public abstract class MappedFaweQueue extends FaweQueue { public void clear() { this.blocks.clear(); this.chunks.clear(); + runTasks(); } @Override diff --git a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java index 440a924f..aa89e0b7 100644 --- a/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/example/NMSMappedFaweQueue.java @@ -2,7 +2,12 @@ package com.boydti.fawe.example; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.TaskManager; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.UUID; public abstract class NMSMappedFaweQueue extends MappedFaweQueue { public NMSMappedFaweQueue(String world) { @@ -14,12 +19,12 @@ public abstract class NMSMappedFaweQueue ex TaskManager.IMP.taskSyncSoon(new Runnable() { @Override public void run() { - final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING) || !Settings.ASYNC_LIGHTING; + final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL) || !Settings.ASYNC_LIGHTING; TaskManager.IMP.taskSyncNow(new Runnable() { @Override public void run() { if (!result) { - fixLighting(fc, Settings.FIX_ALL_LIGHTING); + fixLighting(fc, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL); } CHUNK chunk = (CHUNK) fc.getChunk(); refreshChunk(getWorld(), chunk); @@ -31,6 +36,5 @@ public abstract class NMSMappedFaweQueue ex public abstract void refreshChunk(WORLD world, CHUNK chunk); - @Override - public abstract boolean fixLighting(FaweChunk fc, boolean fixAll); + public abstract CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTION sections, Map tiles, Collection[] entities, Set createdEntities, boolean all) throws Exception; } diff --git a/core/src/main/java/com/boydti/fawe/object/BytePair.java b/core/src/main/java/com/boydti/fawe/object/BytePair.java index cd85edc9..efebfd21 100644 --- a/core/src/main/java/com/boydti/fawe/object/BytePair.java +++ b/core/src/main/java/com/boydti/fawe/object/BytePair.java @@ -9,6 +9,14 @@ public class BytePair { int hash; + public byte get0() { + return pair[0]; + } + + public byte get1() { + return pair[1]; + } + @Override public int hashCode() { return pair[0] + (pair[1] << 8); 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 a8eeb095..a8ca64a2 100644 --- a/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java +++ b/core/src/main/java/com/boydti/fawe/object/EditSessionWrapper.java @@ -117,7 +117,7 @@ public class EditSessionWrapper { return minY; } - public Extent getHistoryExtent(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer player) { - return new HistoryExtent(session, limit, parent, set, queue); + public FaweChangeSet wrapChangeSet(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer player) { + return set; } } diff --git a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java index 5f1f002c..d4bb21e5 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweChunk.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweChunk.java @@ -1,5 +1,6 @@ package com.boydti.fawe.object; +import com.boydti.fawe.FaweCache; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.FaweQueue; import com.sk89q.jnbt.CompoundTag; @@ -7,6 +8,7 @@ import com.sk89q.worldedit.world.biome.BaseBiome; import java.util.ArrayDeque; import java.util.Map; import java.util.Set; +import java.util.UUID; public abstract class FaweChunk { @@ -24,12 +26,23 @@ public abstract class FaweChunk { this.z = z; } + /** + * Change the chunk's location
+ * - E.g. if you are cloning a chunk and want to set multiple + * @param parent + * @param x + * @param z + */ public void setLoc(FaweQueue parent, int x, int z) { this.parent = parent; this.x = x; this.z = z; } + /** + * Get the parent queue this chunk belongs to + * @return + */ public FaweQueue getParent() { return parent; } @@ -42,24 +55,65 @@ public abstract class FaweChunk { return z; } + /** + * Get a unique hashcode for this chunk + * @return + */ public long longHash() { return (long) x << 32 | z & 0xFFFFFFFFL; } + /** + * Get a hashcode; unique below abs(x/z) < Short.MAX_VALUE + * @return + */ @Override public int hashCode() { return x << 16 | z & 0xFFFF; } + /** + * Add the chunk to the queue + */ public void addToQueue() { parent.setChunk(this); } + /** + * Fix the lighting in this chunk + */ public void fixLighting() { - parent.fixLighting(this, Settings.FIX_ALL_LIGHTING); + parent.fixLighting(this, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL); } - public abstract char[][] getIdArrays(); + /** + * This may return the raw value or constructed depending on the implementation
+ * - The first index (i) is the layer (layer = y >> 4) (16 layers)
+ * - The second array is length 4096 and contains the combined ids (cast to an int if you want) + * + * @see com.boydti.fawe.FaweCache#CACHE_I + * @see com.boydti.fawe.FaweCache#CACHE_J + * @see com.boydti.fawe.FaweCache#CACHE_X + * @see com.boydti.fawe.FaweCache#CACHE_Y + * @see com.boydti.fawe.FaweCache#CACHE_Z + * + * @return Combined id arrays + */ + public abstract char[][] getCombinedIdArrays(); + + /** + * Get the combined block id at a location
+ * combined = (id <<<< 4) + data + * @param x + * @param y + * @param z + * @return The combined id + */ + public int getBlockCombinedId(int x, int y, int z) { + char[][] arrays = getCombinedIdArrays(); + char[] array = arrays[y >> 4]; + return array != null ? (array[FaweCache.CACHE_J[y][x][z]]) : 0; + } /** * Fill this chunk with a block @@ -91,6 +145,10 @@ public abstract class FaweChunk { } } + /** + * Add a task to run when this chunk is dispatched + * @param run + */ public void addNotifyTask(Runnable run) { if (run != null) { tasks.add(run); @@ -108,22 +166,61 @@ public abstract class FaweChunk { tasks.clear(); } + /** + * Get the underlying chunk object + * @return + */ public abstract T getChunk(); + /** + * Set a tile entity at a location
+ * - May throw an error if an invalid block is at the location + * @param x + * @param y + * @param z + * @param tile + */ public abstract void setTile(int x, int y, int z, CompoundTag tile); public abstract void setEntity(CompoundTag entity); - public abstract void setBlock(final int x, final int y, final int z, final int id, final byte data); + public abstract void removeEntity(UUID uuid); - public abstract CompoundTag getTile(int x, int y, int z); + public abstract void setBlock(final int x, final int y, final int z, final int id, final byte data); public abstract Set getEntities(); + /** + * Get the UUID of entities being removed + * @return + */ + public abstract Set getEntityRemoves(); + + /** + * Get the map of location to tile entity
+ * - The byte pair represents the location in the chunk
+ * @see com.boydti.fawe.util.MathMan#unpair16x (get0) => x + * @see com.boydti.fawe.util.MathMan#unpair16y (get0) => z + * get1 => y + * @return + */ public abstract Map getTiles(); + /** + * Get the tile at a location + * @param x + * @param y + * @param z + * @return + */ + public abstract CompoundTag getTile(int x, int y, int z); + public abstract void setBiome(final int x, final int z, final BaseBiome biome); + /** + * Spend time now so that the chunk can be more efficiently dispatched later
+ * - Modifications after this call will be ignored + */ public void optimize() {} @Override diff --git a/core/src/main/java/com/boydti/fawe/object/FaweCommand.java b/core/src/main/java/com/boydti/fawe/object/FaweCommand.java index 88b89edb..6dc38130 100644 --- a/core/src/main/java/com/boydti/fawe/object/FaweCommand.java +++ b/core/src/main/java/com/boydti/fawe/object/FaweCommand.java @@ -21,24 +21,29 @@ public abstract class FaweCommand { } public boolean executeSafe(final FawePlayer player, final String... args) { - if (player == null || !safe) { - execute(player, args); - return true; - } else { - if (player.getMeta("fawe_action") != null) { - BBC.WORLDEDIT_COMMAND_LIMIT.send(player); + try { + if (player == null || !safe) { + execute(player, args); return true; - } - player.setMeta("fawe_action", true); - TaskManager.IMP.async(new Runnable() { - @Override - public void run() { - execute(player, args); - player.deleteMeta("fawe_action"); + } else { + if (player.getMeta("fawe_action") != null) { + BBC.WORLDEDIT_COMMAND_LIMIT.send(player); + return true; } - }); + player.setMeta("fawe_action", true); + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + execute(player, args); + player.deleteMeta("fawe_action"); + } + }); + } + return true; + } catch (Throwable e) { + e.printStackTrace(); } - return true; + return false; } public abstract boolean execute(final FawePlayer player, final String... args); 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 71828f24..3cbff22a 100644 --- a/core/src/main/java/com/boydti/fawe/object/FawePlayer.java +++ b/core/src/main/java/com/boydti/fawe/object/FawePlayer.java @@ -173,7 +173,7 @@ public abstract class FawePlayer { FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index); EditSession edit = set.toEditSession(getPlayer()); if (world.equals(getWorld())) { - session.remember(edit, false); + session.remember(edit, false, false); } else { return; } @@ -185,7 +185,16 @@ public abstract class FawePlayer { } /** - * Get the player's limit + * Send a title + * @param head + * @param sub + */ + public abstract void sendTitle(String head, String sub); + + public abstract void resetTitle(); + + /** + * Get the player's limit * @return */ public FaweLimit getLimit() { @@ -370,4 +379,11 @@ public abstract class FawePlayer { WorldEdit.getInstance().removeSession(getPlayer()); Fawe.get().unregister(getName()); } + + /** + * Get a new EditSession from this player + */ + public EditSession getNewEditSession() { + return WorldEdit.getInstance().getEditSessionFactory().getEditSession(getWorld(), -1, getPlayer()); + } } 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 c29ca669..567b8b9c 100644 --- a/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/NullChangeSet.java @@ -3,10 +3,15 @@ package com.boydti.fawe.object; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.history.change.Change; +import com.sk89q.worldedit.world.World; import java.util.ArrayList; import java.util.Iterator; public class NullChangeSet extends FaweChangeSet { + public NullChangeSet(World world) { + super(world); + } + @Override public boolean flush() { return false; diff --git a/core/src/main/java/com/boydti/fawe/object/brush/AbstractDelegateBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/AbstractDelegateBrush.java new file mode 100644 index 00000000..1eccd8d5 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/AbstractDelegateBrush.java @@ -0,0 +1,4 @@ +package com.boydti.fawe.object.brush; + +public class AbstractDelegateBrush { +} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java index a4cfe2f1..0393b2ae 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/HeightBrush.java @@ -1,30 +1,23 @@ package com.boydti.fawe.object.brush; import com.boydti.fawe.config.BBC; -import com.boydti.fawe.object.IntegerPair; -import com.boydti.fawe.object.brush.heightmap.ArrayHeightMap; -import com.boydti.fawe.object.brush.heightmap.HeightMap; +import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap; import com.boydti.fawe.object.exception.FaweException; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.pattern.Pattern; -import java.awt.image.BufferedImage; -import java.awt.image.Raster; import java.io.File; import java.io.IOException; -import java.util.HashSet; -import javax.imageio.ImageIO; public class HeightBrush implements Brush { - public final HeightMap heightMap; + public final ScalableHeightMap heightMap; private final int rotation; double yscale = 1; private final BrushTool tool; @@ -36,57 +29,13 @@ public class HeightBrush implements Brush { if (file == null || !file.exists()) { // Since I can't be bothered using separate args, we'll get it from the filename if (file.getName().equalsIgnoreCase("#clipboard.png") && clipboard != null) { - Vector dim = clipboard.getDimensions(); - byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()]; - int minX = clipboard.getMinimumPoint().getBlockX(); - int minZ = clipboard.getMinimumPoint().getBlockZ(); - int minY = clipboard.getMinimumPoint().getBlockY(); - int maxY = clipboard.getMaximumPoint().getBlockY(); - int clipHeight = maxY - minY + 1; - HashSet visited = new HashSet<>(); - for (Vector pos : clipboard.getRegion()) { - IntegerPair pair = new IntegerPair((int) pos.x, (int) pos.z); - if (visited.contains(pair)) { - continue; - } - visited.add(pair); - int xx = pos.getBlockX(); - int zz = pos.getBlockZ(); - int highestY = 0; - for (int y = minY; y <= maxY; y++) { - pos.y = y; - BaseBlock block = clipboard.getBlock(pos); - if (block != EditSession.nullBlock) { - highestY = y + 1; - } - } - int pointHeight = Math.min(255, (256 * (highestY - minY)) / clipHeight); - int x = xx - minX; - int z = zz - minZ; - heightArray[x][z] = (byte) pointHeight; - } - heightMap = new ArrayHeightMap(heightArray); + heightMap = ScalableHeightMap.fromClipboard(clipboard); } else { - heightMap = new HeightMap(); + heightMap = new ScalableHeightMap(); } } else { try { - BufferedImage heightFile = ImageIO.read(file); - int width = heightFile.getWidth(); - int length = heightFile.getHeight(); - Raster data = heightFile.getData(); - byte[][] array = new byte[width][length]; - for (int x = 0; x < width; x++) { - for (int z = 0; z < length; z++) { - int pixel = heightFile.getRGB(x, z); - int red = (pixel >> 16) & 0xFF; - int green = (pixel >> 8) & 0xFF; - int blue = (pixel >> 0) & 0xFF; - int intensity = (red + green + blue) / 3; - array[x][z] = (byte) intensity; - } - } - heightMap = new ArrayHeightMap(array); + heightMap = ScalableHeightMap.fromPNG(file); } catch (IOException e) { throw new FaweException(BBC.BRUSH_HEIGHT_INVALID); } @@ -101,69 +50,6 @@ public class HeightBrush implements Brush { } int size = (int) sizeDouble; heightMap.setSize(size); - int size2 = size * size; - int startY = position.getBlockY() + size; - int endY = position.getBlockY() - size; - int cx = position.getBlockX(); - int cz = position.getBlockZ(); - Vector mutablePos = new Vector(0, 0, 0); - for (int x = -size; x <= size; x++) { - int xx = cx + x; - mutablePos.x = xx; - for (int z = -size; z <= size; z++) { - int zz = cz + z; - int raise; - switch (rotation) { - default: - raise = heightMap.getHeight(x, z); - break; - case 1: - raise = heightMap.getHeight(z, x); - break; - case 2: - raise = heightMap.getHeight(-x, -z); - break; - case 3: - raise = heightMap.getHeight(-z, -x); - break; - } - raise = (int) (yscale * raise); - if (raise == 0) { - continue; - } - mutablePos.z = zz; - int foundHeight = Integer.MAX_VALUE; - BaseBlock block = null; - for (int y = startY; y >= endY; y--) { - block = editSession.getLazyBlock(xx, y, zz); - if (block != EditSession.nullBlock) { - if (mask != null) { - mutablePos.y = y; - if (!mask.test(mutablePos)) { - continue; - } - } - foundHeight = y; - break; - } - } - if (foundHeight == Integer.MAX_VALUE) { - continue; - } - if (raise > 0) { - for (int y = foundHeight + 1; y <= foundHeight + raise; y++) { - mutablePos.y = y; - editSession.setBlock(mutablePos, block); - } - } else { - for (int y = foundHeight; y > foundHeight + raise; y--) { - mutablePos.y = y; - editSession.setBlock(mutablePos, EditSession.nullBlock); - } - mutablePos.y = foundHeight + raise; - editSession.setBlock(mutablePos, block); - } - } - } + heightMap.apply(editSession, mask, position, size, rotation, yscale, true); } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java index 57f635e5..1e6b92ca 100644 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ArrayHeightMap.java @@ -1,6 +1,6 @@ package com.boydti.fawe.object.brush.heightmap; -public class ArrayHeightMap extends HeightMap { +public class ArrayHeightMap extends ScalableHeightMap { // The heights private final byte[][] height; // The height map width/length @@ -19,15 +19,15 @@ public class ArrayHeightMap extends HeightMap { public void setSize(int size) { super.setSize(size); this.rx = (double) width / (size << 1); - this.rz = (double) width / (size << 1); + this.rz = (double) length / (size << 1); } @Override - public int getHeight(int x, int z) { + public double getHeight(int x, int z) { x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx)); z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz)); - return (((int) height[x][z] & 0xFF) * size) / 256; + return ((height[x][z] & 0xFF) * size) / 256d; } } diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java deleted file mode 100644 index beecee72..00000000 --- a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/HeightMap.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.boydti.fawe.object.brush.heightmap; - -import com.boydti.fawe.util.MathMan; - -public class HeightMap { - public int size2; - public int size; - - public HeightMap() { - setSize(5); - } - - public HeightMap(int size) { - setSize(size); - } - - public void setSize(int size) { - this.size = size; - this.size2 = size * size; - } - - public int getHeight(int x, int z) { - int dx = Math.abs(x); - int dz = Math.abs(z); - int d2 = dx * dx + dz * dz; - if (d2 > size2) { - return 0; - } - return size - MathMan.sqrt(d2); - } -} diff --git a/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java new file mode 100644 index 00000000..d834a043 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/brush/heightmap/ScalableHeightMap.java @@ -0,0 +1,152 @@ +package com.boydti.fawe.object.brush.heightmap; + +import com.boydti.fawe.object.IntegerPair; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.WorldVector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.internal.LocalWorldAdapter; +import com.sk89q.worldedit.math.convolution.GaussianKernel; +import com.sk89q.worldedit.math.convolution.HeightMap; +import com.sk89q.worldedit.math.convolution.HeightMapFilter; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import javax.imageio.ImageIO; + +public class ScalableHeightMap { + public int size2; + public int size; + + public ScalableHeightMap() { + setSize(5); + } + + public ScalableHeightMap(int size) { + setSize(size); + } + + public void setSize(int size) { + this.size = size; + this.size2 = size * size; + } + + public double getHeight(int x, int z) { + int dx = Math.abs(x); + int dz = Math.abs(z); + int d2 = dx * dx + dz * dz; + if (d2 > size2) { + return 0; + } + return size - MathMan.sqrtApprox(d2); + } + + public static ScalableHeightMap fromClipboard(Clipboard clipboard) { + Vector dim = clipboard.getDimensions(); + byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()]; + int minX = clipboard.getMinimumPoint().getBlockX(); + int minZ = clipboard.getMinimumPoint().getBlockZ(); + int minY = clipboard.getMinimumPoint().getBlockY(); + int maxY = clipboard.getMaximumPoint().getBlockY(); + int clipHeight = maxY - minY + 1; + HashSet visited = new HashSet<>(); + for (Vector pos : clipboard.getRegion()) { + IntegerPair pair = new IntegerPair((int) pos.x, (int) pos.z); + if (visited.contains(pair)) { + continue; + } + visited.add(pair); + int xx = pos.getBlockX(); + int zz = pos.getBlockZ(); + int highestY = minY; + for (int y = minY; y <= maxY; y++) { + pos.y = y; + BaseBlock block = clipboard.getBlock(pos); + if (block != EditSession.nullBlock) { + highestY = y + 1; + } + } + int pointHeight = Math.min(255, (256 * (highestY - minY)) / clipHeight); + int x = xx - minX; + int z = zz - minZ; + heightArray[x][z] = (byte) pointHeight; + } + return new ArrayHeightMap(heightArray); + } + + public static ScalableHeightMap fromPNG(File file) throws IOException { + BufferedImage heightFile = ImageIO.read(file); + int width = heightFile.getWidth(); + int length = heightFile.getHeight(); + Raster data = heightFile.getData(); + byte[][] array = new byte[width][length]; + for (int x = 0; x < width; x++) { + for (int z = 0; z < length; z++) { + int pixel = heightFile.getRGB(x, z); + int red = (pixel >> 16) & 0xFF; + int green = (pixel >> 8) & 0xFF; + int blue = (pixel >> 0) & 0xFF; + int intensity = (red + green + blue) / 3; + array[x][z] = (byte) intensity; + } + } + return new ArrayHeightMap(array); + } + + public void apply(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth) throws MaxChangedBlocksException { + int diameter = 2 * size + 1; + int centerX = pos.getBlockX(); + int centerZ = pos.getBlockZ(); + int endY = pos.getBlockY() + size; + int startY = pos.getBlockY() - size; + int[] newData = new int[diameter * diameter]; + Vector mutablePos = new Vector(0, 0, 0); + for (int x = -size; x <= size; x++) { + int xx = centerX + x; + mutablePos.x = xx; + for (int z = -size; z <= size; z++) { + int index = (z + size) * diameter + (x + size); + int zz = centerZ + z; + double raise; + switch (rotationMode) { + default: + raise = getHeight(x, z); + break; + case 1: + raise = getHeight(z, x); + break; + case 2: + raise = getHeight(-x, -z); + break; + case 3: + raise = getHeight(-z, -x); + break; + } + raise = (yscale * raise); + int random = PseudoRandom.random.random(256) < (int) ((raise - (int) raise) * 256) ? 1 : 0; + int height = session.getHighestTerrainBlock(xx, zz, 0, 255, true) + (int) raise + random; + newData[index] = height; + } + } + int iterations = 1; + WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, 255, size)); + Vector max = pos.add(size, 255, size); + Region region = new CuboidRegion(session.getWorld(), min, max); + HeightMap heightMap = new HeightMap(session, region, true); + if (smooth) { + HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1)); + newData = filter.filter(newData, diameter, diameter); +// MainUtil.smoothArray(newData, diameter, 1, 4); + } + heightMap.apply(newData); + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java new file mode 100644 index 00000000..4f2285d8 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/change/MutableChunkChange.java @@ -0,0 +1,44 @@ +package com.boydti.fawe.object.change; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.extent.FastWorldEditExtent; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.history.UndoContext; +import com.sk89q.worldedit.history.change.Change; + +public class MutableChunkChange implements Change { + + public FaweChunk from; + public FaweChunk to; + + public MutableChunkChange(FaweChunk from, FaweChunk to) { + this.from = from; + this.to = to; + } + + @Override + public void undo(UndoContext context) throws WorldEditException { + create(context, true); + } + + @Override + public void redo(UndoContext context) throws WorldEditException { + create(context, false); + } + + public void create(UndoContext context, boolean undo) { + Extent extent = context.getExtent(); + if (extent.getClass() == FastWorldEditExtent.class) { + FastWorldEditExtent fwee = (FastWorldEditExtent) extent; + if (undo) { + fwee.getQueue().setChunk(from); + } else { + fwee.getQueue().setChunk(to); + } + } else { + Fawe.debug("FAWE doesn't support: " + context + " for " + getClass()); + } + } +} diff --git a/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java b/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java index d952a4f8..fe778544 100644 --- a/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java +++ b/core/src/main/java/com/boydti/fawe/object/change/MutableEntityChange.java @@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.LongTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; @@ -11,6 +12,7 @@ import com.sk89q.worldedit.history.UndoContext; import com.sk89q.worldedit.history.change.Change; import java.util.List; import java.util.Map; +import java.util.UUID; public class MutableEntityChange implements Change { @@ -26,6 +28,8 @@ public class MutableEntityChange implements Change { public void undo(UndoContext context) throws WorldEditException { if (!create) { create(context); + } else { + delete(context); } } @@ -33,6 +37,36 @@ public class MutableEntityChange implements Change { public void redo(UndoContext context) throws WorldEditException { if (create) { create(context); + } else { + delete(context); + } + } + + public void delete(UndoContext context) { + Extent extent = context.getExtent(); + if (extent.getClass() == FastWorldEditExtent.class) { + FastWorldEditExtent fwee = (FastWorldEditExtent) extent; + Map map = tag.getValue(); + long most; + long least; + if (map.containsKey("UUIDMost")) { + most = ((LongTag) map.get("UUIDMost")).getValue(); + least = ((LongTag) map.get("UUIDLeast")).getValue(); + } else if (map.containsKey("PersistentIDMSB")) { + most = ((LongTag) map.get("PersistentIDMSB")).getValue(); + least = ((LongTag) map.get("PersistentIDLSB")).getValue(); + } else { + Fawe.debug("Skipping entity without uuid."); + return; + } + List pos = (List) map.get("Pos").getValue(); + int x = (int) Math.round(pos.get(0).getValue()); + int y = (int) Math.round(pos.get(1).getValue()); + int z = (int) Math.round(pos.get(2).getValue()); + UUID uuid = new UUID(most, least); + fwee.getQueue().removeEntity(x, y, z, uuid); + } else { + Fawe.debug("FAWE doesn't support: " + context + " for " + getClass() + " (bug Empire92)"); } } diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedChangeSet.java new file mode 100644 index 00000000..84916278 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/changeset/CPUOptimizedChangeSet.java @@ -0,0 +1,72 @@ +package com.boydti.fawe.object.changeset; + +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.object.change.MutableChunkChange; +import com.boydti.fawe.util.FaweQueue; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.history.change.Change; +import com.sk89q.worldedit.world.World; +import java.util.ArrayList; +import java.util.Iterator; + +public class CPUOptimizedChangeSet extends FaweChangeSet { + + public CPUOptimizedChangeSet(World world) { + super(world); + } + + private ArrayList changes = new ArrayList<>(); + + public void addChangeTask(FaweQueue queue) { + queue.setChangeTask(new RunnableVal2() { + @Override + public void run(final FaweChunk previous, final FaweChunk next) { + char[][] previousIds = previous.getCombinedIdArrays(); + char[][] nextIds = next.getCombinedIdArrays(); + for (int i = 0; i < nextIds.length; i++) { + if (nextIds[i] != null && previousIds[i] == null) { + previous.fillCuboid(0, 15, i << 4, (i << 4) + 15, 0, 15, 0, (byte) 0); + } + } + changes.add(new MutableChunkChange(previous, next)); + } + }); + } + + @Override + + public void add(int x, int y, int z, int combinedFrom, int combinedTo) { + throw new UnsupportedOperationException("Invalid mode"); + } + + @Override + public void addTileCreate(CompoundTag tag) { + throw new UnsupportedOperationException("Invalid mode"); + } + + @Override + public void addTileRemove(CompoundTag tag) { + throw new UnsupportedOperationException("Invalid mode"); + } + + @Override + public void addEntityRemove(CompoundTag tag) { + throw new UnsupportedOperationException("Invalid mode"); + } + + @Override + public void addEntityCreate(CompoundTag tag) { + throw new UnsupportedOperationException("Invalid mode"); + } + + @Override + public Iterator getIterator(boolean redo) { + return changes.iterator(); + } + + @Override + public int size() { + return changes.size() * 65536; // num chunks * 65536 (guess of 65536 changes per chunk) + } +} 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 c543ff02..a48e444e 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 @@ -16,8 +16,6 @@ import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.zip.GZIPOutputStream; import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4InputStream; @@ -56,20 +54,15 @@ public class DiskStorageHistory extends FaweStreamChangeSet { // NBT From private NBTOutputStream osNBTF; - private GZIPOutputStream osNBTFG; - private AtomicInteger osNBTFI; // NBT To private NBTOutputStream osNBTT; - private GZIPOutputStream osNBTTG; // Entity Create From private NBTOutputStream osENTCF; - private GZIPOutputStream osENTCFG; // Entity Create To private NBTOutputStream osENTCT; - private GZIPOutputStream osENTCTG; private World world; @@ -82,6 +75,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } public DiskStorageHistory(World world, UUID uuid) { + super(world); String base = "history" + File.separator + world.getName() + File.separator + uuid; File folder = new File(Fawe.imp().getDirectory(), base); int max = 0; @@ -100,6 +94,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } public DiskStorageHistory(World world, UUID uuid, int index) { + super(world); init(world, uuid, index); } @@ -125,27 +120,28 @@ public class DiskStorageHistory extends FaweStreamChangeSet { @Override public boolean flush() { - boolean flushed = false; + super.flush(); + boolean flushed = osBD != null || osNBTF != null || osNBTT != null && osENTCF != null || osENTCT != null; try { if (osBD != null) { - flushed = true; - osBD.flush(); osBD.close(); osBD = null; } if (osNBTF != null) { - flushed = true; - osNBTFG.flush(); osNBTF.close(); osNBTF = null; - osNBTFG = null; } if (osNBTT != null) { - flushed = true; - osNBTTG.flush(); osNBTT.close(); osNBTT = null; - osNBTTG = null; + } + if (osENTCF != null) { + osENTCF.close(); + osENTCF = null; + } + if (osENTCT != null) { + osENTCT.close(); + osENTCT = null; } } catch (Exception e) { e.printStackTrace(); @@ -185,8 +181,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } enttFile.getParentFile().mkdirs(); enttFile.createNewFile(); - osENTCTG = new GZIPOutputStream(new FileOutputStream(enttFile), true); - osENTCT = new NBTOutputStream(osENTCTG); + osENTCT = new NBTOutputStream(getCompressedOS(new FileOutputStream(enttFile))); return osENTCT; } @@ -197,8 +192,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } entfFile.getParentFile().mkdirs(); entfFile.createNewFile(); - osENTCFG = new GZIPOutputStream(new FileOutputStream(entfFile), true); - osENTCF = new NBTOutputStream(osENTCFG); + osENTCF = new NBTOutputStream(getCompressedOS(new FileOutputStream(entfFile))); return osENTCF; } @@ -209,8 +203,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } nbttFile.getParentFile().mkdirs(); nbttFile.createNewFile(); - osNBTTG = new GZIPOutputStream(new FileOutputStream(nbttFile), true); - osNBTT = new NBTOutputStream(osNBTTG); + osNBTT = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbttFile))); return osNBTT; } @@ -221,9 +214,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet { } nbtfFile.getParentFile().mkdirs(); nbtfFile.createNewFile(); - osNBTFG = new GZIPOutputStream(new FileOutputStream(nbtfFile), true); - osNBTF = new NBTOutputStream(osNBTFG); - osNBTFI = new AtomicInteger(); + osNBTF = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbtfFile))); return osNBTF; } 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 index e7c5ea2a..33d9cb2d 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -7,6 +7,7 @@ import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.TaskManager; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.EditSession; @@ -20,12 +21,36 @@ 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 com.sk89q.worldedit.world.World; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; public abstract class FaweChangeSet implements ChangeSet { - public abstract boolean flush(); + + private final World world; + + public FaweChangeSet(World world) { + this.world = world; + } + + public World getWorld() { + return world; + } + + public boolean flush() { + try { + while (waiting.get() > 0) { + synchronized (lock) { + lock.wait(1000); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } public abstract void add(int x, int y, int z, int combinedFrom, int combinedTo); @@ -47,7 +72,7 @@ public abstract class FaweChangeSet implements ChangeSet { public EditSession toEditSession(Player player) { EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory(); - EditSession edit = factory.getEditSession(player.getWorld(), -1, null, player); + EditSession edit = factory.getEditSession(world, -1, null, player); edit.setChangeSet(this); edit.dequeue(); return edit; @@ -131,91 +156,111 @@ public abstract class FaweChangeSet implements ChangeSet { } } + private AtomicInteger waiting = new AtomicInteger(0); + private Object lock = new Object(); + public void addChangeTask(FaweQueue queue) { queue.setChangeTask(new RunnableVal2() { @Override - public void run(final FaweChunk previous, FaweChunk next) { - /** - * TODO cache NBT - * - Counter variable for nbt changes - * - Record biome changes - */ - int cx = previous.getX(); - int cz = previous.getZ(); - int bx = cx << 4; - int bz = cz << 4; - - // Biome changes - { - // TODO - } - - // Block changes - { - // Current blocks - char[][] currentIds = next.getIdArrays(); - // Previous blocks in modified sections (i.e. we skip sections that weren't modified) - char[][] previousIds = previous.getIdArrays(); - for (int layer = 0; layer < currentIds.length; layer++) { - char[] currentLayer = currentIds[layer]; - char[] previousLayer = previousIds[layer]; - if (currentLayer == null) { - continue; - } - int startY = layer << 4; - for (int y = 0; y < 16; y++) { - short[][] i1 = FaweCache.CACHE_J[y]; - int yy = y + startY; - for (int x = 0; x < 16; x++) { - int xx = x + bx; - short[] i2 = i1[x]; - for (int z = 0; z < 16; z++) { - int zz = z + bz; - int index = i2[z]; - int combinedIdCurrent = currentLayer[index]; - switch (combinedIdCurrent) { - case 0: - continue; - case 1: - combinedIdCurrent = 0; - default: - char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0; - if (combinedIdCurrent != combinedIdPrevious) { - add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent); + public void run(final FaweChunk previous, final FaweChunk next) { + waiting.incrementAndGet(); + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + try { + int cx = previous.getX(); + int cz = previous.getZ(); + int bx = cx << 4; + int bz = cz << 4; + // Biome changes + { + // TODO + } + // Block changes + { + // Current blocks + char[][] currentIds = next.getCombinedIdArrays(); + // Previous blocks in modified sections (i.e. we skip sections that weren't modified) + char[][] previousIds = previous.getCombinedIdArrays(); + for (int layer = 0; layer < currentIds.length; layer++) { + char[] currentLayer = currentIds[layer]; + char[] previousLayer = previousIds[layer]; + if (currentLayer == null) { + continue; + } + int startY = layer << 4; + for (int y = 0; y < 16; y++) { + short[][] i1 = FaweCache.CACHE_J[y]; + int yy = y + startY; + for (int x = 0; x < 16; x++) { + int xx = x + bx; + short[] i2 = i1[x]; + for (int z = 0; z < 16; z++) { + int zz = z + bz; + int index = i2[z]; + int combinedIdCurrent = currentLayer[index]; + switch (combinedIdCurrent) { + case 0: + continue; + case 1: + combinedIdCurrent = 0; + default: + char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0; + if (combinedIdCurrent != combinedIdPrevious) { + synchronized (lock) { + add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent); + } + } + } } + } } } } + // Tile changes + { + // Tiles created + Map tiles = next.getTiles(); + for (Map.Entry entry : tiles.entrySet()) { + synchronized (lock) { + addTileCreate(entry.getValue()); + } + } + // Tiles removed + tiles = previous.getTiles(); + for (Map.Entry entry : tiles.entrySet()) { + synchronized (lock) { + addTileRemove(entry.getValue()); + } + } + } + // Entity changes + { + // Entities created + Set entities = next.getEntities(); + for (CompoundTag entityTag : entities) { + synchronized (lock) { + addEntityCreate(entityTag); + } + } + // Entities removed + entities = previous.getEntities(); + for (CompoundTag entityTag : entities) { + synchronized (lock) { + addEntityRemove(entityTag); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } finally { + waiting.decrementAndGet(); + synchronized (lock) { + lock.notifyAll(); + } } } - } - - // Tile changes - { - // Tiles created - Map tiles = next.getTiles(); - for (Map.Entry entry : tiles.entrySet()) { - addTileCreate(entry.getValue()); - } - // Tiles removed - tiles = previous.getTiles(); - for (Map.Entry entry : tiles.entrySet()) { - addTileRemove(entry.getValue()); - } - } - // Entity changes - { - // Entities created - Set entities = next.getEntities(); - for (CompoundTag entityTag : entities) { - addEntityCreate(entityTag); - } - // Entities removed - entities = previous.getEntities(); - for (CompoundTag entityTag : entities) { - addEntityRemove(entityTag); - } - } + }); } }); } diff --git a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java index 95a4217d..f1be6a95 100644 --- a/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java +++ b/core/src/main/java/com/boydti/fawe/object/changeset/FaweStreamChangeSet.java @@ -4,11 +4,11 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.change.MutableBlockChange; import com.boydti.fawe.object.change.MutableEntityChange; import com.boydti.fawe.object.change.MutableTileChange; -import com.google.common.collect.Iterators; import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.history.change.Change; +import com.sk89q.worldedit.world.World; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -21,9 +21,12 @@ import net.jpountz.lz4.LZ4OutputStream; public abstract class FaweStreamChangeSet extends FaweChangeSet { + public FaweStreamChangeSet(World world) { + super(world); + } + @Override public int size() { - System.out.println("SIZE: " + blockSize); // Flush so we can accurately get the size flush(); return blockSize; @@ -172,7 +175,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet { if (read0 == -1) { return null; } - System.out.println("r0: " + read0); int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX; int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ; int y = is.read() & 0xff; @@ -192,7 +194,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet { change.id = (short) ((from2 << 4) + (from1 >> 4)); change.data = (byte) (from1 & 0xf); } - System.out.println("CHANGE: " + change.id); return change; } catch (Exception ignoreEOF) { ignoreEOF.printStackTrace(); @@ -206,10 +207,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet { last = read(); } if (last != null) { - System.out.println("HAS NEXT!"); return true; } - System.out.println("NO NEXT"); try { is.close(); } catch (IOException e) { @@ -333,18 +332,43 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet { } public Iterator getIterator(final boolean dir) { - System.out.println("GET ITERATOR: " + dir); flush(); try { - Iterator tileCreate = getTileIterator(getTileCreateIS(), true, dir); - Iterator tileRemove = getTileIterator(getTileRemoveIS(), false, dir); + final Iterator tileCreate = getTileIterator(getTileCreateIS(), true, dir); + final Iterator tileRemove = getTileIterator(getTileRemoveIS(), false, dir); - Iterator entityCreate = getEntityIterator(getEntityCreateIS(), true, dir); - Iterator entityRemove = getEntityIterator(getEntityRemoveIS(), false, dir); + final Iterator entityCreate = getEntityIterator(getEntityCreateIS(), true, dir); + final Iterator entityRemove = getEntityIterator(getEntityRemoveIS(), false, dir); - Iterator blockChange = getBlockIterator(dir); + final Iterator blockChange = getBlockIterator(dir); - return Iterators.concat(tileCreate, tileRemove, entityCreate, entityRemove, blockChange); + return new Iterator() { + Iterator[] iterators = new Iterator[]{tileCreate, tileRemove, entityCreate, entityRemove, blockChange}; + int i = 0; + Iterator current = iterators[0]; + + @Override + public boolean hasNext() { + if (current.hasNext()) { + return true; + } else if (i >= iterators.length - 1) { + return false; + } else { + current = iterators[++i]; + } + return hasNext(); + } + + @Override + public void remove() { + current.remove(); + } + + @Override + public Change next() { + return current.next(); + } + }; } catch (Exception e) { e.printStackTrace(); } 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 e9d79a8f..0ac0c436 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,17 +1,14 @@ package com.boydti.fawe.object.changeset; import com.boydti.fawe.config.Settings; -import com.boydti.fawe.object.io.FastByteArrayOutputStream; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTOutputStream; -import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.world.World; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import net.jpountz.lz4.LZ4Factory; -import net.jpountz.lz4.LZ4InputStream; -import net.jpountz.lz4.LZ4OutputStream; /** * ChangeSet optimized for low memory usage @@ -21,26 +18,67 @@ import net.jpountz.lz4.LZ4OutputStream; */ public class MemoryOptimizedHistory extends FaweStreamChangeSet { - private final Actor actor; + private byte[] ids; + private ByteArrayOutputStream idsStream; + private OutputStream idsStreamZip; - public MemoryOptimizedHistory(Actor actor) { - this.actor = actor; + private byte[] entC; + private ByteArrayOutputStream entCStream; + private NBTOutputStream entCStreamZip; + + private byte[] entR; + private ByteArrayOutputStream entRStream; + private NBTOutputStream entRStreamZip; + + private byte[] tileC; + private ByteArrayOutputStream tileCStream; + private NBTOutputStream tileCStreamZip; + + private byte[] tileR; + private ByteArrayOutputStream tileRStream; + private NBTOutputStream tileRStreamZip; + + public MemoryOptimizedHistory(World world) { + super(world); } @Override public boolean flush() { - if (idsStreamZip != null) { - try { - idsStream.flush(); - idsStreamZip.flush(); + super.flush(); + try { + if (idsStream != null) { idsStreamZip.close(); - ids = idsStream.toByteArray(true); + ids = idsStream.toByteArray(); idsStream = null; idsStreamZip = null; - } catch (IOException e) { - e.printStackTrace(); + } + if (entCStream != null) { + entCStreamZip.close(); + entC = entCStream.toByteArray(); + entCStream = null; + entCStreamZip = null; + } + if (entRStream != null) { + entRStreamZip.close(); + entR = entRStream.toByteArray(); + entRStream = null; + entRStreamZip = null; + } + if (tileCStream != null) { + tileCStreamZip.close(); + tileC = tileCStream.toByteArray(); + tileCStream = null; + tileCStreamZip = null; + } + if (tileRStream != null) { + tileRStreamZip.close(); + tileR = tileRStream.toByteArray(); + tileRStream = null; + tileRStreamZip = null; } return true; + } catch (IOException e) { + e.printStackTrace(); } return false; } @@ -50,75 +88,74 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet { return ids == null ? 0 : ids.length; } - private byte[] ids; - private FastByteArrayOutputStream idsStream; - private OutputStream idsStreamZip; - @Override public OutputStream getBlockOS(int x, int y, int z) throws IOException { if (idsStreamZip != null) { return idsStreamZip; } - LZ4Factory factory = LZ4Factory.fastestInstance(); - idsStream = new FastByteArrayOutputStream(Settings.BUFFER_SIZE); - idsStreamZip = new LZ4OutputStream(idsStream, Settings.BUFFER_SIZE, factory.fastCompressor()); - if (Settings.COMPRESSION_LEVEL > 0) { - idsStreamZip = new LZ4OutputStream(idsStreamZip, Settings.BUFFER_SIZE, factory.highCompressor()); - } setOrigin(x, z); - return idsStreamZip; - } - - @Override - public NBTOutputStream getEntityCreateOS() throws IOException { - return null; - } - - @Override - public NBTOutputStream getEntityRemoveOS() throws IOException { - return null; - } - - @Override - public NBTOutputStream getTileCreateOS() throws IOException { - return null; - } - - @Override - public NBTOutputStream getTileRemoveOS() throws IOException { - return null; + idsStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); + return idsStreamZip = getCompressedOS(idsStream); } @Override public InputStream getBlockIS() { - if (ids == null) { - return null; + return ids == null ? null : getCompressedIS(new ByteArrayInputStream(ids)); + } + + @Override + public NBTOutputStream getEntityCreateOS() throws IOException { + if (entCStreamZip != null) { + return entCStreamZip; } - InputStream is = new ByteArrayInputStream(ids); - is = new LZ4InputStream(is); - if (Settings.COMPRESSION_LEVEL > 0) { - is = new LZ4InputStream(is); + entCStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); + return entCStreamZip = new NBTOutputStream(getCompressedOS(entCStream)); + } + + @Override + public NBTOutputStream getEntityRemoveOS() throws IOException { + if (entRStreamZip != null) { + return entRStreamZip; } - return is; + entRStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); + return entRStreamZip = new NBTOutputStream(getCompressedOS(entRStream)); + } + + @Override + public NBTOutputStream getTileCreateOS() throws IOException { + if (tileCStreamZip != null) { + return tileCStreamZip; + } + tileCStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); + return tileCStreamZip = new NBTOutputStream(getCompressedOS(tileCStream)); + } + + @Override + public NBTOutputStream getTileRemoveOS() throws IOException { + if (tileRStreamZip != null) { + return tileRStreamZip; + } + tileRStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE); + return tileRStreamZip = new NBTOutputStream(getCompressedOS(tileRStream)); } @Override public NBTInputStream getEntityCreateIS() throws IOException { - return null; + return entC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entC))); } @Override public NBTInputStream getEntityRemoveIS() throws IOException { - return null; + return entR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entR))); } @Override public NBTInputStream getTileCreateIS() throws IOException { - return null; + return tileC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileC))); } @Override public NBTInputStream getTileRemoveIS() throws IOException { - return null; + return tileR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileR))); } } diff --git a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java index 8aab7928..d891a0e3 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/FastWorldEditExtent.java @@ -154,6 +154,7 @@ public class FastWorldEditExtent extends AbstractDelegateExtent { case 178: { if (block.hasNbtData()) { CompoundTag nbt = block.getNbtData(); + MainUtil.setPosition(nbt, x, y, z); queue.setTile(x, y, z, nbt); } queue.setBlock(x, y, z, id, (byte) block.getData()); diff --git a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java index 5d6191c7..5bd6d969 100644 --- a/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java +++ b/core/src/main/java/com/boydti/fawe/object/extent/ProcessedWEExtent.java @@ -75,12 +75,12 @@ public class ProcessedWEExtent extends FaweRegionExtent { } if (WEManager.IMP.maskContains(this.mask, (int) location.x, (int) location.z)) { if (limit.MAX_CHANGES-- < 0) { - WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); + WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES); return false; } return super.setBlock(location, block); } else if (limit.MAX_FAILS-- < 0) { - WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); + WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS); } return false; } diff --git a/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java b/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java new file mode 100644 index 00000000..9aeb208b --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/progress/DefaultProgressTracker.java @@ -0,0 +1,76 @@ +package com.boydti.fawe.object.progress; + +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.util.FaweQueue; +import com.boydti.fawe.util.StringMan; +import com.boydti.fawe.util.TaskManager; +import org.bukkit.Bukkit; + +public class DefaultProgressTracker extends RunnableVal2 { + + private final FawePlayer player; + private final long start; + + public DefaultProgressTracker(FawePlayer player) { + this.start = System.currentTimeMillis(); + this.player = player; + } + + private int amountQueue = 0; + private int amountDispatch = 0; + private long lastTick = 0; + + @Override + public void run(FaweQueue.ProgressType type, Integer amount) { + switch (type) { + case DISPATCH: + amountDispatch = amount; + break; + case QUEUE: + amountQueue = amount; + break; + case DONE: + if (amountDispatch > 64) { + done(); + } + return; + } + if (amountQueue > 64 || amountDispatch > 64) { + send(); + } + } + + private void done() { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + final long time = System.currentTimeMillis() - start; + player.sendTitle("", BBC.PROGRESS_DONE.format(time / 1000d)); + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + player.resetTitle(); + } + }, 60); + } + }); + } + + public void send() { + TaskManager.IMP.task(new Runnable() { + @Override + public void run() { + long currentTick = Bukkit.getServer().getWorlds().get(0).getFullTime(); + if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) { + lastTick = currentTick; + String queue = StringMan.padRight("" + amountQueue, 3); + String dispatch = StringMan.padRight("" + amountDispatch, 3); + player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch)); + } + } + }); + } +} diff --git a/core/src/main/java/com/boydti/fawe/util/FaweQueue.java b/core/src/main/java/com/boydti/fawe/util/FaweQueue.java index 54909eb4..aa3aaffd 100644 --- a/core/src/main/java/com/boydti/fawe/util/FaweQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/FaweQueue.java @@ -10,14 +10,28 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.world.biome.BaseBiome; import java.util.HashSet; import java.util.Set; +import java.util.UUID; import java.util.concurrent.LinkedBlockingDeque; public abstract class FaweQueue { + public static enum ProgressType { + QUEUE, + DISPATCH, + DONE, + } + + public enum RelightMode { + MINIMAL, + OPTIMAL, + ALL, + } + public final String world; public LinkedBlockingDeque sessions; public long modified = System.currentTimeMillis(); public RunnableVal2 changeTask; + public RunnableVal2 progressTask; public FaweQueue(String world) { this.world = world; @@ -33,6 +47,16 @@ public abstract class FaweQueue { sessions.add(session); } + /** + * Add a progress task
+ * - Progress type + * - Amount of type + * @param progressTask + */ + public void setProgressTracker(RunnableVal2 progressTask) { + this.progressTask = progressTask; + } + public Set getEditSessions() { return sessions == null ? new HashSet() : new HashSet<>(sessions); } @@ -53,13 +77,15 @@ public abstract class FaweQueue { public abstract void setEntity(int x, int y, int z, CompoundTag tag); + public abstract void removeEntity(int x, int y, int z, UUID uuid); + public abstract boolean setBiome(final int x, final int z, final BaseBiome biome); public abstract FaweChunk getChunk(int x, int z); public abstract void setChunk(final FaweChunk chunk); - public abstract boolean fixLighting(final FaweChunk chunk, final boolean fixAll); + public abstract boolean fixLighting(final FaweChunk chunk, RelightMode mode); public abstract boolean isChunkLoaded(final int x, final int z); 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 3d3cbcdb..4c89f583 100644 --- a/core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -70,6 +70,9 @@ public class MainUtil { try { int elements = set.size(); int compressedSize = set.getCompressedSize(); + if (compressedSize == 0) { + return; + } /* * BlockVector * - reference to the object --> 8 bytes @@ -90,10 +93,9 @@ public class MainUtil { * This compares FAWE's usage to standard WE. */ int total = 128 * elements; - int current = compressedSize; - int ratio = total / current; - int saved = total - current; + int ratio = total / compressedSize; + int saved = total - compressedSize; if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive()) { BBC.COMPRESSED.send(actor, saved, ratio); @@ -103,6 +105,31 @@ public class MainUtil { } } + public static void smoothArray(int[] data, int width, int radius, int weight) { + int[] copy = data.clone(); + int length = data.length / width; + int diameter = 2 * radius + 1; + weight += diameter * diameter - 1; + for (int x = 0; x < width; x++) { + for (int y = 0; y < length; y++) { + int index = x + width * y; + int value = 0; + int count = 0; + for (int x2 = Math.max(0, x - radius); x2 <= Math.min(width - 1, x + radius); x2++) { + for (int y2 = Math.max(0, y - radius); y2 <= Math.min(length - 1, y + radius); y2++) { + count++; + int index2 = x2 + width * y2; + value += data[index2]; + + } + } + value += data[index] * (weight - count); + value = value / (weight); + data[index] = value; + } + } + } + public static void warnDeprecated(Class... alternatives) { StackTraceElement[] stack = new RuntimeException().getStackTrace(); if (stack.length > 1) { diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index 2389585c..aa9dc32c 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -89,11 +89,11 @@ public class SetQueue { // Disable the async catcher as it can't discern async vs parallel SET_TASK.value2.startSet(true); try { - if (Settings.UNSAFE_PARALLEL_THREADS <= 1) { + if (Settings.PARALLEL_THREADS <= 1) { SET_TASK.run(); } else { ArrayList threads = new ArrayList(); - for (int i = 0; i < Settings.UNSAFE_PARALLEL_THREADS; i++) { + for (int i = 0; i < Settings.PARALLEL_THREADS; i++) { threads.add(new Thread(SET_TASK)); } for (Thread thread : threads) { @@ -197,7 +197,6 @@ public class SetQueue { inactiveQueues.remove(queue); } } - return null; } } if (Settings.QUEUE_SIZE != -1) { diff --git a/core/src/main/java/com/boydti/fawe/util/StringMan.java b/core/src/main/java/com/boydti/fawe/util/StringMan.java index 51ddb3cf..38b6f558 100644 --- a/core/src/main/java/com/boydti/fawe/util/StringMan.java +++ b/core/src/main/java/com/boydti/fawe/util/StringMan.java @@ -40,6 +40,14 @@ public class StringMan { return count; } + public static String padRight(String s, int n) { + return String.format("%1$-" + n + "s", s); + } + + public static String padLeft(String s, int n) { + return String.format("%1$" + n + "s", s); + } + public static String getString(final Object obj) { if (obj == null) { return "null"; diff --git a/core/src/main/java/com/boydti/fawe/util/TaskManager.java b/core/src/main/java/com/boydti/fawe/util/TaskManager.java index c8bf590c..6985f59d 100644 --- a/core/src/main/java/com/boydti/fawe/util/TaskManager.java +++ b/core/src/main/java/com/boydti/fawe/util/TaskManager.java @@ -104,7 +104,6 @@ public abstract class TaskManager { /** * Break up a task and run it in fragments of 5ms.
* - Each task will run on the main thread.
- * - Usualy wait time is around 25ms
* @param objects - The list of objects to run the task for * @param task - The task to run on each object * @param whenDone - When the object task completes @@ -133,6 +132,7 @@ public abstract class TaskManager { /** * Quickly run a task on the main thread, and wait for execution to finish:
* - Useful if you need to access something from the Bukkit API from another thread
+ * - Usualy wait time is around 25ms
* @param function * @param * @return @@ -144,6 +144,7 @@ public abstract class TaskManager { /** * Quickly run a task on the main thread, and wait for execution to finish:
* - Useful if you need to access something from the Bukkit API from another thread
+ * - Usualy wait time is around 25ms
* @param function * @param timeout - How long to wait for execution * @param diff --git a/core/src/main/java/com/boydti/fawe/util/WEManager.java b/core/src/main/java/com/boydti/fawe/util/WEManager.java index 75f5c9b5..6a65a07e 100644 --- a/core/src/main/java/com/boydti/fawe/util/WEManager.java +++ b/core/src/main/java/com/boydti/fawe/util/WEManager.java @@ -5,6 +5,7 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.regions.FaweMask; @@ -58,36 +59,50 @@ public class WEManager { } } + /** + * Get a player's mask + * @param player + * @return + */ public HashSet getMask(final FawePlayer player) { - final HashSet regions = new HashSet<>(); - if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) { - regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE)); - player.deleteMeta("lastmask"); - return regions; - } - for (final FaweMaskManager manager : this.managers) { - if (player.hasPermission("fawe." + manager.getKey())) { - final FaweMask mask = manager.getMask(player); - if (mask != null) { - regions.addAll(mask.getRegions()); + return TaskManager.IMP.sync(new RunnableVal>() { + @Override + public void run(HashSet value) { + final HashSet regions = new HashSet<>(); + if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) { + regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE)); + player.deleteMeta("lastmask"); + this.value = regions; + return; } - } - } - if (regions.size() == 0) { - HashSet mask = player.>getMeta("lastmask"); - if (mask != null) { - FaweLocation loc = player.getLocation(); - for (RegionWrapper region : mask) { - if (region.isIn(loc.x, loc.z)) { - player.deleteMeta("lastmask"); - return regions; + for (final FaweMaskManager manager : managers) { + if (player.hasPermission("fawe." + manager.getKey())) { + final FaweMask mask = manager.getMask(player); + if (mask != null) { + regions.addAll(mask.getRegions()); + } } } - return mask; + if (regions.size() == 0) { + HashSet mask = player.>getMeta("lastmask"); + if (mask != null) { + FaweLocation loc = player.getLocation(); + for (RegionWrapper region : mask) { + if (region.isIn(loc.x, loc.z)) { + player.deleteMeta("lastmask"); + this.value = regions; + return; + } + } + this.value = mask; + return; + } + } + player.setMeta("lastmask", regions); + this.value = regions; + return; } - } - player.setMeta("lastmask", regions); - return regions; + }, 1000); } public boolean intersects(final RegionWrapper region1, final RegionWrapper region2) { diff --git a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java index 45ec30c7..001519af 100644 --- a/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java +++ b/core/src/main/java/com/boydti/fawe/wrappers/PlayerWrapper.java @@ -205,7 +205,7 @@ public class PlayerWrapper implements Player { TaskManager.IMP.sync(new RunnableVal() { @Override public void run(Object value) { - edit.queue.next(); + edit.getQueue().next(); setPosition(new Vector(x + 0.5, y, z + 0.5)); } }); diff --git a/core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java b/core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java index c858b4b7..0cb7970a 100644 --- a/core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java +++ b/core/src/main/java/com/boydti/fawe/wrappers/WorldWrapper.java @@ -229,6 +229,7 @@ public class WorldWrapper extends AbstractWorld { @Override public boolean regenerate(final Region region, final EditSession session) { final FaweQueue queue = session.getQueue(); + queue.setChangeTask(null); final FaweChangeSet fcs = (FaweChangeSet) session.getChangeSet(); final FaweRegionExtent fe = session.getRegionExtent(); session.setChangeSet(fcs); @@ -274,22 +275,25 @@ public class WorldWrapper extends AbstractWorld { } } } else { + Vector mutable = new Vector(0,0,0); for (int x = 0; x < 16; x++) { int xx = x + bx; + mutable.x = xx; for (int z = 0; z < 16; z++) { int zz = z + bz; + mutable.z = zz; for (int y = 0; y < getMaxY() + 1; y++) { - final Vector loc = new Vector(xx, y, zz); + mutable.y = y; int from = queue.getCombinedId4Data(xx, y, zz); - boolean contains = (fe != null && fe.contains(xx, y, zz)) && region.contains(loc); + boolean contains = (fe == null || fe.contains(xx, y, zz)) && region.contains(mutable); if (contains) { if (fcs != null) { if (!FaweCache.hasNBT(from >> 4)) { fcs.add(xx, y, zz, from, 0); } else { try { - BaseBlock block = getLazyBlock(loc); - fcs.add(loc, block, FaweCache.CACHE_BLOCK[0]); + BaseBlock block = getLazyBlock(mutable); + fcs.add(mutable, block, FaweCache.CACHE_BLOCK[0]); } catch (Throwable e) { fcs.add(xx, y, zz, from, 0); } @@ -298,30 +302,14 @@ public class WorldWrapper extends AbstractWorld { } else { short id = (short) (from >> 4); byte data = (byte) (from & 0xf); - if (!FaweCache.hasNBT(id)) { - queue.setBlock(xx, y, zz, id, data); - } else { - try { - final BaseBlock block = getBlock(loc); - final Vector v = new Vector(loc.x, loc.y, loc.z); - queue.addNotifyTask(cx, cz, new Runnable() { - @Override - public void run() { - try { - setBlock(v, block, false); - } catch (WorldEditException e) { - e.printStackTrace(); - } - } - }); - } catch (Throwable e) { - e.printStackTrace(); - queue.setBlock(xx, y, zz, id, data); + queue.setBlock(xx, y, zz, id, data); + if (FaweCache.hasNBT(id)) { + BaseBlock block = getBlock(new Vector(xx, y, zz)); + if (block.hasNbtData()) { + queue.setTile(xx, y, zz, block.getNbtData()); } } } - - } } } diff --git a/core/src/main/java/com/sk89q/worldedit/EditSession.java b/core/src/main/java/com/sk89q/worldedit/EditSession.java index 01b2eabb..e519fefc 100644 --- a/core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -26,9 +26,11 @@ import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.object.NullChangeSet; import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.changeset.CPUOptimizedChangeSet; import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; @@ -38,6 +40,7 @@ import com.boydti.fawe.object.extent.FaweRegionExtent; import com.boydti.fawe.object.extent.MemoryCheckingExtent; import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent; +import com.boydti.fawe.object.progress.DefaultProgressTracker; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.Perm; @@ -158,21 +161,21 @@ public class EditSession implements Extent { BEFORE_HISTORY, BEFORE_REORDER, BEFORE_CHANGE } - public World world; - public Actor actor; - public FaweChangeSet changeSet; - public EditSessionWrapper wrapper; - public MaskingExtent maskingExtent; - public FaweRegionExtent regionExtent; - public Extent primaryExtent; - public Extent bypassReorderHistory; - public Extent bypassHistory; - public Extent bypassNone; - public SurvivalModeExtent lazySurvivalExtent; - public boolean fastmode; - public Mask oldMask; - public FaweLimit limit = FaweLimit.MAX.copy(); - public FaweQueue queue; + private World world; + private Actor actor; + private FaweChangeSet changeSet; + private EditSessionWrapper wrapper; + private MaskingExtent maskingExtent; + private FaweRegionExtent regionExtent; + private Extent primaryExtent; + private Extent bypassReorderHistory; + private Extent bypassHistory; + private Extent bypassNone; + private SurvivalModeExtent lazySurvivalExtent; + private boolean fastmode; + private Mask oldMask; + private FaweLimit limit = FaweLimit.MAX.copy(); + private FaweQueue queue; public static BaseBiome nullBiome = new BaseBiome(0); public static BaseBlock nullBlock = FaweCache.CACHE_BLOCK[0]; @@ -229,13 +232,13 @@ public class EditSession implements Extent { this.bypassReorderHistory = extent; this.bypassHistory = extent; this.bypassNone = extent; - this.changeSet = new NullChangeSet(); + this.changeSet = new NullChangeSet(world); this.wrapper = Fawe.imp().getEditSessionWrapper(this); return; } // Wrap the world - this.world = (world = new WorldWrapper((AbstractWorld) world)); + this.world = (world instanceof WorldWrapper) ? world : (world = new WorldWrapper((AbstractWorld) world)); // Delegate some methods to an implementation specific class this.wrapper = Fawe.imp().getEditSessionWrapper(this); @@ -252,7 +255,7 @@ public class EditSession implements Extent { this.bypassReorderHistory = extent; this.bypassHistory = extent; this.bypassNone = extent; - this.changeSet = new NullChangeSet(); + this.changeSet = new NullChangeSet(world); return; } @@ -261,8 +264,10 @@ public class EditSession implements Extent { final FawePlayer fp = FawePlayer.wrap(actor); final LocalSession session = fp.getSession(); this.fastmode = session.hasFastMode(); - if (fp.hasWorldEditBypass()) { - this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), true, true); + boolean bypass = fp.hasWorldEditBypass(); + this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), bypass, true); + queue.setProgressTracker(new DefaultProgressTracker(fp)); + if (bypass) { queue.addEditSession(this); // Bypass skips processing and area restrictions extent = primaryExtent = new FastWorldEditExtent(world, queue); @@ -278,7 +283,6 @@ public class EditSession implements Extent { } mask = null; } else { - this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), false, true); queue.addEditSession(this); this.limit = fp.getLimit(); mask = WEManager.IMP.getMask(fp); @@ -326,13 +330,13 @@ public class EditSession implements Extent { extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); // History - this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : new MemoryOptimizedHistory(actor); + this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : (Settings.COMBINE_HISTORY_STAGE && Settings.COMPRESSION_LEVEL == 0) ? new CPUOptimizedChangeSet(world) : new MemoryOptimizedHistory(world); + this.changeSet = this.wrapper.wrapChangeSet(this, limit, extent, this.changeSet, queue, fp); if (Settings.COMBINE_HISTORY_STAGE) { changeSet.addChangeTask(queue); } else { - extent = this.wrapper.getHistoryExtent(this, limit, extent, this.changeSet, queue, fp); + extent = new HistoryExtent(this, limit, extent, changeSet, queue); } - // Region restrictions if mask is not null if (mask != null) { extent = this.regionExtent = new ProcessedWEExtent(extent, mask, limit); @@ -367,6 +371,15 @@ public class EditSession implements Extent { return regionExtent; } + /** + * Get the actor + * @return + */ + @Nullable + public Actor getActor() { + return actor; + } + public boolean cancel() { // Cancel this if (primaryExtent != null && queue != null) { @@ -388,6 +401,12 @@ public class EditSession implements Extent { } } + public void addNotifyTask(Runnable whenDone) { + if (queue != null) { + queue.addNotifyTask(whenDone); + } + } + public FastWorldEditExtent getPrimaryExtent() { return (FastWorldEditExtent) primaryExtent; } diff --git a/core/src/main/java/com/sk89q/worldedit/LocalSession.java b/core/src/main/java/com/sk89q/worldedit/LocalSession.java index b78ba184..7f02c6db 100644 --- a/core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -26,6 +26,7 @@ import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard; import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.SetQueue; +import com.boydti.fawe.util.TaskManager; import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Options; import com.sk89q.jchronic.utils.Span; @@ -199,10 +200,16 @@ public class LocalSession { * @param editSession the edit session */ public void remember(EditSession editSession) { - remember(editSession, true); + remember(editSession, true, false); } - public void remember(final EditSession editSession, final boolean append) { + public void remember(final EditSession editSession, final boolean append, final boolean sendMessage) { + if (editSession == null) { + return; + } + if (Settings.STORE_HISTORY_ON_DISK) { + MAX_HISTORY_SIZE = Integer.MAX_VALUE; + } // Enqueue it if (editSession.getQueue() != null) { FaweQueue queue = editSession.getQueue(); @@ -215,8 +222,10 @@ public class LocalSession { if (editSession.size() == 0 || editSession.hasFastMode()) return; // Destroy any sessions after this undo point - while (historyPointer < history.size()) { - history.remove(historyPointer); + if (append) { + while (historyPointer < history.size()) { + history.remove(historyPointer); + } } ChangeSet set = editSession.getChangeSet(); if (set instanceof FaweStreamChangeSet) { @@ -225,14 +234,19 @@ public class LocalSession { editSession.getQueue().addNotifyTask(new Runnable() { @Override public void run() { - if (fcs.flush() && append) { - MainUtil.sendCompressedMessage(fcs, editSession.actor); - } + TaskManager.IMP.async(new Runnable() { + @Override + public void run() { + if (fcs.flush() && append && sendMessage) { + MainUtil.sendCompressedMessage(fcs, editSession.getActor()); + } + } + }); } }); } else { - if (fcs.flush() && append) { - MainUtil.sendCompressedMessage(fcs, editSession.actor); + if (fcs.flush() && append && sendMessage) { + MainUtil.sendCompressedMessage(fcs, editSession.getActor()); } } @@ -241,13 +255,15 @@ public class LocalSession { } if (append) { history.add(editSession); + historyPointer = history.size(); } else { history.add(0, editSession); + historyPointer++; } while (history.size() > MAX_HISTORY_SIZE) { history.remove(0); + historyPointer--; } - historyPointer = append ? history.size() : historyPointer + 1; } /** @@ -278,7 +294,6 @@ public class LocalSession { newEditSession.enableQueue(); newEditSession.setFastMode(fastMode); editSession.undo(newEditSession); - System.out.println("UNDO: " + historyPointer + " | " + history.size()); return editSession; } else { historyPointer = 0; @@ -306,7 +321,6 @@ public class LocalSession { */ public EditSession redo(@Nullable BlockBag newBlockBag, Player player) { checkNotNull(player); - System.out.println("CHECK REDO: " + historyPointer + " | " + history.size()); if (historyPointer < history.size()) { EditSession editSession = history.get(historyPointer); EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory() @@ -315,10 +329,7 @@ public class LocalSession { newEditSession.setFastMode(fastMode); editSession.redo(newEditSession); ++historyPointer; - System.out.println("CAN REDO"); return editSession; - } else { - System.out.println("POINTER"); } return null; diff --git a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java index 200ef2fa..c58fc4f4 100644 --- a/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java +++ b/core/src/main/java/com/sk89q/worldedit/command/composition/SelectionCommand.java @@ -155,7 +155,7 @@ public class SelectionCommand extends SimpleCommand { } }); queue.enqueue(); - editSession.setChangeSet(new NullChangeSet()); + editSession.setChangeSet(new NullChangeSet(null)); BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea())); return null; } 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 df4d5e09..90e6d958 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 @@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.platform; import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.exception.FaweException; -import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.wrappers.PlayerWrapper; import com.google.common.base.Joiner; @@ -224,7 +223,6 @@ public final class CommandManager { TaskManager.IMP.async(new Runnable() { @Override public void run() { - System.out.println("COMMAND START!"); final Actor actor = platformManager.createProxyActor(event.getActor()); String[] split = commandDetection(event.getArguments().split(" ")); @@ -233,11 +231,11 @@ public final class CommandManager { return; } - LocalSession session = worldEdit.getSessionManager().get(actor); + final LocalSession session = worldEdit.getSessionManager().get(actor); LocalConfiguration config = worldEdit.getConfiguration(); CommandLocals locals = new CommandLocals(); - FawePlayer fp = FawePlayer.wrap(actor); + final FawePlayer fp = FawePlayer.wrap(actor); if (fp != null) { if (fp.getMeta("fawe_action") != null) { BBC.WORLDEDIT_COMMAND_LIMIT.send(fp); @@ -285,36 +283,33 @@ public final class CommandManager { log.log(Level.SEVERE, "An unknown error occurred", e); } } finally { - System.out.println("DONE!"); - if (fp != null) { - fp.deleteMeta("fawe_action"); - } - EditSession editSession = locals.get(EditSession.class); - + final EditSession editSession = locals.get(EditSession.class); + boolean delayed = false; if (editSession != null) { - session.remember(editSession); editSession.flushQueue(); - final long time = System.currentTimeMillis() - start; - if (time > 5 && editSession.size() != 0) { - SetQueue.IMP.addTask(new Runnable() { + worldEdit.flushBlockBag(actor, editSession); + } + if (fp != null) { + if (editSession != null && editSession.size() > 0 && editSession.getQueue() != null) { + delayed = true; + editSession.getQueue().addNotifyTask(new Runnable() { @Override public void run() { + session.remember(editSession, true, true); + fp.deleteMeta("fawe_action"); final long time = System.currentTimeMillis() - start; - BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); + if (time > 5) { + BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); + } } }); } - if (config.profile) { - int changed = editSession.getBlockChangeCount(); - if (time > 0) { - double throughput = changed / (time / 1000.0); - actor.printDebug((time / 1000.0) + "s elapsed (history: " + changed + " changed; " + Math.round(throughput) + " blocks/sec)."); - } else { - actor.printDebug((time / 1000.0) + "s elapsed."); - } + } + if (!delayed) { + if (fp != null) { + fp.deleteMeta("fawe_action"); } - - worldEdit.flushBlockBag(actor, editSession); + session.remember(editSession, true, true); } } } diff --git a/pom.xml b/pom.xml index 35c06ece..c6e0e232 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,13 @@ bukkit18/src/main/resources/ + + false + + **/*.* + + core/src/main/resources/ + diff --git a/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java b/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java index 3085a4c1..af14464c 100644 --- a/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java +++ b/sponge/src/main/java/com/boydti/fawe/SpongeCommand.java @@ -47,11 +47,11 @@ public class SpongeCommand implements CommandCallable { @Override public Optional getHelp(final CommandSource cmd) { - return Optional.of(Text.of("/")); + return Optional.of(Text.of("/")); } @Override public Text getUsage(final CommandSource cmd) { - return Text.of("/"); + return Text.of("/"); } } diff --git a/sponge/src/main/java/com/boydti/fawe/sponge/v1_8/SpongeQueue_ALL.java b/sponge/src/main/java/com/boydti/fawe/sponge/v1_8/SpongeQueue_ALL.java index f0260e08..afb30210 100644 --- a/sponge/src/main/java/com/boydti/fawe/sponge/v1_8/SpongeQueue_ALL.java +++ b/sponge/src/main/java/com/boydti/fawe/sponge/v1_8/SpongeQueue_ALL.java @@ -130,7 +130,7 @@ public class SpongeQueue_ALL extends NMSMappedFaweQueue blockWorker = spongeChunk.getBlockWorker(); blockWorker.map(new BlockVolumeMapper() { @Override