mirror of
https://github.com/boy0001/FastAsyncWorldedit.git
synced 2024-12-29 12:37:53 +01:00
Various
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:
parent
0f3138e894
commit
7ae2d65607
@ -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/"}
|
||||
|
@ -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'
|
||||
|
@ -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();
|
||||
|
135
bukkit/src/main/java/com/boydti/fawe/bukkit/RenderListener.java
Normal file
135
bukkit/src/main/java/com/boydti/fawe/bukkit/RenderListener.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -164,4 +164,9 @@ public class AsyncChunk implements Chunk {
|
||||
public boolean unload() {
|
||||
return unload(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSlimeChunk() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
@ -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"),
|
||||
|
@ -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 {
|
||||
|
@ -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>() {
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user