Dynamic chunk rendering (experimental section in config)
Use local files for CFI heightmaps: file://<file-path>
- Root directory is plugins/FastAsyncWorldEdit/heightmap
Optimize anvil for 1.12
Add safety checks to anvil commands
Move anvil command implementation to com.boydti.fawe.jnbt.anvil.filters
Add anvil chunk delete
Add MCAWriter mca offset method
Fix count -d
Fix taskbuilder split task concurrency issue
This commit is contained in:
Jesse Boyd 2017-06-16 15:28:10 +10:00
parent 0f3138e894
commit 7ae2d65607
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
34 changed files with 1287 additions and 445 deletions

View File

@ -79,6 +79,7 @@ subprojects {
repositories {
mavenCentral()
maven {url "https://repo.destroystokyo.com/repository/maven-public//"}
maven { url = "https://mvnrepository.com/artifact/"}
maven {url "http://ci.emc.gs/nexus/content/groups/aikar/" }
maven {url "http://ci.mengcraft.com:8080/plugin/repository/everything/"}

View File

@ -2,6 +2,7 @@ repositories {
flatDir {dirs 'lib'}
}
dependencies {
compile 'com.destroystokyo.paper:paper-api:1.11.2-R0.1-SNAPSHOT'
compile project(':core')
compile 'org.bukkit.craftbukkitv1_10:craftbukkitv1_10:1.10'
compile 'org.bukkit.craftbukkitv1_11:Craftbukkit:1.11'

View File

@ -21,7 +21,6 @@ import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.bukkit.EditSessionBlockChangeDelegate;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
@ -44,9 +43,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkPopulateEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.plugin.Plugin;
import org.primesoft.blockshub.BlocksHubBukkit;
@ -87,6 +83,9 @@ public class FaweBukkit implements IFawe, Listener {
debug(" - This is only a recommendation");
debug("==============================");
}
if (Bukkit.getVersion().contains("git-Paper") && Settings.IMP.EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING) {
new RenderListener(plugin);
}
} catch (final Throwable e) {
MainUtil.handleError(e);
Bukkit.getServer().shutdown();
@ -411,29 +410,6 @@ public class FaweBukkit implements IFawe, Listener {
// }
// }
private boolean runChunkLoad = false;
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
if (runChunkLoad) return;
try {
runChunkLoad = true;
SetQueue.IMP.runMiscTasks();
} finally {
runChunkLoad = false;
}
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
SetQueue.IMP.runMiscTasks();
}
@EventHandler
public void onChunkPopulate(ChunkPopulateEvent event) {
SetQueue.IMP.runMiscTasks();
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();

View File

@ -0,0 +1,135 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.TaskManager;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
public class RenderListener implements Listener {
private final Map<UUID, int[]> views = new ConcurrentHashMap<>();
private Iterator<Map.Entry<UUID, int[]>> entrySet;
private final long GLOBAL_START = System.currentTimeMillis();
private int OFFSET = 6;
public RenderListener(Plugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
TaskManager.IMP.repeat(new Runnable() {
private long last = 0;
@Override
public void run() {
if (views.isEmpty()) return;
long now = System.currentTimeMillis();
int tps32 = (int) (Math.round(Fawe.get().getTimer().getTPS()) * 32);
long diff = now - last;
last = now;
if (diff > 75) {
OFFSET = diff > 100 ? 0 : 4;
return;
}
int timeOut;
if (diff < 55 && tps32 > 608) {
OFFSET = 8;
timeOut = 2;
} else {
int tpsSqr = tps32 * tps32;
OFFSET = 1 + (tps32 / 102400);
timeOut = 162 - (tps32 / 2560);
}
if (entrySet == null || !entrySet.hasNext()) {
entrySet = views.entrySet().iterator();
}
int nowTick = (int) (Fawe.get().getTimer().getTick());
while (entrySet.hasNext()) {
Map.Entry<UUID, int[]> entry = entrySet.next();
int[] value = entry.getValue();
if (nowTick - value[1] >= timeOut) {
value[1] = nowTick + 1;
Player player = Bukkit.getPlayer(entry.getKey());
setViewDistance(player, Math.max(4, value[0] + 1));
long spent = System.currentTimeMillis() - now;
if (spent > 5) {
if (spent > 10)
value[1] = nowTick + 20;
return;
}
}
}
}
}, 1);
}
public void setViewDistance(Player player, int value) {
UUID uuid = player.getUniqueId();
if (value == 10) {
views.remove(uuid);
} else {
int[] val = views.get(uuid);
if (val == null) {
val = new int[] {value, (int) Fawe.get().getTimer().getTick()};
UUID uid = player.getUniqueId();
views.put(uid, val);
} else {
if (value <= val[0]) {
val[1] = (int) Fawe.get().getTimer().getTick();
}
if (val[0] == value) {
return;
} else {
val[0] = value;
}
}
}
player.setViewDistance(value);
}
public int getViewDistance(Player player) {
int[] value = views.get(player.getUniqueId());
return value == null ? 10 : value[0];
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) {
setViewDistance(event.getPlayer(), 1);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Location from = event.getFrom();
Location to = event.getTo();
if (from.getBlockX() >> OFFSET != to.getBlockX() >> OFFSET || from.getBlockZ() >> OFFSET != to.getBlockZ() >> OFFSET) {
Player player = event.getPlayer();
int currentView = getViewDistance(player);
setViewDistance(player, Math.max(currentView - 1, 1));
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) {
Player player = event.getPlayer();
setViewDistance(player, 1);
FawePlayer fp = FawePlayer.wrap(player);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerLeave(org.bukkit.event.player.PlayerQuitEvent event) {
Player player = event.getPlayer();
UUID uid = player.getUniqueId();
views.remove(uid);
FawePlayer fp = FawePlayer.wrap(player);
}
}

View File

@ -39,6 +39,7 @@ import net.minecraft.server.v1_10_R1.BiomeBase;
import net.minecraft.server.v1_10_R1.BiomeCache;
import net.minecraft.server.v1_10_R1.Block;
import net.minecraft.server.v1_10_R1.BlockPosition;
import net.minecraft.server.v1_10_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_10_R1.ChunkProviderGenerate;
import net.minecraft.server.v1_10_R1.ChunkProviderServer;
import net.minecraft.server.v1_10_R1.ChunkSection;
@ -457,6 +458,7 @@ public class BukkitQueue_1_10 extends BukkitQueue_0<net.minecraft.server.v1_10_R
for (net.minecraft.server.v1_10_R1.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk);
sendChunk(chunk, 0);
}
}

View File

@ -39,6 +39,7 @@ import net.minecraft.server.v1_11_R1.BiomeBase;
import net.minecraft.server.v1_11_R1.BiomeCache;
import net.minecraft.server.v1_11_R1.Block;
import net.minecraft.server.v1_11_R1.BlockPosition;
import net.minecraft.server.v1_11_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_11_R1.ChunkProviderGenerate;
import net.minecraft.server.v1_11_R1.ChunkProviderServer;
import net.minecraft.server.v1_11_R1.ChunkSection;
@ -300,6 +301,7 @@ public class BukkitQueue_1_11 extends BukkitQueue_0<net.minecraft.server.v1_11_R
for (net.minecraft.server.v1_11_R1.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk);
sendChunk(chunk, 0);
}
}

View File

@ -10,11 +10,13 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.brush.visualization.VisualChunk;
import com.boydti.fawe.object.collection.PrimitiveList;
import com.boydti.fawe.object.visitor.FaweChunkVisitor;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.task.TaskBuilder;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
@ -34,13 +36,17 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
import net.minecraft.server.v1_12_R1.BiomeBase;
import net.minecraft.server.v1_12_R1.BiomeCache;
import net.minecraft.server.v1_12_R1.Block;
import net.minecraft.server.v1_12_R1.BlockPosition;
import net.minecraft.server.v1_12_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_12_R1.ChunkProviderGenerate;
import net.minecraft.server.v1_12_R1.ChunkProviderServer;
import net.minecraft.server.v1_12_R1.ChunkRegionLoader;
import net.minecraft.server.v1_12_R1.ChunkSection;
import net.minecraft.server.v1_12_R1.DataPaletteBlock;
import net.minecraft.server.v1_12_R1.Entity;
@ -51,6 +57,7 @@ import net.minecraft.server.v1_12_R1.EnumDifficulty;
import net.minecraft.server.v1_12_R1.EnumGamemode;
import net.minecraft.server.v1_12_R1.EnumSkyBlock;
import net.minecraft.server.v1_12_R1.IBlockData;
import net.minecraft.server.v1_12_R1.IChunkLoader;
import net.minecraft.server.v1_12_R1.IDataManager;
import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.NBTTagCompound;
@ -100,6 +107,8 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
protected static Field fieldBiomes2;
protected static Field fieldGenLayer1;
protected static Field fieldGenLayer2;
protected static Field fieldChunks;
protected static Field fieldChunkLoader;
protected static MutableGenLayer genLayer;
protected static ChunkSection emptySection;
@ -130,6 +139,11 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
fieldGenLayer1.setAccessible(true);
fieldGenLayer2.setAccessible(true);
fieldChunks = ChunkProviderServer.class.getDeclaredField("chunks");
fieldChunkLoader = ChunkProviderServer.class.getDeclaredField("chunkLoader");
fieldChunks.setAccessible(true);
fieldChunkLoader.setAccessible(true);
fieldPalette = DataPaletteBlock.class.getDeclaredField("c");
fieldPalette.setAccessible(true);
fieldSize = DataPaletteBlock.class.getDeclaredField("e");
@ -252,65 +266,147 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
return super.regenerateChunk(world, x, z, biome, seed);
}
private void unload(RegionWrapper allowed, PrimitiveList<Long> chunks) {
ArrayDeque<net.minecraft.server.v1_12_R1.Chunk> queue = TaskManager.IMP.sync(new RunnableVal<ArrayDeque<net.minecraft.server.v1_12_R1.Chunk>>() {
@Override
public void run(ArrayDeque<net.minecraft.server.v1_12_R1.Chunk> value) {
this.value = new ArrayDeque<>();
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5;
int tcx = 31 + (allowed.maxX >> 9) << 5;
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_12_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) {
net.minecraft.server.v1_12_R1.Chunk chunk = iter.next();
int cx = chunk.locX;
int cz = chunk.locZ;
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
this.value.add(chunk);
if (getPlayerChunk(nmsWorld, cx, cz) != null) {
chunks.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
}
}
}
}
});
if (Fawe.get().getMainThread() == Thread.currentThread()) {
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
for (net.minecraft.server.v1_12_R1.Chunk chunk : queue) {
provider.unloadChunk(chunk, true);
}
World world = getWorld();
boolean autoSave = world.isAutoSave();
world.setAutoSave(true);
for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) {
provider.unloadChunks();
}
world.setAutoSave(autoSave);
} else {
new TaskBuilder().syncWhenFree(new TaskBuilder.SplitTask(50) {
@Override
public Object exec(Object previous) {
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
for (net.minecraft.server.v1_12_R1.Chunk chunk : queue) {
provider.unloadChunk(chunk, true);
split();
}
try {
IChunkLoader loader = (IChunkLoader) fieldChunkLoader.get(provider);
if (loader instanceof ChunkRegionLoader) {
ChunkRegionLoader crl = (ChunkRegionLoader) loader;
for (int i = 0; i < queue.size() && crl.a(); i++) {
split();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}).build();
}
}
@Override
public boolean setMCA(Runnable whileLocked, final RegionWrapper allowed, boolean unload) {
try {
TaskManager.IMP.sync(new RunnableVal<Object>() {
Object lock = new Object();
ForkJoinPool pool = new ForkJoinPool();
PrimitiveList<Long> chunks = new PrimitiveList<Long>(Long.class);
if (unload && Fawe.get().getMainThread() != Thread.currentThread()) unload(allowed, chunks);
Thread operation = new Thread(new Runnable() {
@Override
public void run(Object value) {
public void run() {
try {
synchronized (lock) {
lock.wait();
}
synchronized (RegionFileCache.class) {
ArrayDeque<net.minecraft.server.v1_12_R1.Chunk> chunks = new ArrayDeque<>();
World world = getWorld();
world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) { // Unload chunks
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5;
int tcx = 31 + (allowed.maxX >> 9) << 5;
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<net.minecraft.server.v1_12_R1.Chunk> iter = provider.a().iterator();
while (iter.hasNext()) {
net.minecraft.server.v1_12_R1.Chunk chunk = iter.next();
int cx = chunk.locX;
int cz = chunk.locZ;
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
chunks.add(chunk);
}
}
for (net.minecraft.server.v1_12_R1.Chunk chunk : chunks) {
provider.unloadChunk(chunk, true);
}
}
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (net.minecraft.server.v1_12_R1.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
sendChunk(chunk, 0);
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
synchronized (RegionFileCache.class) {
operation.start(); // Async
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
if (unload) unload(allowed, chunks);
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
pool.submit(new Runnable() {
@Override
public void run() {
try {
regionFile.c();
} catch (IOException e) {
e.printStackTrace();
}
}
});
iter.remove();
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
synchronized (lock) {
lock.notifyAll();
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
operation.join();
new TaskBuilder().syncWhenFree(new TaskBuilder.SplitTask(5) {
@Override
public Object exec(Object previous) {
ChunkProviderServer provider = nmsWorld.getChunkProviderServer();
for (long pos : chunks) {
int x = (int) pos;
int z = (int) (pos >> 32);
net.minecraft.server.v1_12_R1.Chunk chunk = provider.getOrLoadChunkAt(x, z);
if (chunk != null) {
PlayerChunk pc = getPlayerChunk(nmsWorld, x, z);
sendChunk(pc, chunk, 0);
}
split();
}
return null;
}
}).buildAsync();
return true;
} catch (Throwable e) {
e.printStackTrace();
@ -474,7 +570,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
public void sendChunk(int x, int z, int bitMask) {
net.minecraft.server.v1_12_R1.Chunk chunk = getCachedChunk(getWorld(), x, z);
if (chunk != null) {
sendChunk(chunk, bitMask);
sendChunk(getPlayerChunk((WorldServer) chunk.getWorld(), chunk.locX, chunk.locZ), chunk, bitMask);
}
}
@ -532,28 +628,32 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
@Override
public void refreshChunk(FaweChunk fc) {
net.minecraft.server.v1_12_R1.Chunk chunk = getCachedChunk(getWorld(), fc.getX(), fc.getZ());
if (chunk != null) {
sendChunk(chunk, fc.getBitMask());
}
sendChunk(fc.getX(), fc.getZ(), fc.getBitMask());
}
public void sendChunk(net.minecraft.server.v1_12_R1.Chunk nmsChunk, int mask) {
WorldServer w = (WorldServer) nmsChunk.getWorld();
private PlayerChunk getPlayerChunk(WorldServer w, int cx, int cz) {
PlayerChunkMap chunkMap = w.getPlayerChunkMap();
PlayerChunk playerChunk = chunkMap.getChunk(nmsChunk.locX, nmsChunk.locZ);
PlayerChunk playerChunk = chunkMap.getChunk(cx, cz);
if (playerChunk == null) {
return;
return null;
}
if (playerChunk.c.isEmpty()) {
return;
return null;
}
return playerChunk;
}
public boolean sendChunk(PlayerChunk playerChunk, net.minecraft.server.v1_12_R1.Chunk nmsChunk, int mask) {
WorldServer w = (WorldServer) nmsChunk.getWorld();
if (playerChunk == null) {
return false;
}
if (mask == 0) {
PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535);
for (EntityPlayer player : playerChunk.c) {
player.playerConnection.sendPacket(packet);
}
return;
return true;
}
// Send chunks
boolean empty = false;
@ -582,6 +682,7 @@ public class BukkitQueue_1_12 extends BukkitQueue_0<net.minecraft.server.v1_12_R
}
}
}
return true;
}
public boolean hasEntities(net.minecraft.server.v1_12_R1.Chunk nmsChunk) {

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
@ -15,11 +16,13 @@ import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -28,6 +31,7 @@ import net.minecraft.server.v1_7_R4.Block;
import net.minecraft.server.v1_7_R4.Chunk;
import net.minecraft.server.v1_7_R4.ChunkCoordIntPair;
import net.minecraft.server.v1_7_R4.ChunkPosition;
import net.minecraft.server.v1_7_R4.ChunkProviderServer;
import net.minecraft.server.v1_7_R4.ChunkSection;
import net.minecraft.server.v1_7_R4.Entity;
import net.minecraft.server.v1_7_R4.EntityPlayer;
@ -42,6 +46,8 @@ import net.minecraft.server.v1_7_R4.NBTTagCompound;
import net.minecraft.server.v1_7_R4.NibbleArray;
import net.minecraft.server.v1_7_R4.PacketPlayOutMapChunk;
import net.minecraft.server.v1_7_R4.PlayerChunkMap;
import net.minecraft.server.v1_7_R4.RegionFile;
import net.minecraft.server.v1_7_R4.RegionFileCache;
import net.minecraft.server.v1_7_R4.ServerNBTManager;
import net.minecraft.server.v1_7_R4.TileEntity;
import net.minecraft.server.v1_7_R4.WorldManager;
@ -120,6 +126,74 @@ public class BukkitQueue17 extends BukkitQueue_0<net.minecraft.server.v1_7_R4.Ch
nmsChunk.mustSave = true;
}
@Override
public boolean setMCA(Runnable whileLocked, RegionWrapper allowed, boolean unload) {
try {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
synchronized (RegionFileCache.class) {
ArrayDeque<Chunk> chunks = new ArrayDeque<>();
World world = getWorld();
world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.chunkProviderServer;
if (unload) { // Unload chunks
boolean autoSave = world.isAutoSave();
world.setAutoSave(true);
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5;
int tcx = 31 + (allowed.maxX >> 9) << 5;
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<Chunk> iter = provider.a().iterator();
while (iter.hasNext()) {
Chunk chunk = iter.next();
int cx = chunk.locX;
int cz = chunk.locZ;
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
provider.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
}
}
for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) provider.unloadChunks();
world.setAutoSave(autoSave);
}
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk);
sendChunk(chunk, 0);
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public boolean regenerateChunk(World world, int x, int z, BaseBiome biome, Long seed) {
if (biome != null) {

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
@ -15,9 +16,11 @@ import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.biome.BaseBiome;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -25,6 +28,8 @@ import java.util.UUID;
import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.Chunk;
import net.minecraft.server.v1_8_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_8_R3.ChunkProviderServer;
import net.minecraft.server.v1_8_R3.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
@ -40,6 +45,8 @@ import net.minecraft.server.v1_8_R3.NibbleArray;
import net.minecraft.server.v1_8_R3.Packet;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerChunkMap;
import net.minecraft.server.v1_8_R3.RegionFile;
import net.minecraft.server.v1_8_R3.RegionFileCache;
import net.minecraft.server.v1_8_R3.ServerNBTManager;
import net.minecraft.server.v1_8_R3.TileEntity;
import net.minecraft.server.v1_8_R3.WorldData;
@ -124,6 +131,74 @@ public class BukkitQueue18R3 extends BukkitQueue_0<net.minecraft.server.v1_8_R3.
nmsChunk.mustSave = true;
}
@Override
public boolean setMCA(Runnable whileLocked, RegionWrapper allowed, boolean unload) {
try {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
synchronized (RegionFileCache.class) {
ArrayDeque<Chunk> chunks = new ArrayDeque<>();
World world = getWorld();
world.setKeepSpawnInMemory(false);
ChunkProviderServer provider = nmsWorld.chunkProviderServer;
if (unload) { // Unload chunks
boolean autoSave = world.isAutoSave();
world.setAutoSave(true);
int bcx = (allowed.minX >> 9) << 5;
int bcz = (allowed.minZ >> 9) << 5;
int tcx = 31 + (allowed.maxX >> 9) << 5;
int tcz = 31 + (allowed.maxZ >> 9) << 5;
Iterator<Chunk> iter = provider.a().iterator();
while (iter.hasNext()) {
Chunk chunk = iter.next();
int cx = chunk.locX;
int cz = chunk.locZ;
if (cx >= bcx && cx <= tcx && cz >= bcz && cz <= tcz) {
provider.unloadQueue.add(ChunkCoordIntPair.a(chunk.locX, chunk.locZ));
}
}
for (int i = 0; i < 50 && !provider.getName().endsWith(" 0"); i++) provider.unloadChunks();
world.setAutoSave(autoSave);
}
provider.c();
if (unload) { // Unload regions
Map<File, RegionFile> map = RegionFileCache.a;
Iterator<Map.Entry<File, RegionFile>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<File, RegionFile> entry = iter.next();
RegionFile regionFile = entry.getValue();
regionFile.c();
iter.remove();
}
}
whileLocked.run();
// Load the chunks again
if (unload) {
for (Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk);
sendChunk(chunk, 0);
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
return true;
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public boolean regenerateChunk(World world, int x, int z, BaseBiome biome, Long seed) {
if (biome != null) {

View File

@ -37,6 +37,7 @@ 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.ChunkCoordIntPair;
import net.minecraft.server.v1_9_R2.ChunkProviderServer;
import net.minecraft.server.v1_9_R2.ChunkSection;
import net.minecraft.server.v1_9_R2.DataBits;
@ -318,6 +319,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<net.minecraft.server.v1_9_
for (net.minecraft.server.v1_9_R2.Chunk chunk : chunks) {
chunk = provider.loadChunk(chunk.locX, chunk.locZ);
if (chunk != null) {
provider.chunks.put(ChunkCoordIntPair.a(chunk.locX, chunk.locZ), chunk);
sendChunk(chunk, 0);
}
}

View File

@ -164,4 +164,9 @@ public class AsyncChunk implements Chunk {
public boolean unload() {
return unload(true);
}
@Override
public boolean isSlimeChunk() {
return false;
}
}

View File

@ -28,6 +28,7 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.WorldBorder;
@ -46,8 +47,10 @@ import org.bukkit.entity.Player;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Consumer;
import org.bukkit.util.Vector;
/**
@ -237,6 +240,56 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
parent.spawnParticle(particle, location, i, v, v1, v2, v3, t);
}
@Override
public int getEntityCount() {
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
@Override
public void run(Integer value) {
this.value = parent.getEntityCount();
}
});
}
@Override
public int getTileEntityCount() {
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
@Override
public void run(Integer value) {
this.value = parent.getTileEntityCount();
}
});
}
@Override
public int getTickableTileEntityCount() {
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
@Override
public void run(Integer value) {
this.value = parent.getTickableTileEntityCount();
}
});
}
@Override
public int getChunkCount() {
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
@Override
public void run(Integer value) {
this.value = parent.getChunkCount();
}
});
}
@Override
public int getPlayerCount() {
return TaskManager.IMP.sync(new RunnableVal<Integer>() {
@Override
public void run(Integer value) {
this.value = parent.getPlayerCount();
}
});
}
@Override
public Block getBlockAt(final int x, final int y, final int z) {
return new AsyncBlock(this, queue, x, y, z);
@ -300,6 +353,21 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
return getChunkAt(block.getX(), block.getZ());
}
@Override
public void getChunkAtAsync(int x, int z, ChunkLoadCallback cb) {
parent.getChunkAtAsync(x, z, cb);
}
@Override
public void getChunkAtAsync(Location location, ChunkLoadCallback cb) {
parent.getChunkAtAsync(location, cb);
}
@Override
public void getChunkAtAsync(Block block, ChunkLoadCallback cb) {
parent.getChunkAtAsync(block, cb);
}
@Override
public boolean isChunkLoaded(Chunk chunk) {
return chunk.isLoaded();
@ -746,6 +814,26 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
});
}
@Override
public <T extends Entity> T spawn(Location location, Class<T> clazz, Consumer<T> function) throws IllegalArgumentException {
return TaskManager.IMP.sync(new RunnableVal<T>() {
@Override
public void run(T value) {
this.value = parent.spawn(location, clazz, function);
}
});
}
@Override
public FallingBlock spawnFallingBlock(Location location, MaterialData data) throws IllegalArgumentException {
return TaskManager.IMP.sync(new RunnableVal<FallingBlock>() {
@Override
public void run(FallingBlock value) {
this.value = parent.spawnFallingBlock(location, data);
}
});
}
@Override
@Deprecated
public FallingBlock spawnFallingBlock(Location location, Material material, byte data) throws IllegalArgumentException {
@ -979,6 +1067,26 @@ public class AsyncWorld extends DelegateFaweQueue implements World, HasFaweQueue
});
}
@Override
public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
parent.playSound(location, sound, category, volume, pitch);
}
});
}
@Override
public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
parent.playSound(location, sound, category, volume, pitch);
}
});
}
@Override
public String[] getGameRules() {
return parent.getGameRules();

View File

@ -14,12 +14,15 @@ import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.blocks.ImmutableBlock;
import com.sk89q.worldedit.blocks.ImmutableDatalessBlock;
import com.sk89q.worldedit.blocks.ImmutableNBTBlock;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.world.biome.BaseBiome;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.awt.Color;
@ -118,6 +121,13 @@ public class FaweCache {
return getCombined(block.getId(), block.getData());
}
public static final BaseBlock getBlock(String block, boolean allAllowed, boolean allowNoData) throws InputParseException {
ParserContext context = new ParserContext();
context.setRestricted(!allAllowed);
context.setPreferringWildcard(allowNoData);
return WorldEdit.getInstance().getBlockFactory().parseFromInput(block, context);
}
public static final Color getColor(int id, int data) {
Color exact = CACHE_COLOR[getCombined(id, data)];
if (exact != null) {

View File

@ -1,31 +1,32 @@
package com.boydti.fawe.command;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAClipboard;
import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.jnbt.anvil.MCAFilter;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.jnbt.anvil.MCAQueue;
import com.boydti.fawe.jnbt.anvil.filters.CountFilter;
import com.boydti.fawe.jnbt.anvil.filters.CountIdFilter;
import com.boydti.fawe.jnbt.anvil.filters.DeleteUninhabitedFilter;
import com.boydti.fawe.jnbt.anvil.filters.MappedReplacePatternFilter;
import com.boydti.fawe.jnbt.anvil.filters.RemoveLayerFilter;
import com.boydti.fawe.jnbt.anvil.filters.ReplacePatternFilter;
import com.boydti.fawe.jnbt.anvil.filters.ReplaceSimpleFilter;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.RunnableVal4;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.object.number.MutableLong;
import com.boydti.fawe.util.ArrayUtil;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
@ -41,15 +42,12 @@ import com.sk89q.worldedit.util.command.binding.Switch;
import com.sk89q.worldedit.util.command.parametric.Optional;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
@ -69,16 +67,70 @@ public class AnvilCommands {
this.worldEdit = worldEdit;
}
/**
* Run safely on an unloaded world (no selection)
* @param player
* @param folder
* @param filter
* @param <G>
* @param <T>
* @return
*/
public static <G, T extends MCAFilter<G>> T runWithWorld(Player player, String folder, T filter) {
if (FaweAPI.getWorld(folder) != null) {
BBC.WORLD_IS_LOADED.send(player);
return null;
}
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
return queue.filterWorld(filter);
}
/**
* Run safely on an existing world within a selection
* @param player
* @param editSession
* @param selection
* @param filter
* @param <G>
* @param <T>
* @return
*/
public static <G, T extends MCAFilter<G>> T runWithSelection(Player player, EditSession editSession, Region selection, T filter) {
if (!(selection instanceof CuboidRegion)) {
BBC.NO_REGION.send(player);
return null;
}
CuboidRegion cuboid = (CuboidRegion) selection;
RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
File folder = tmp.getSaveFolder();
MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky());
player.print(BBC.getPrefix() + "Safely unloading regions...");
tmp.setMCA(new Runnable() {
@Override
public void run() {
player.print(BBC.getPrefix() + "Performing operation...");
queue.filterRegion(filter, wrappedRegion);
player.print(BBC.getPrefix() + "Safely loading regions...");
}
}, wrappedRegion, true);
return filter;
}
@Command(
aliases = {"replaceall", "rea", "repall"},
usage = "<folder> [from-block] <to-block>",
desc = "Replace all blocks in the selection with another",
flags = "d",
help = "Replace all blocks in the selection with another\n" +
"The -d flag disabled wildcard data matching\n",
flags = "df",
min = 2,
max = 4
)
@CommandPermissions("worldedit.anvil.replaceall")
public void replaceAll(Player player, EditSession editSession, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
@ -89,15 +141,9 @@ public class AnvilCommands {
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
}
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
MCAFilterCounter counter = queue.filterWorld(new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
if (matchFrom.apply(block)) matchTo.apply(block);
}
});
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal()));
ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo);
ReplaceSimpleFilter result = runWithWorld(player, folder, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
@ -108,75 +154,10 @@ public class AnvilCommands {
max = 3
)
@CommandPermissions("worldedit.anvil.deleteallold")
public void deleteAllOld(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileAgeMillis) throws WorldEditException {
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
MCAFilterCounter result = queue.filterWorld(new MCAFilterCounter() {
@Override
public MCAFile applyFile(MCAFile mca) {
File file = mca.getFile();
try {
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
long creation = attr.creationTime().toMillis();
long modified = attr.lastModifiedTime().toMillis();
if (modified - creation < fileAgeMillis && modified > creation) {
mca.setDeleted(true);
get().add(512 * 512 * 256);
return null;
}
} catch (IOException | UnsupportedOperationException ignore) {}
try {
ForkJoinPool pool = new ForkJoinPool();
mca.init();
mca.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer x, Integer z, Integer offset, Integer size) {
try {
byte[] bytes = mca.getChunkCompressedBytes(offset);
if (bytes == null) return;
Runnable task = new Runnable() {
@Override
public void run() {
try {
mca.streamChunk(offset, new RunnableVal<NBTStreamer>() {
@Override
public void run(NBTStreamer value) {
value.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() {
@Override
public void run(Integer index, Long value) {
if (value <= inhabitedTicks) {
MCAChunk chunk = new MCAChunk(queue, x, z);
chunk.setDeleted(true);
synchronized (mca) {
mca.setChunk(chunk);
}
get().add(16 * 16 * 256);
}
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
};
pool.submit(task);
} catch (IOException e) {
e.printStackTrace();
}
}
});
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
mca.close(pool);
pool.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
});
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
public void deleteAllOld(Player player, String folder, int inhabitedTicks, @Optional("60000") int fileAgeMillis, @Switch('f') boolean force) throws WorldEditException {
DeleteUninhabitedFilter filter = new DeleteUninhabitedFilter(fileAgeMillis, inhabitedTicks);
DeleteUninhabitedFilter result = runWithWorld(player, folder, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
@ -189,62 +170,11 @@ public class AnvilCommands {
)
@CommandPermissions("worldedit.anvil.replaceall")
public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
MCAQueue queue = new MCAQueue(folder, defaultQueue.getSaveFolder(), defaultQueue.hasSky());
MCAFilterCounter counter;
MCAFilterCounter filter;
if (useMap) {
List<String> split = StringMan.split(from, ',');
if (to instanceof RandomPattern) {
Pattern[] patterns = ((RandomPattern) to).getPatterns().toArray(new Pattern[0]);
if (patterns.length == split.size()) {
Pattern[] map = new Pattern[Character.MAX_VALUE + 1];
for (int i = 0; i < split.size(); i++) {
Pattern pattern = patterns[i];
String arg = split.get(i);
ArrayList<BaseBlock> blocks = new ArrayList<BaseBlock>();
for (String arg2 : arg.split(",")) {
BaseBlock block = worldEdit.getBlock(player, arg, true);
if (!useData && !arg2.contains(":")) {
block = new BaseBlock(block.getId(), -1);
}
blocks.add(block);
}
for (BaseBlock block : blocks) {
if (block.getData() != -1) {
int combined = FaweCache.getCombined(block);
map[combined] = pattern;
} else {
for (int data = 0; data < 16; data++) {
int combined = FaweCache.getCombined(block.getId(), data);
map[combined] = pattern;
}
}
}
}
counter = queue.filterWorld(new MCAFilterCounter() {
private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
int id = block.getId();
int data = FaweCache.hasData(id) ? block.getData() : 0;
int combined = FaweCache.getCombined(id, data);
Pattern p = map[combined];
if (p != null) {
BaseBlock newBlock = p.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
});
} else {
player.print(BBC.getPrefix() + "Mask:Pattern must be a 1:1 match");
return;
}
List<String> split = StringMan.split(from, ',');
filter = new MappedReplacePatternFilter(from, (RandomPattern) to, useData);
} else {
player.print(BBC.getPrefix() + "Must be a pattern list!");
return;
@ -254,27 +184,12 @@ public class AnvilCommands {
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
if (from.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
}
counter = queue.filterWorld(new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
if (matchFrom.apply(block)) {
BaseBlock newBlock = to.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
});
filter = new ReplacePatternFilter(matchFrom, to);
}
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(counter.getTotal()));
MCAFilterCounter result = runWithWorld(player, folder, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
@ -285,70 +200,96 @@ public class AnvilCommands {
min = 2,
max = 3
)
@CommandPermissions("worldedit.anvil.countallstone")
@CommandPermissions("worldedit.anvil.countall")
public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException {
File root = new File(folder + File.separator + "region");
MCAQueue queue = new MCAQueue(folder, root, true);
if (arg.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
final boolean[] allowedId = new boolean[FaweCache.getId(Character.MAX_VALUE)];
for (BaseBlock block : searchBlocks) {
allowedId[block.getId()] = true;
}
MCAFilterCounter filter;
if (useData) { // Optimize for both cases
final boolean[] allowed = new boolean[Character.MAX_VALUE];
for (BaseBlock block : searchBlocks) {
allowed[FaweCache.getCombined(block)] = true;
}
filter = new MCAFilterCounter() {
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids == null) {
continue;
}
byte[] datas = chunk.data[layer];
for (int i = 0; i < ids.length; i++) {
int id = ids[i] & 0xFF;
if (!allowedId[id]) {
continue;
}
int combined = (id) << 4;
if (FaweCache.hasData(id)) {
combined += chunk.getNibble(i, datas);
}
if (allowed[combined]) {
count.increment();
}
}
}
return null;
}
};
if (useData || arg.contains(":")) { // Optimize for both cases
CountFilter counter = new CountFilter();
searchBlocks.forEach(counter::addBlock);
filter = counter;
} else {
filter = new MCAFilterCounter() {
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids != null) {
for (byte i : ids) {
if (allowedId[i & 0xFF]) {
count.increment();
CountIdFilter counter = new CountIdFilter();
searchBlocks.forEach(counter::addBlock);
filter = counter;
}
MCAFilterCounter result = runWithWorld(player, folder, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
}
@Command(
aliases = {"clear", "unset"},
desc = "Clear the chunks in a selection (delete without defrag)"
)
@CommandPermissions("worldedit.anvil.replaceall")
public void unset(Player player, EditSession editSession, @Selection Region selection) throws WorldEditException {
Vector bot = selection.getMinimumPoint();
Vector top = selection.getMaximumPoint();
RegionWrapper region = new RegionWrapper(bot, top);
MCAFilterCounter filter = new MCAFilterCounter() {
@Override
public MCAFile applyFile(MCAFile file) {
int X = file.getX();
int Z = file.getZ();
int bcx = X << 5;
int bcz = Z << 5;
int bx = X << 9;
int bz = Z << 9;
if (region.isIn(bx, bz) && region.isIn(bx + 511, bz + 511)) {
file.setDeleted(true);
get().add(512 * 512 * 256);
} else if (region.isInMCA(X, Z)){
file.init();
final byte[] empty = new byte[4];
RandomAccessFile raf = file.getRandomAccessFile();
file.forEachChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer cx, Integer cz, Integer offset, Integer size) {
if (region.isInChunk(bcx + cx, bcz + cz)) {
int index = ((cx & 31) << 2) + ((cz & 31) << 7);
try {
raf.seek(index);
raf.write(empty);
get().add(16 * 16 * 256);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
});
file.clear();
}
};
return null;
}
};
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
@Command(
aliases = {"count"},
usage = "<ids>",
desc = "Count blocks in a selection",
flags = "d",
min = 1,
max = 2
)
@CommandPermissions("worldedit.anvil.count")
public void count(Player player, EditSession editSession, @Selection Region selection, String arg, @Switch('d') boolean useData) throws WorldEditException {
Set<BaseBlock> searchBlocks = worldEdit.getBlocks(player, arg, true);
MCAFilterCounter filter;
if (useData || arg.contains(":")) { // Optimize for both cases
CountFilter counter = new CountFilter();
searchBlocks.forEach(counter::addBlock);
filter = counter;
} else {
CountIdFilter counter = new CountIdFilter();
searchBlocks.forEach(counter::addBlock);
filter = counter;
}
queue.filterWorld(filter);
player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(filter.getTotal()));
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal()));
}
@Command(
@ -426,31 +367,8 @@ public class AnvilCommands {
}
}
private <G, T extends MCAFilter<G>> T runWithSelection(Player player, EditSession editSession, Region selection, T filter) {
if (!(selection instanceof CuboidRegion)) {
BBC.NO_REGION.send(player);
return null;
}
CuboidRegion cuboid = (CuboidRegion) selection;
RegionWrapper wrappedRegion = new RegionWrapper(cuboid.getMinimumPoint(), cuboid.getMaximumPoint());
String worldName = Fawe.imp().getWorldName(editSession.getWorld());
FaweQueue tmp = SetQueue.IMP.getNewQueue(worldName, true, false);
File folder = tmp.getSaveFolder();
MCAQueue queue = new MCAQueue(worldName, folder, tmp.hasSky());
player.print(BBC.getPrefix() + "Safely unloading regions...");
tmp.setMCA(new Runnable() {
@Override
public void run() {
player.print(BBC.getPrefix() + "Performing operation...");
queue.filterRegion(filter, wrappedRegion);
player.print(BBC.getPrefix() + "Safely loading regions...");
}
}, wrappedRegion, true);
return filter;
}
@Command(
aliases = {"replace"},
aliases = {"replace", "r"},
usage = "[from-block] <to-block>",
desc = "Replace all blocks in the selection with another"
)
@ -460,23 +378,45 @@ public class AnvilCommands {
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
if (from.contains(":")) {
useData = true; //override d flag, if they specified data they want it
}
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData);
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
}
final FaweBlockMatcher matchTo = FaweBlockMatcher.setBlocks(worldEdit.getBlocks(player, to, true));
MCAFilterCounter filter = runWithSelection(player, editSession, selection, new MCAFilterCounter() {
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong count) {
if (matchFrom.apply(block)) {
matchTo.apply(block);
count.increment();
}
ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo);
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
}
@Command(
aliases = {"replacepattern", "preplace", "rp"},
usage = "[from-mask] <to-pattern>",
desc = "Replace all blocks in the selection with a pattern"
)
@CommandPermissions("worldedit.anvil.replace")
// Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap
public void replacePattern(Player player, EditSession editSession, @Selection Region selection, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException {
MCAFilterCounter filter;
if (useMap) {
if (to instanceof RandomPattern) {
List<String> split = StringMan.split(from, ',');
filter = new MappedReplacePatternFilter(from, (RandomPattern) to, useData);
} else {
player.print(BBC.getPrefix() + "Must be a pattern list!");
return;
}
});
if (filter != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(filter.getTotal()));
} else {
final FaweBlockMatcher matchFrom;
if (from == null) {
matchFrom = FaweBlockMatcher.NOT_AIR;
} else {
matchFrom = FaweBlockMatcher.fromBlocks(worldEdit.getBlocks(player, from, true), useData || from.contains(":"));
}
filter = new ReplacePatternFilter(matchFrom, to);
}
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
}
@ -492,39 +432,10 @@ public class AnvilCommands {
Vector max = selection.getMaximumPoint();
int minY = min.getBlockY();
int maxY = max.getBlockY();
final int startLayer = minY >> 4;
final int endLayer = maxY >> 4;
MCAFilterCounter filter = runWithSelection(player, editSession, selection, new MCAFilterCounter() {
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong cache) {
for (int layer = startLayer; layer <= endLayer; layer++) {
byte[] ids = chunk.ids[layer];
if (ids == null) {
return null;
}
int startY = Math.max(minY, layer << 4) & 15;
int endY = Math.min(maxY, 15 + (layer << 4)) & 15;
for (int y = startY; y <= endY; y++) {
int indexStart = y << 8;
int indexEnd = indexStart + 255;
for (int index = indexStart; index <= indexEnd; index++) {
if (ids[index] != id) {
return null;
}
}
}
for (int y = startY; y <= endY; y++) {
int indexStart = y << 8;
int indexEnd = indexStart + 255;
ArrayUtil.fill(ids, indexStart, indexEnd + 1, (byte) 0);
}
chunk.setModified();
}
return null;
}
});
if (filter != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(filter.getTotal()));
RemoveLayerFilter filter = new RemoveLayerFilter(minY, maxY, id);
MCAFilterCounter result = runWithSelection(player, editSession, selection, filter);
if (result != null) {
player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal()));
}
}
@ -601,6 +512,5 @@ public class AnvilCommands {
}, copyRegion, false);
}
}, pasteRegion, true);
player.print("Done!");
}
}

View File

@ -106,6 +106,8 @@ public enum BBC {
SELECTION_SHIFT("Region shifted", "WorldEdit.Selection"),
SELECTION_CLEARED("Selection cleared", "WorldEdit.Selection"),
WORLD_IS_LOADED("The world shouldn't be in use when executing. Unload the world, or use use -f to override (save first)", "WorldEdit.Anvil"),
BRUSH_RESET("Reset your brush.", "WorldEdit.Brush"),
BRUSH_NONE("You aren't holding a brush!", "WorldEdit.Brush"),
BRUSH_SCROLL_ACTION_SET("Set scroll action to %s0", "WorldEdit.Brush"),

View File

@ -72,6 +72,7 @@ public class Settings extends Config {
"Put any minecraft or mod jars for FAWE to be aware of block textures",
})
public String TEXTURES = "textures";
public String HEIGHTMAP = "heightmap";
public String HISTORY = "history";
public String CLIPBOARD = "clipboard";
@Comment("Each player has their own sub directory for schematics")
@ -281,15 +282,22 @@ public class Settings extends Config {
}
@Comment({
"Experimental options, use at your own risk",
" - Apparently that wasn't enough, need an all caps warning?",
" - DO NOT USE IF YOU ARE CLUELESS!"
"Experimental options, use at your own risk"
})
public static class EXPERIMENTAL {
@Comment({
"Directly modify the region files.",
"[UNSAFE] Directly modify the region files. (OBSOLETE - USE ANVIL COMMANDS)",
" - IMPROPER USE CAN CAUSE WORLD CORRUPTION!",
})
public boolean ANVIL_QUEUE_MODE = false;
@Comment({
"[SAFE] Dynamically increase the number of chunks rendered",
" - Requires Paper: ci.destroystokyo.com/job/PaperSpigot/",
" - Set your server view distance to 1 (spigot.yml, server.properties)",
" - Based on tps and player movement",
" - Please provide feedback",
})
public boolean DYNAMIC_CHUNK_RENDERING = false;
}
public static class WEB {

View File

@ -16,6 +16,10 @@ public class NBTStreamer {
readers = new HashMap<>();
}
/**
* Reads the entire stream and runs the applicable readers
* @throws IOException
*/
public void readFully() throws IOException {
is.readNamedTagLazy(new RunnableVal2<String, RunnableVal2>() {
@Override
@ -26,6 +30,12 @@ public class NBTStreamer {
is.close();
}
/**
* Reads the stream until all readers have been used<br>
* - Use readFully if you expect a reader to appear more than once
* - Can exit early without having reading the entire file
* @throws IOException
*/
public void readQuick() throws IOException {
try {
is.readNamedTagLazy(new RunnableVal2<String, RunnableVal2>() {

View File

@ -83,6 +83,8 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
try {
if (randomVariation) {
return new RandomTextureUtil(textureUtil);
} else if (textureUtil instanceof CachedTextureUtil) {
return textureUtil;
} else {
return new CachedTextureUtil(textureUtil);
}
@ -807,8 +809,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
@Override
public MCAChunk write(MCAChunk chunk, int csx, int cex, int csz, int cez) {
try {
int cx = chunk.getX();
int cz = chunk.getZ();
int[] indexes = indexStore.get();
for (int i = 0; i < chunk.ids.length; i++) {
byte[] idsArray = chunk.ids[i];
@ -848,6 +848,27 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
chunk.blockLight[layer] = new byte[2048];
}
}
if (waterHeight != 0) {
maxY = Math.max(maxY, waterHeight);
int maxWaterLayer = ((waterHeight + 15) >> 4);
for (int layer = 0; layer < maxWaterLayer; layer++) {
boolean fillAll = (layer << 4) + 15 <= waterHeight;
byte[] ids = chunk.ids[layer];
if (ids == null) {
chunk.ids[layer] = ids = new byte[4096];
chunk.data[layer] = new byte[2048];
chunk.skyLight[layer] = new byte[2048];
chunk.blockLight[layer] = new byte[2048];
Arrays.fill(chunk.skyLight[layer], (byte) 255);
}
if (fillAll) {
Arrays.fill(ids, waterId);
} else {
int maxIndex = maxWaterLayer << 8;
Arrays.fill(ids, 0, maxIndex, waterId);
}
}
}
if (modifiedMain) { // If the main block is modified, we can't short circuit this
for (int layer = 0; layer < fillLayers; layer++) {
byte[] layerIds = chunk.ids[layer];
@ -876,27 +897,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
Arrays.fill(chunk.ids[layer], (byte) 1);
}
}
if (waterHeight != 0) {
maxY = Math.max(maxY, waterHeight);
int maxWaterLayer = ((waterHeight + 15) >> 4);
for (int layer = 0; layer < maxWaterLayer; layer++) {
boolean fillAll = (layer << 4) + 15 <= waterHeight;
byte[] ids = chunk.ids[layer];
if (ids == null) {
chunk.ids[layer] = ids = new byte[4096];
chunk.data[layer] = new byte[2048];
chunk.skyLight[layer] = new byte[2048];
chunk.blockLight[layer] = new byte[2048];
Arrays.fill(chunk.skyLight[layer], (byte) 255);
}
if (fillAll) {
Arrays.fill(ids, waterId);
} else {
int maxIndex = maxWaterLayer << 8;
Arrays.fill(ids, 0, maxIndex, waterId);
}
}
}
for (int layer = fillLayers; layer <= maxLayer; layer++) {
Arrays.fill(chunk.skyLight[layer], (byte) 255);
byte[] layerIds = chunk.ids[layer];
@ -973,7 +973,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements Extent {
}
}
}
int chunkPair = MathMan.pair((short) cx, (short) cz);
int chunkPair = MathMan.pair((short) chunk.getX(), (short) chunk.getZ());
char[][][] localBlocks = blocks.get(chunkPair);
if (localBlocks != null) {
for (int layer = 0; layer < 16; layer++) {

View File

@ -137,6 +137,10 @@ public class MCAFile {
return Z;
}
public RandomAccessFile getRandomAccessFile() {
return raf;
}
public File getFile() {
return file;
}
@ -181,6 +185,11 @@ public class MCAFile {
return chunk;
}
/**
* CX, CZ, OFFSET, SIZE
* @param onEach
* @throws IOException
*/
public void forEachSortedChunk(RunnableVal4<Integer, Integer, Integer, Integer> onEach) throws IOException {
char[] offsets = new char[(int) (raf.length() / 4096) - 2];
Arrays.fill(offsets, Character.MAX_VALUE);

View File

@ -2,7 +2,6 @@ package com.boydti.fawe.jnbt.anvil;
import com.boydti.fawe.object.io.BufferedRandomAccessFile;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ForkJoinPool;
@ -14,6 +13,8 @@ public abstract class MCAWriter {
private final int length;
private final int width;
private final int area;
private int OX, OZ;
public MCAWriter(int width, int length, File regionFolder) {
if (!regionFolder.exists()) {
@ -37,6 +38,26 @@ public abstract class MCAWriter {
return length;
}
/**
* Set the MCA file offset (each mca file is 512 blocks)
* - A negative value will shift the map negative
* - This only applies to generation, not block get/set
* @param mcaOX
* @param mcaOZ
*/
public void setMCAOffset(int mcaOX, int mcaOZ) {
OX = mcaOX << 9;
OZ = mcaOZ << 9;
}
public int getOffsetX() {
return OX;
}
public int getOffsetZ() {
return OZ;
}
public final int getArea() {
return area;
}
@ -79,11 +100,16 @@ public abstract class MCAWriter {
}
};
byte[] fileBuf = new byte[1 << 16];
for (int mcaZ = 0; mcaZ <= (length >> 9); mcaZ++) {
for (int mcaX = 0; mcaX <= (width >> 9); mcaX++) {
int mcaXMin = 0;
int mcaZMin = 0;
int mcaXMax = mcaXMin + width >> 9;
int mcaZMax = mcaZMin + length >> 9;
for (int mcaZ = mcaXMin; mcaZ <= mcaZMax; mcaZ++) {
for (int mcaX = mcaXMin; mcaX <= mcaXMax; mcaX++) {
final int fmcaX = mcaX;
final int fmcaZ = mcaZ;
File file = new File(folder, "r." + mcaX + "." + mcaZ + ".mca");
File file = new File(folder, "r." + (mcaX + (getOffsetX() >> 9)) + "." + (mcaZ + (getOffsetZ() >> 9)) + ".mca");
if (!file.exists()) {
file.createNewFile();
}
@ -96,7 +122,6 @@ public abstract class MCAWriter {
int ecx = Math.min(scx + 31, tcx);
int scz = bz >> 4;
int ecz = Math.min(scz + 31, tcz);
short pair = MathMan.pairByte(mcaX, mcaZ);
for (int cz = scz; cz <= ecz; cz++) {
final int csz = cz << 4;
final int cez = Math.min(csz + 15, length - 1);
@ -114,6 +139,9 @@ public abstract class MCAWriter {
chunk.setLoc(null, fcx, fcz);
chunk = write(chunk, csx, cex, csz, cez);
if (chunk != null) {
// Generation offset
chunk.setLoc(null, fcx + (getOffsetX() >> 4), fcz + (getOffsetZ() >> 4));
// Compress
byte[] bytes = chunk.toBytes(byteStore1.get());
byte[] compressedBytes = MainUtil.compress(bytes, byteStore2.get(), deflateStore.get());
int blocks = (compressed.length + 4095) >> 12;

View File

@ -0,0 +1,50 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.number.MutableLong;
import com.sk89q.worldedit.blocks.BaseBlock;
public class CountFilter extends MCAFilterCounter {
private final boolean[] allowedId = new boolean[FaweCache.getId(Character.MAX_VALUE)];
private final boolean[] allowed = new boolean[Character.MAX_VALUE];
public CountFilter() {}
public CountFilter addBlock(BaseBlock block) {
addBlock(block.getId(), block.getData());
return this;
}
public CountFilter addBlock(int id, int data) {
allowedId[id] = true;
allowed[FaweCache.getCombined(id, data)] = true;
return this;
}
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids == null) {
continue;
}
byte[] datas = chunk.data[layer];
for (int i = 0; i < ids.length; i++) {
int id = ids[i] & 0xFF;
if (!allowedId[id]) {
continue;
}
int combined = (id) << 4;
if (FaweCache.hasData(id)) {
combined += chunk.getNibble(i, datas);
}
if (allowed[combined]) {
count.increment();
}
}
}
return null;
}
}

View File

@ -0,0 +1,38 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.number.MutableLong;
import com.sk89q.worldedit.blocks.BaseBlock;
public class CountIdFilter extends MCAFilterCounter {
private final boolean[] allowedId = new boolean[FaweCache.getId(Character.MAX_VALUE)];
public CountIdFilter() {}
public CountIdFilter addBlock(int id) {
allowedId[id] = true;
return this;
}
public CountIdFilter addBlock(BaseBlock block) {
allowedId[block.getId()] = true;
return this;
}
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong count) {
for (int layer = 0; layer < chunk.ids.length; layer++) {
byte[] ids = chunk.ids[layer];
if (ids != null) {
for (byte i : ids) {
if (allowedId[i & 0xFF]) {
count.increment();
}
}
}
}
return null;
}
}

View File

@ -0,0 +1,92 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.jnbt.NBTStreamer;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFile;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.RunnableVal4;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
/**
* Deletes unvisited MCA files and Chunks<br>
* - This a global filter and cannot be used a selection<br>
*/
public class DeleteUninhabitedFilter extends MCAFilterCounter {
private final long inhabitedTicks;
private final long fileAgeMillis;
public DeleteUninhabitedFilter(long fileAgeMillis, long inhabitedTicks) {
this.fileAgeMillis = fileAgeMillis;
this.inhabitedTicks = inhabitedTicks;
}
@Override
public MCAFile applyFile(MCAFile mca) {
File file = mca.getFile();
try {
BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
long creation = attr.creationTime().toMillis();
long modified = attr.lastModifiedTime().toMillis();
if (modified - creation < fileAgeMillis && modified > creation) {
mca.setDeleted(true);
get().add(512 * 512 * 256);
return null;
}
} catch (IOException | UnsupportedOperationException ignore) {}
try {
ForkJoinPool pool = new ForkJoinPool();
mca.init();
mca.forEachSortedChunk(new RunnableVal4<Integer, Integer, Integer, Integer>() {
@Override
public void run(Integer x, Integer z, Integer offset, Integer size) {
try {
byte[] bytes = mca.getChunkCompressedBytes(offset);
if (bytes == null) return;
Runnable task = new Runnable() {
@Override
public void run() {
try {
mca.streamChunk(offset, new RunnableVal<NBTStreamer>() {
@Override
public void run(NBTStreamer value) {
value.addReader(".Level.InhabitedTime", new RunnableVal2<Integer, Long>() {
@Override
public void run(Integer index, Long value) {
if (value <= inhabitedTicks) {
MCAChunk chunk = new MCAChunk(null, x, z);
chunk.setDeleted(true);
synchronized (mca) {
mca.setChunk(chunk);
}
get().add(16 * 16 * 256);
}
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
};
pool.submit(task);
} catch (IOException e) {
e.printStackTrace();
}
}
});
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
mca.close(pool);
pool.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,71 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.number.MutableLong;
import com.boydti.fawe.util.StringMan;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.function.pattern.RandomPattern;
import java.util.ArrayList;
import java.util.List;
public class MappedReplacePatternFilter extends MCAFilterCounter {
private Pattern[] map = new Pattern[Character.MAX_VALUE + 1];
public MappedReplacePatternFilter() {}
public MappedReplacePatternFilter(String from, RandomPattern to, boolean useData) throws InputParseException {
List<String> split = StringMan.split(from, ',');
Pattern[] patterns = ((RandomPattern) to).getPatterns().toArray(new Pattern[0]);
if (patterns.length == split.size()) {
for (int i = 0; i < split.size(); i++) {
Pattern pattern = patterns[i];
String arg = split.get(i);
ArrayList<BaseBlock> blocks = new ArrayList<BaseBlock>();
for (String arg2 : arg.split(",")) {
BaseBlock block = FaweCache.getBlock(arg, true, !useData);
blocks.add(block);
}
for (BaseBlock block : blocks) {
if (block.getData() != -1) {
int combined = FaweCache.getCombined(block);
map[combined] = pattern;
} else {
for (int data = 0; data < 16; data++) {
int combined = FaweCache.getCombined(block.getId(), data);
map[combined] = pattern;
}
}
}
}
} else {
throw new InputParseException("Mask:Pattern must be a 1:1 match");
}
}
public void addReplace(BaseBlock block, Pattern pattern) {
map[block.getCombined()] = pattern;
}
private final MutableBlockVector mutable = new MutableBlockVector(0, 0, 0);
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
int id = block.getId();
int data = FaweCache.hasData(id) ? block.getData() : 0;
int combined = FaweCache.getCombined(id, data);
Pattern p = map[combined];
if (p != null) {
BaseBlock newBlock = p.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
}

View File

@ -0,0 +1,50 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.jnbt.anvil.MCAChunk;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.number.MutableLong;
import com.boydti.fawe.util.ArrayUtil;
public class RemoveLayerFilter extends MCAFilterCounter {
private final int startLayer;
private final int endLayer;
private final int minY, maxY;
private final int id;
public RemoveLayerFilter(int minY, int maxY, int id) {
this.minY = minY;
this.maxY = maxY;
this.startLayer = minY >> 4;
this.endLayer = maxY >> 4;
this.id = id;
}
@Override
public MCAChunk applyChunk(MCAChunk chunk, MutableLong cache) {
for (int layer = startLayer; layer <= endLayer; layer++) {
byte[] ids = chunk.ids[layer];
if (ids == null) {
return null;
}
int startY = Math.max(minY, layer << 4) & 15;
int endY = Math.min(maxY, 15 + (layer << 4)) & 15;
for (int y = startY; y <= endY; y++) {
int indexStart = y << 8;
int indexEnd = indexStart + 255;
for (int index = indexStart; index <= indexEnd; index++) {
if (ids[index] != id) {
return null;
}
}
}
for (int y = startY; y <= endY; y++) {
int indexStart = y << 8;
int indexEnd = indexStart + 255;
ArrayUtil.fill(ids, indexStart, indexEnd + 1, (byte) 0);
}
chunk.setModified();
}
return null;
}
}

View File

@ -0,0 +1,31 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.object.number.MutableLong;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.pattern.Pattern;
public class ReplacePatternFilter extends MCAFilterCounter {
private final FaweBlockMatcher matchFrom;
private final Pattern to;
public ReplacePatternFilter(FaweBlockMatcher from, Pattern to) {
this.matchFrom = from;
this.to = to;
}
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong ignore) {
if (matchFrom.apply(block)) {
BaseBlock newBlock = to.apply(x, y, z);
int currentId = block.getId();
if (FaweCache.hasNBT(currentId)) {
block.setNbtData(null);
}
block.setId(newBlock.getId());
block.setData(newBlock.getData());
}
}
}

View File

@ -0,0 +1,23 @@
package com.boydti.fawe.jnbt.anvil.filters;
import com.boydti.fawe.jnbt.anvil.MCAFilterCounter;
import com.boydti.fawe.object.mask.FaweBlockMatcher;
import com.boydti.fawe.object.number.MutableLong;
import com.sk89q.worldedit.blocks.BaseBlock;
public class ReplaceSimpleFilter extends MCAFilterCounter {
private final FaweBlockMatcher to;
private final FaweBlockMatcher from;
public ReplaceSimpleFilter(FaweBlockMatcher from, FaweBlockMatcher to) {
this.from = from;
this.to = to;
}
@Override
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong count) {
if (from.apply(block)) {
to.apply(block);
count.increment();
}
}
}

View File

@ -14,7 +14,6 @@ import java.util.Set;
* - This will use 8 bytes for every 64 Vector2Ds (about 800x less than a HashSet)
*/
public class LocalBlockVector2DSet implements Set<Vector2D> {
private boolean chunksValid = true;
private final SparseBitSet set;
private final MutableBlockVector2D mutable = new MutableBlockVector2D();
@ -115,7 +114,6 @@ public class LocalBlockVector2DSet implements Set<Vector2D> {
int previous = -1;
@Override
public void remove() {
chunksValid = false;
set.clear(previous);
}
@Override
@ -193,7 +191,6 @@ public class LocalBlockVector2DSet implements Set<Vector2D> {
boolean value = set.get(index);
if (value) {
set.clear(index);
chunksValid = false;
}
return value;
}
@ -239,7 +236,6 @@ public class LocalBlockVector2DSet implements Set<Vector2D> {
if (!c.contains(mutable)) {
result = true;
set.clear(index);
chunksValid = false;
}
}
return result;
@ -273,6 +269,5 @@ public class LocalBlockVector2DSet implements Set<Vector2D> {
@Override
public void clear() {
set.clear();
chunksValid = true;
}
}

View File

@ -178,6 +178,23 @@ public class PrimitiveList<T> extends AbstractList<T> {
}
}
public void set(int index, long value) {
switch (type) {
default:
setFast(index, value);
return;
case Integer:
((int[]) arr)[index] = (int) value;
return;
case Long:
((long[]) arr)[index] = value;
return;
case Double:
((double[]) arr)[index] = (double) value;
return;
}
}
public void set(int index, double value) {
switch (type) {
default:
@ -271,6 +288,12 @@ public class PrimitiveList<T> extends AbstractList<T> {
return true;
}
public boolean add(long element) {
ensureAddCapacity();
set(length++, element);
return true;
}
public boolean add(double element) {
ensureAddCapacity();
set(length++, element);

View File

@ -8,6 +8,7 @@ import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.CleanTextureUtil;
import com.boydti.fawe.util.FilteredTextureUtil;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.intellectualcrafters.plot.PS;
@ -25,7 +26,6 @@ import com.intellectualcrafters.plot.object.RunnableVal3;
import com.intellectualcrafters.plot.object.worlds.PlotAreaManager;
import com.intellectualcrafters.plot.object.worlds.SinglePlotArea;
import com.intellectualcrafters.plot.object.worlds.SinglePlotAreaManager;
import com.intellectualcrafters.plot.util.MainUtil;
import com.plotsquared.general.commands.Command;
import com.plotsquared.general.commands.CommandDeclaration;
import com.sk89q.worldedit.Vector;
@ -85,7 +85,8 @@ public class CreateFromImage extends Command {
if (generator == null) {
final Vector2D dimensions;
final BufferedImage image;
if (argList.get(0).toLowerCase().startsWith("http")) {
String arg0 = argList.get(0).toLowerCase();
if (arg0.startsWith("http") || arg0.startsWith("file://")) {
try {
player.sendMessage(BBC.getPrefix() + "Loading image... (1)");
image = getImage(argList.get(0), fp);
@ -113,7 +114,7 @@ public class CreateFromImage extends Command {
int currentPlots = Settings.Limit.GLOBAL ? player.getPlotCount() : player.getPlotCount(area.worldname);
int diff = player.getAllowedPlots() - currentPlots;
if (diff < 1) {
MainUtil.sendMessage(player, C.CANT_CLAIM_MORE_PLOTS_NUM, -diff + "");
C.CANT_CLAIM_MORE_PLOTS_NUM.send(player, -diff);
return;
}
if (area.getMeta("lastPlot") == null) {
@ -195,7 +196,8 @@ public class CreateFromImage extends Command {
}
int argOffset = 0;
BufferedImage img = null;
if (argList.get(1).startsWith("http")) {
String arg1 = argList.get(1);
if (arg1.startsWith("http") || arg1.startsWith("file://")) {
img = getImage(argList.get(1), fp);
argOffset++;
}
@ -601,7 +603,12 @@ public class CreateFromImage extends Command {
if (arg.startsWith("http")) {
URL url = new URL(arg);
fp.sendMessage(BBC.getPrefix() + "Downloading image... (3)");
return com.boydti.fawe.util.MainUtil.toRGB(ImageIO.read(url));
return MainUtil.toRGB(ImageIO.read(url));
}
if (arg.startsWith("file://")) {
arg = arg.substring(7);
File file = MainUtil.getFile(Fawe.imp().getDirectory(), com.boydti.fawe.config.Settings.IMP.PATHS.HEIGHTMAP + File.separator + arg);
return MainUtil.toRGB(ImageIO.read(file));
}
return null;
}

View File

@ -464,6 +464,7 @@ public class TaskBuilder extends Metadatable {
private long start;
private Object asyncWaitLock = new Object();
private Object syncWaitLock = new Object();
private boolean finished;
public SplitTask() {
@ -483,6 +484,7 @@ public class TaskBuilder extends Metadatable {
public void run() {
try {
synchronized (asyncWaitLock) {
asyncWaitLock.notifyAll();
asyncWaitLock.wait(Long.MAX_VALUE);
}
} catch (InterruptedException e) {
@ -495,24 +497,31 @@ public class TaskBuilder extends Metadatable {
}
}
});
thread.start();
try {
synchronized (asyncWaitLock) {
thread.start();
asyncWaitLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
while (thread.isAlive()) {
TaskManager.IMP.syncWhenFree(new RunnableVal() {
@Override
public void run(Object ignore) {
queue.startSet(true);
start = System.currentTimeMillis();
synchronized (asyncWaitLock) {
asyncWaitLock.notifyAll();
}
synchronized (syncWaitLock) {
try {
if (!finished) {
try {
syncWaitLock.wait(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
synchronized (asyncWaitLock) {
asyncWaitLock.notifyAll();
}
synchronized (syncWaitLock) {
syncWaitLock.wait();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.endSet(true);
}
@ -529,7 +538,7 @@ public class TaskBuilder extends Metadatable {
syncWaitLock.notifyAll();
}
synchronized (asyncWaitLock) {
asyncWaitLock.wait(Long.MAX_VALUE);
asyncWaitLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();

View File

@ -680,7 +680,7 @@ public class BrushCommands extends MethodCommands {
private InputStream getHeightmapStream(String filename) {
String filenamePng = (filename.endsWith(".png") ? filename : filename + ".png");
File file = new File(Fawe.imp().getDirectory(), "heightmap" + File.separator + filenamePng);
File file = new File(Fawe.imp().getDirectory(), Settings.IMP.PATHS.HEIGHTMAP + File.separator + filenamePng);
if (!file.exists()) {
if (!filename.equals("#clipboard") && filename.length() >= 7) {
try {

View File

@ -674,7 +674,8 @@ public class SelectionCommands {
if (useData) {
for (Countable<BaseBlock> c : distributionData) {
String name = BlockType.fromID(c.getID().getId()).getName();
BlockType block = BlockType.fromID(c.getID().getId());
String name = block != null ? block.getName() : null;
String str = String.format("%-7s (%.3f%%) %s #%d:%d",
String.valueOf(c.getAmount()),
c.getAmount() / (double) size * 100,

View File

@ -379,9 +379,6 @@ public final class CommandManager {
try {
Object result = dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
} catch (Throwable t) {
while (t.getCause() != null) {
t = t.getCause();
}
// Use the exception converter to convert the exception if any of its causes
// can be converted, otherwise throw the original exception
Throwable next = t;
@ -407,26 +404,22 @@ public final class CommandManager {
BBC.COMMAND_SYNTAX.send(finalActor, e.getSimpleUsageString("/"));
}
} catch (WrappedCommandException e) {
FaweException faweException = FaweException.get(e);
Exception faweException = FaweException.get(e);
String message = e.getMessage();
if (faweException != null) {
BBC.WORLDEDIT_CANCEL_REASON.send(finalActor, faweException.getMessage());
} else {
Throwable t = e.getCause();
while (t.getCause() != null) {
t = t.getCause();
}
finalActor.printError("There was an error handling a FAWE command: [See console]");
finalActor.printRaw(t.getClass().getName() + ": " + t.getMessage());
log.log(Level.SEVERE, "An unexpected error occurred while handling a FAWE command", t);
finalActor.printRaw(e.getClass().getName() + ": " + e.getMessage());
log.log(Level.SEVERE, "An unexpected error occurred while handling a FAWE command", e);
}
} catch (Throwable e) {
e.printStackTrace();
} catch (CommandException e) {
String message = e.getMessage();
if (message != null) {
finalActor.printError(e.getMessage());
actor.printError(e.getMessage());
} else {
finalActor.printError("An unknown error has occurred! Please see console.");
log.log(Level.SEVERE, "An unknown error occurred", e);
actor.printError("An unknown FAWE error has occurred! Please see console.");
log.log(Level.SEVERE, "An unknown FAWE error occurred", e);
}
} finally {
final EditSession editSession = locals.get(EditSession.class);