update to 1.9.4
progress notifications
lighting fixes
optimizations

Only stable for bukkit 1.8/1.9
This commit is contained in:
Jesse Boyd 2016-05-13 03:46:38 +10:00
parent 947ff54a4c
commit b5a8eb2176
61 changed files with 2135 additions and 1060 deletions

View File

@ -3,14 +3,18 @@ package com.boydti.fawe.bukkit;
import com.boydti.fawe.Fawe; import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import java.lang.reflect.Method;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public class BukkitPlayer extends FawePlayer<Player> { public class BukkitPlayer extends FawePlayer<Player> {
private static ConsoleCommandSender console;
public BukkitPlayer(final Player parent) { public BukkitPlayer(final Player parent) {
super(parent); super(parent);
} }
@ -49,6 +53,27 @@ public class BukkitPlayer extends FawePlayer<Player> {
} }
} }
@Override
public void resetTitle() {
sendTitle("","");
}
public void sendTitle(String title, String sub) {
try {
Method methodSendTitle = Player.class.getDeclaredMethod("sendTitle", String.class, String.class);
methodSendTitle.invoke(parent, ChatColor.GOLD + title, ChatColor.GOLD + sub);
return;
} catch (Throwable ignore) {}
if (console == null) {
console = Bukkit.getConsoleSender();
Bukkit.getServer().dispatchCommand(console, "gamerule sendCommandFeedback false");
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " times 0 60 20");
}
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " subtitle [{\"text\":\"" + sub + "\",\"color\":\"gold\"}]");
Bukkit.getServer().dispatchCommand(console, "title " + getName() + " title [{\"text\":\"" + title + "\",\"color\":\"gold\"}]");
}
@Override @Override
public void sendMessage(final String message) { public void sendMessage(final String message) {
this.parent.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); this.parent.sendMessage(ChatColor.translateAlternateColorCodes('&', message));

View File

@ -195,7 +195,7 @@ public class FaweBukkit implements IFawe, Listener {
debug("====== NO NMS BLOCK PLACER FOUND ======"); debug("====== NO NMS BLOCK PLACER FOUND ======");
debug("FAWE couldn't find a fast block placer"); debug("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion()); debug("Bukkit version: " + Bukkit.getVersion());
debug("Supported NMS versions: 1.8, 1.9"); debug("Supported NMS versions: 1.8.8, 1.9.4");
debug("Fallback placer: " + BukkitQueue_All.class); debug("Fallback placer: " + BukkitQueue_All.class);
debug("======================================="); debug("=======================================");
hasNMS = false; hasNMS = false;

View File

@ -19,6 +19,7 @@ public class LoggingChangeSet extends FaweChangeSet {
private final String name; private final String name;
public LoggingChangeSet(FawePlayer<Player> player, FaweChangeSet parent, IBlocksHubApi api) { public LoggingChangeSet(FawePlayer<Player> player, FaweChangeSet parent, IBlocksHubApi api) {
super(parent.getWorld());
this.parent = parent; this.parent = parent;
this.name = player.getName(); this.name = player.getName();
this.api = api; this.api = api;

View File

@ -22,12 +22,11 @@ public class BukkitEditSessionWrapper_0 extends EditSessionWrapper {
} }
@Override @Override
public Extent getHistoryExtent(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer<?> player) { public FaweChangeSet wrapChangeSet(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer<?> player) {
if (this.hook != null) { if (this.hook != null) {
// If we are doing logging, use a custom logging ChangeSet // If we are doing logging, use a custom logging ChangeSet
set = hook.getLoggingChangeSet(session, limit, parent, set, queue, player); return hook.getLoggingChangeSet(session, limit, parent, set, queue, player);
} }
// Now return the normal history extent return set;
return super.getHistoryExtent(session, limit, parent, set, queue, player);
} }
} }

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.bukkit.v0; package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.example.NMSMappedFaweQueue; import com.boydti.fawe.example.NMSMappedFaweQueue;
@ -14,6 +15,10 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.World; import org.bukkit.World;
@ -21,18 +26,26 @@ import org.bukkit.block.Block;
public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMappedFaweQueue<World, CHUNK, CHUNKSECTIONS, SECTION> { public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMappedFaweQueue<World, CHUNK, CHUNKSECTIONS, SECTION> {
public final BukkitImplAdapter adapter; public Object adapter;
public Method methodToNative; public Method methodToNative;
public Method methodFromNative; public Method methodFromNative;
public BukkitQueue_0(final String world) { public BukkitQueue_0(final String world) {
super(world); super(world);
setupAdapter(null);
}
public void setupAdapter(BukkitImplAdapter adapter) {
try { try {
WorldEditPlugin instance = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); WorldEditPlugin instance = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
Field fieldAdapter = WorldEditPlugin.class.getDeclaredField("bukkitAdapter"); Field fieldAdapter = WorldEditPlugin.class.getDeclaredField("bukkitAdapter");
fieldAdapter.setAccessible(true); fieldAdapter.setAccessible(true);
this.adapter = (BukkitImplAdapter) fieldAdapter.get(instance); if ((this.adapter = adapter) != null) {
for (Method method : adapter.getClass().getDeclaredMethods()) { fieldAdapter.set(instance, adapter);
} else {
this.adapter = fieldAdapter.get(instance);
}
for (Method method : this.adapter.getClass().getDeclaredMethods()) {
switch (method.getName()) { switch (method.getName()) {
case "toNative": case "toNative":
methodToNative = method; methodToNative = method;
@ -45,7 +58,12 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
} }
} }
} catch (Throwable e) { } catch (Throwable e) {
throw new RuntimeException(e); e.printStackTrace();
Fawe.debug("====== NO NATIVE WORLDEDIT ADAPTER ======");
Fawe.debug("Try updating WorldEdit: ");
Fawe.debug(" - http://builds.enginehub.org/job/worldedit?branch=master");
Fawe.debug("See also: http://wiki.sk89q.com/wiki/WorldEdit/Bukkit_adapters");
Fawe.debug("=========================================");
} }
} }
@ -69,8 +87,14 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
return world.regenerateChunk(x, z); return world.regenerateChunk(x, z);
} }
@Override @Override
public boolean fixLighting(FaweChunk fc, boolean fixAll) { public CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTIONS sections, Map<?, ?> tiles, Collection<?>[] entities, Set<UUID> createdEntities, boolean all) throws Exception {
return fs;
}
@Override
public boolean fixLighting(FaweChunk fc, RelightMode mode) {
// Not implemented // Not implemented
return true; return true;
} }
@ -121,7 +145,7 @@ public abstract class BukkitQueue_0<CHUNK, CHUNKSECTIONS, SECTION> extends NMSMa
final Chunk chunk = fs.getChunk(); final Chunk chunk = fs.getChunk();
chunk.load(true); chunk.load(true);
final World world = chunk.getWorld(); final World world = chunk.getWorld();
char[][] sections = fs.getIdArrays(); char[][] sections = fs.getCombinedIdArrays();
boolean done = false; boolean done = false;
boolean more = false; boolean more = false;
// Efficiently merge sections // Efficiently merge sections

View File

@ -2,11 +2,11 @@ package com.boydti.fawe.bukkit.v1_8;
import com.boydti.fawe.bukkit.ABukkitMain; import com.boydti.fawe.bukkit.ABukkitMain;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.bukkit.v1_8.BukkitQueue18R3;
import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.EditSessionWrapper;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
public class BukkitMain_18 extends ABukkitMain { public class BukkitMain_18 extends ABukkitMain {
@Override @Override
public BukkitQueue_0 getQueue(String world) { public BukkitQueue_0 getQueue(String world) {
return new BukkitQueue18R3(world); return new BukkitQueue18R3(world);

View File

@ -21,16 +21,19 @@ import com.sk89q.worldedit.internal.Constants;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import net.minecraft.server.v1_8_R3.BlockPosition; import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.ChunkCoordIntPair; import net.minecraft.server.v1_8_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_8_R3.ChunkSection; import net.minecraft.server.v1_8_R3.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity; import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EntityTypes; import net.minecraft.server.v1_8_R3.EntityTypes;
import net.minecraft.server.v1_8_R3.NBTTagCompound; import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.NBTTagInt; import net.minecraft.server.v1_8_R3.NibbleArray;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk; import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerConnection; import net.minecraft.server.v1_8_R3.PlayerConnection;
import net.minecraft.server.v1_8_R3.TileEntity; import net.minecraft.server.v1_8_R3.TileEntity;
@ -95,6 +98,71 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
return section == null ? null : section.getIdArray(); return section == null ? null : section.getIdArray();
} }
@Override
public CharFaweChunk getPrevious(CharFaweChunk fs, ChunkSection[] sections, Map<?, ?> tilesGeneric, Collection<?>[] entitiesGeneric, Set<UUID> createdEntities, boolean all) throws Exception {
Map<BlockPosition, TileEntity> tiles = (Map<BlockPosition, TileEntity>) tilesGeneric;
Collection<Entity>[] entities = (Collection<Entity>[]) entitiesGeneric;
CharFaweChunk previous = (CharFaweChunk) getChunk(fs.getX(), fs.getZ());
char[][] idPrevious = new char[16][];
for (int layer = 0; layer < sections.length; layer++) {
if (fs.getCount(layer) != 0 || all) {
ChunkSection section = sections[layer];
if (section != null) {
idPrevious[layer] = section.getIdArray().clone();
short solid = 0;
for (int combined : idPrevious[layer]) {
if (combined > 1) {
solid++;
}
}
previous.count[layer] = solid;
previous.air[layer] = (short) (4096 - solid);
}
}
}
previous.ids = idPrevious;
if (tiles != null) {
for (Map.Entry<BlockPosition, TileEntity> entry : tiles.entrySet()) {
TileEntity tile = entry.getValue();
NBTTagCompound tag = new NBTTagCompound();
tile.b(tag); // readTileEntityIntoTag
BlockPosition pos = entry.getKey();
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag);
}
}
if (entities != null) {
for (Collection<Entity> entityList : entities) {
for (Entity ent : entityList) {
if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && !createdEntities.contains(ent.getUniqueID()))) {
continue;
}
int x = ((int) Math.round(ent.locX) & 15);
int z = ((int) Math.round(ent.locZ) & 15);
int y = (int) Math.round(ent.locY);
int i = FaweCache.CACHE_I[y][x][z];
char[] array = fs.getIdArray(i);
if (array == null) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
String id = EntityTypes.b(ent);
if (id != null) {
NBTTagCompound tag = new NBTTagCompound();
ent.e(tag); // readEntityIntoTag
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
Map<String, Tag> map = ReflectionUtils.getMap(nativeTag.getValue());
map.put("Id", new StringTag(id));
previous.setEntity(nativeTag);
}
}
}
}
}
return previous;
}
@Override @Override
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) { public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
CharFaweChunk<Chunk> fs = (CharFaweChunk<Chunk>) fc; CharFaweChunk<Chunk> fs = (CharFaweChunk<Chunk>) fc;
@ -108,54 +176,70 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities(); Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
Collection<net.minecraft.server.v1_8_R3.Entity>[] entities = nmsChunk.getEntitySlices(); Collection<net.minecraft.server.v1_8_R3.Entity>[] entities = nmsChunk.getEntitySlices();
// Run change task if applicable // Remove entities
if (changeTask != null) { for (int i = 0; i < 16; i++) {
CharFaweChunk previous = (CharFaweChunk) getChunk(fc.getX(), fc.getZ()); int count = fs.getCount(i);
char[][] idPrevious = new char[16][]; if (count == 0) {
for (int layer = 0; layer < sections.length; layer++) { continue;
if (fs.getCount(layer) != 0) { } else if (count >= 4096) {
ChunkSection section = sections[layer]; entities[i].clear();
if (section != null) { } else {
idPrevious[layer] = section.getIdArray().clone(); char[] array = fs.getIdArray(i);
Collection<Entity> ents = new ArrayList<>(entities[i]);
for (Entity entity : ents) {
if (entity instanceof EntityPlayer) {
continue;
} }
} int x = ((int) Math.round(entity.locX) & 15);
} int z = ((int) Math.round(entity.locZ) & 15);
previous.ids = idPrevious; int y = (int) Math.round(entity.locY);
for (Map.Entry<BlockPosition, TileEntity> entry : tiles.entrySet()) {
TileEntity tile = entry.getValue();
NBTTagCompound tag = new NBTTagCompound();
tile.b(tag); // ReadTileIntoTag
BlockPosition pos = entry.getKey();
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag);
}
for (Collection<Entity> entityList : entities) {
for (Entity ent : entityList) {
int x = ((int) Math.round(ent.locX) & 15);
int z = ((int) Math.round(ent.locZ) & 15);
int y = (int) Math.round(ent.locY);
int i = FaweCache.CACHE_I[y][x][z];
char[] array = fs.getIdArray(i);
if (array == null) { if (array == null) {
continue; continue;
} }
int j = FaweCache.CACHE_J[y][x][z]; int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) { if (array[j] != 0) {
String id = EntityTypes.b(ent); nmsWorld.removeEntity(entity);
if (id != null) {
NBTTagCompound tag = new NBTTagCompound();
ent.e(tag);
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
Map<String, Tag> map = ReflectionUtils.getMap(nativeTag.getValue());
map.put("Id", new StringTag(id));
previous.setEntity(nativeTag);
}
} }
} }
} }
}
// Set entities
Set<UUID> createdEntities = new HashSet<>();
Set<CompoundTag> entitiesToSpawn = fs.getEntities();
for (CompoundTag nativeTag : entitiesToSpawn) {
Map<String, Tag> entityTagMap = nativeTag.getValue();
StringTag idTag = (StringTag) entityTagMap.get("Id");
ListTag posTag = (ListTag) entityTagMap.get("Pos");
ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
if (idTag == null || posTag == null || rotTag == null) {
Fawe.debug("Unknown entity tag: " + nativeTag);
continue;
}
double x = posTag.getDouble(0);
double y = posTag.getDouble(1);
double z = posTag.getDouble(2);
float yaw = rotTag.getFloat(0);
float pitch = rotTag.getFloat(1);
String id = idTag.getValue();
Entity entity = EntityTypes.createEntityByName(id, nmsWorld);
if (entity != null) {
if (nativeTag != null) {
NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(adapter, nativeTag);
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
entity.f(tag);
}
entity.setLocation(x, y, z, yaw, pitch);
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
createdEntities.add(entity.getUniqueID());
}
}
// Run change task if applicable
if (changeTask != null) {
CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false);
changeTask.run(previous); changeTask.run(previous);
} }
// Trim tiles // Trim tiles
Set<Map.Entry<BlockPosition, TileEntity>> entryset = tiles.entrySet(); Set<Map.Entry<BlockPosition, TileEntity>> entryset = tiles.entrySet();
Iterator<Map.Entry<BlockPosition, TileEntity>> iterator = entryset.iterator(); Iterator<Map.Entry<BlockPosition, TileEntity>> iterator = entryset.iterator();
@ -175,31 +259,17 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
iterator.remove(); iterator.remove();
} }
} }
// Remove entities HashSet<UUID> entsToRemove = fs.getEntityRemoves();
for (int i = 0; i < 16; i++) { if (entsToRemove.size() > 0) {
int count = fs.getCount(i); for (int i = 0; i < entities.length; i++) {
if (count == 0) {
continue;
} else if (count >= 4096) {
entities[i].clear();
} else {
char[] array = fs.getIdArray(i);
Collection<Entity> ents = new ArrayList<>(entities[i]); Collection<Entity> ents = new ArrayList<>(entities[i]);
for (Entity entity : ents) { for (Entity entity : ents) {
int x = ((int) Math.round(entity.locX) & 15); if (entsToRemove.contains(entity.getUniqueID())) {
int z = ((int) Math.round(entity.locZ) & 15);
int y = (int) Math.round(entity.locY);
if (array == null) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
nmsWorld.removeEntity(entity); nmsWorld.removeEntity(entity);
} }
} }
} }
} }
// Set blocks // Set blocks
for (int j = 0; j < sections.length; j++) { for (int j = 0; j < sections.length; j++) {
if (fs.getCount(j) == 0) { if (fs.getCount(j) == 0) {
@ -256,53 +326,19 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
} }
// Set tiles // Set tiles
Map<BytePair, CompoundTag> tilesToSpawn = fs.getTiles(); Map<BytePair, CompoundTag> tilesToSpawn = fs.getTiles();
BlockPosition.MutableBlockPosition mutablePos = new BlockPosition.MutableBlockPosition(0, 0, 0);
int bx = fs.getX() << 4; int bx = fs.getX() << 4;
int bz = fs.getZ() << 4; int bz = fs.getZ() << 4;
for (Map.Entry<BytePair, CompoundTag> entry : tilesToSpawn.entrySet()) { for (Map.Entry<BytePair, CompoundTag> entry : tilesToSpawn.entrySet()) {
CompoundTag nativeTag = entry.getValue(); CompoundTag nativeTag = entry.getValue();
BytePair pair = entry.getKey(); BytePair pair = entry.getKey();
mutablePos.c(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos
TileEntity tileEntity = nmsWorld.getTileEntity(mutablePos); TileEntity tileEntity = nmsWorld.getTileEntity(pos);
if (tileEntity != null) { if (tileEntity != null) {
NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag);
tag.set("x", new NBTTagInt(mutablePos.getX()));
tag.set("y", new NBTTagInt(mutablePos.getY()));
tag.set("z", new NBTTagInt(mutablePos.getZ()));
tileEntity.a(tag); // ReadTagIntoTile tileEntity.a(tag); // ReadTagIntoTile
} }
} }
// Set entities
Set<CompoundTag> entitiesToSpawn = fs.getEntities();
for (CompoundTag nativeTag : entitiesToSpawn) {
Map<String, Tag> entityTagMap = nativeTag.getValue();
StringTag idTag = (StringTag) entityTagMap.get("Id");
ListTag posTag = (ListTag) entityTagMap.get("Pos");
ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
if (idTag == null || posTag == null || rotTag == null) {
Fawe.debug("Unknown entity tag: " + nativeTag);
continue;
}
double x = posTag.getDouble(0);
double y = posTag.getDouble(1);
double z = posTag.getDouble(2);
float yaw = rotTag.getFloat(0);
float pitch = rotTag.getFloat(1);
String id = idTag.getValue();
Entity entity = EntityTypes.createEntityByName(id, nmsWorld);
if (entity != null) {
if (nativeTag != null) {
NBTTagCompound tag = (NBTTagCompound)methodFromNative.invoke(adapter, nativeTag);
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
entity.f(tag);
}
entity.setLocation(x, y, z, yaw, pitch);
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
}
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -339,8 +375,8 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
FaweLocation loc = fp.getLocation(); FaweLocation loc = fp.getLocation();
int px = loc.x >> 4; int px = loc.x >> 4;
int pz = loc.z >> 4; int pz = loc.z >> 4;
int dx = Math.abs(cx - (loc.x >> 4)); int dx = Math.abs(cx - (px));
int dz = Math.abs(cz - (loc.z >> 4)); int dz = Math.abs(cz - (pz));
if ((dx > view) || (dz > view)) { if ((dx > view) || (dz > view)) {
continue; continue;
} }
@ -350,7 +386,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
} }
@Override @Override
public boolean fixLighting(FaweChunk chunk, boolean fixAll) { public boolean fixLighting(FaweChunk chunk, RelightMode mode) {
try { try {
CharFaweChunk<Chunk> fc = (CharFaweChunk<Chunk>) chunk; CharFaweChunk<Chunk> fc = (CharFaweChunk<Chunk>) chunk;
CraftChunk craftChunk = (CraftChunk) fc.getChunk(); CraftChunk craftChunk = (CraftChunk) fc.getChunk();
@ -358,11 +394,22 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
if (!craftChunk.isLoaded()) { if (!craftChunk.isLoaded()) {
return false; return false;
} }
ChunkSection[] sections = nmsChunk.getSections();
if (mode != RelightMode.MINIMAL) {
for (int i = 0; i < sections.length; i++) {
ChunkSection section = sections[i];
if (section != null) {
if (mode == RelightMode.ALL) {
section.a(new NibbleArray());
}
section.b(new NibbleArray());
}
}
}
nmsChunk.initLighting(); nmsChunk.initLighting();
if (fc.getTotalRelight() == 0 && !fixAll) { if (fc.getTotalRelight() == 0 && mode == RelightMode.MINIMAL) {
return true; return true;
} }
ChunkSection[] sections = nmsChunk.getSections();
net.minecraft.server.v1_8_R3.World nmsWorld = nmsChunk.getWorld(); net.minecraft.server.v1_8_R3.World nmsWorld = nmsChunk.getWorld();
int X = fc.getX() << 4; int X = fc.getX() << 4;
@ -374,7 +421,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
if (section == null) { if (section == null) {
continue; continue;
} }
if ((fc.getRelight(j) == 0 && !fixAll) || fc.getCount(j) == 0 || (fc.getCount(j) >= 4096 && fc.getAir(j) == 0)) { if ((fc.getRelight(j) == 0 && mode == RelightMode.MINIMAL) || (fc.getCount(j) == 0 && mode != RelightMode.ALL) || (fc.getCount(j) >= 4096 && fc.getAir(j) == 0)) {
continue; continue;
} }
char[] array = section.getIdArray(); char[] array = section.getIdArray();
@ -387,7 +434,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
short id = (short) (i >> 4); short id = (short) (i >> 4);
switch (id) { // Lighting switch (id) { // Lighting
default: default:
if (!fixAll) { if (mode == RelightMode.MINIMAL) {
continue; continue;
} }
if ((k & 1) == l) { if ((k & 1) == l) {

View File

@ -21,9 +21,9 @@ commands:
fawe: fawe:
description: (FAWE) Reload the plugin description: (FAWE) Reload the plugin
aliases: [/fawe,/fawereload] aliases: [/fawe,/fawereload]
wrg: select:
description: (FAWE) Select your current WorldEdit Region. description: (FAWE) Select your current WorldEdit Region.
aliases: [/wrg,wer,/wer,worldeditregion,/worldeditregion,/region] aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region]
frb: frb:
description: (FAWE) Rollback an edit description: (FAWE) Rollback an edit
aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb]

View File

@ -1,6 +1,6 @@
dependencies { dependencies {
compile project(':bukkit0') compile project(':bukkit0')
compile 'org.bukkit.craftbukkit:CraftBukkit:1.9.2' compile 'org.bukkit.craftbukkit.v1_9R2:craftbukkitv1_9R2:1.9.4'
} }
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'

View File

@ -5,12 +5,12 @@ import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.FaweQueue;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import net.minecraft.server.v1_9_R1.Block; import net.minecraft.server.v1_9_R2.Block;
import net.minecraft.server.v1_9_R1.DataBits; import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R1.DataPalette; import net.minecraft.server.v1_9_R2.DataPalette;
import net.minecraft.server.v1_9_R1.DataPaletteBlock; import net.minecraft.server.v1_9_R2.DataPaletteBlock;
import net.minecraft.server.v1_9_R1.DataPaletteGlobal; import net.minecraft.server.v1_9_R2.DataPaletteGlobal;
import net.minecraft.server.v1_9_R1.IBlockData; import net.minecraft.server.v1_9_R2.IBlockData;
import org.bukkit.Chunk; import org.bukkit.Chunk;
public class BukkitChunk_1_9 extends CharFaweChunk<Chunk> { public class BukkitChunk_1_9 extends CharFaweChunk<Chunk> {
@ -101,7 +101,7 @@ public class BukkitChunk_1_9 extends CharFaweChunk<Chunk> {
if (sectionPalettes != null) { if (sectionPalettes != null) {
return; return;
} }
char[][] arrays = getIdArrays(); char[][] arrays = getCombinedIdArrays();
IBlockData lastBlock = null; IBlockData lastBlock = null;
char lastChar = Character.MAX_VALUE; char lastChar = Character.MAX_VALUE;
for (int layer = 0; layer < 16; layer++) { for (int layer = 0; layer < 16; layer++) {

View File

@ -2,7 +2,6 @@ package com.boydti.fawe.bukkit.v1_9;
import com.boydti.fawe.bukkit.ABukkitMain; import com.boydti.fawe.bukkit.ABukkitMain;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.bukkit.v1_9.BukkitQueue_1_9_R1;
import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.EditSessionWrapper;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;

View File

@ -4,44 +4,50 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache; import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0; import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.example.CharFaweChunk; import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.BytePair;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.PseudoRandom; import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.LocalSession; import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.internal.Constants;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import net.minecraft.server.v1_9_R1.Block; import java.util.Set;
import net.minecraft.server.v1_9_R1.BlockPosition; import java.util.UUID;
import net.minecraft.server.v1_9_R1.Blocks; import net.minecraft.server.v1_9_R2.Block;
import net.minecraft.server.v1_9_R1.ChunkSection; import net.minecraft.server.v1_9_R2.BlockPosition;
import net.minecraft.server.v1_9_R1.DataBits; import net.minecraft.server.v1_9_R2.Blocks;
import net.minecraft.server.v1_9_R1.DataPalette; import net.minecraft.server.v1_9_R2.ChunkSection;
import net.minecraft.server.v1_9_R1.DataPaletteBlock; import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R1.IBlockData; import net.minecraft.server.v1_9_R2.DataPalette;
import net.minecraft.server.v1_9_R1.TileEntity; import net.minecraft.server.v1_9_R2.DataPaletteBlock;
import org.bukkit.Bukkit; import net.minecraft.server.v1_9_R2.Entity;
import net.minecraft.server.v1_9_R2.EntityPlayer;
import net.minecraft.server.v1_9_R2.EntityTypes;
import net.minecraft.server.v1_9_R2.IBlockData;
import net.minecraft.server.v1_9_R2.NBTTagCompound;
import net.minecraft.server.v1_9_R2.NibbleArray;
import net.minecraft.server.v1_9_R2.TileEntity;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.v1_9_R1.CraftChunk; import org.bukkit.craftbukkit.v1_9_R2.CraftChunk;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.generator.BlockPopulator; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], DataPaletteBlock> { public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], DataPaletteBlock> {
@ -53,9 +59,16 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
Field fieldAir = DataPaletteBlock.class.getDeclaredField("a"); Field fieldAir = DataPaletteBlock.class.getDeclaredField("a");
fieldAir.setAccessible(true); fieldAir.setAccessible(true);
air = (IBlockData) fieldAir.get(null); air = (IBlockData) fieldAir.get(null);
if (adapter == null) {
setupAdapter(new FaweAdapter_1_9());
Fawe.debug("=========================================");
Fawe.debug("Using adapter: " + adapter);
Fawe.debug("=========================================");
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
Player player = null;
} }
@Override @Override
@ -88,37 +101,47 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
} }
@Override @Override
public boolean fixLighting(final FaweChunk pc, final boolean fixAll) { public boolean fixLighting(final FaweChunk pc, RelightMode mode) {
try { try {
final CharFaweChunk bc = (CharFaweChunk) pc; CharFaweChunk bc = (CharFaweChunk) pc;
final Chunk chunk = (Chunk) bc.getChunk(); Chunk chunk = (Chunk) bc.getChunk();
if (!chunk.isLoaded()) { if (!chunk.isLoaded()) {
if (Fawe.get().getMainThread() != Thread.currentThread()) { if (Fawe.get().getMainThread() != Thread.currentThread()) {
return false; return false;
} }
chunk.load(false); chunk.load(false);
} }
// Initialize lighting net.minecraft.server.v1_9_R2.Chunk c = ((CraftChunk) chunk).getHandle();
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle(); ChunkSection[] sections = c.getSections();
if (mode != RelightMode.MINIMAL) {
for (int i = 0; i < sections.length; i++) {
ChunkSection section = sections[i];
if (section != null) {
if (mode == RelightMode.ALL) {
section.a(new NibbleArray());
}
section.b(new NibbleArray());
}
}
}
c.initLighting(); c.initLighting();
if (((bc.getTotalRelight() == 0) && mode == RelightMode.MINIMAL)) {
if (((bc.getTotalRelight() == 0) && !fixAll)) {
return true; return true;
} }
if (mode == RelightMode.ALL) {
ChunkSection[] sections = c.getSections(); bc = getPrevious(bc, c.getSections(), null, null, null, true);
net.minecraft.server.v1_9_R1.World w = c.world; }
int total = bc.getTotalCount();
net.minecraft.server.v1_9_R2.World w = c.world;
final int X = chunk.getX() << 4; final int X = chunk.getX() << 4;
final int Z = chunk.getZ() << 4; final int Z = chunk.getZ() << 4;
BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0); BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0);
for (int j = 0; j < sections.length; j++) { for (int j = 0; j < sections.length; j++) {
final Object section = sections[j]; final Object section = sections[j];
if (section == null) { if (section == null) {
continue; continue;
} }
if (((bc.getRelight(j) == 0) && !fixAll) || (bc.getCount(j) == 0) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0))) { if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0))) {
continue; continue;
} }
final char[] array = bc.getIdArray(j); final char[] array = bc.getIdArray(j);
@ -136,7 +159,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
case 0: case 0:
continue; continue;
default: default:
if (!fixAll) { if (mode == RelightMode.MINIMAL) {
continue; continue;
} }
if ((k & 1) == l) { if ((k & 1) == l) {
@ -161,7 +184,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
final int x = FaweCache.CACHE_X[j][k]; final int x = FaweCache.CACHE_X[j][k];
final int y = FaweCache.CACHE_Y[j][k]; final int y = FaweCache.CACHE_Y[j][k];
final int z = FaweCache.CACHE_Z[j][k]; final int z = FaweCache.CACHE_Z[j][k];
if (this.isSurrounded(bc.getIdArrays(), x, y, z)) { if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) {
continue; continue;
} }
pos.c(X + x, y, Z + z); pos.c(X + x, y, Z + z);
@ -249,62 +272,199 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
} }
@Override @Override
public boolean setComponents(final FaweChunk pc, RunnableVal<FaweChunk> changeTask) { public CharFaweChunk getPrevious(CharFaweChunk fs, ChunkSection[] sections, Map<?, ?> tilesGeneric, Collection<?>[] entitiesGeneric, Set<UUID> createdEntities, boolean all) throws Exception {
// TODO change task Map<BlockPosition, TileEntity> tiles = (Map<BlockPosition, TileEntity>) tilesGeneric;
{ Collection<Entity>[] entities = (Collection<Entity>[]) entitiesGeneric;
// blah, stuff CharFaweChunk previous = (CharFaweChunk) getChunk(fs.getX(), fs.getZ());
// Copy blocks
char[][] idPrevious = new char[16][];
for (int layer = 0; layer < sections.length; layer++) {
if (fs.getCount(layer) != 0 || all) {
ChunkSection section = sections[layer];
if (section != null) {
short solid = 0;
char[] previousLayer = idPrevious[layer] = new char[4096];
DataPaletteBlock blocks = section.getBlocks();
for (int j = 0; j < 4096; j++) {
int x = FaweCache.CACHE_X[0][j];
int y = FaweCache.CACHE_Y[0][j];
int z = FaweCache.CACHE_Z[0][j];
IBlockData ibd = blocks.a(x, y, z);
Block block = ibd.getBlock();
int combined = Block.getId(block);
if (FaweCache.hasData(combined)) {
combined = (combined << 4) + block.toLegacyData(ibd);
} else {
combined = combined << 4;
}
if (combined > 1) {
solid++;
}
previousLayer[j] = (char) combined;
}
previous.count[layer] = solid;
previous.air[layer] = (short) (4096 - solid);
}
}
} }
previous.ids = idPrevious;
// Copy tiles
if (tiles != null) {
for (Map.Entry<BlockPosition, TileEntity> entry : tiles.entrySet()) {
TileEntity tile = entry.getValue();
NBTTagCompound tag = new NBTTagCompound();
tile.save(tag); // readTagIntoEntity
BlockPosition pos = entry.getKey();
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
previous.setTile(pos.getX(), pos.getY(), pos.getZ(), nativeTag);
}
}
// Copy entities
if (entities != null) {
for (Collection<Entity> entityList : entities) {
for (Entity ent : entityList) {
if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && !createdEntities.contains(ent.getUniqueID()))) {
continue;
}
int x = ((int) Math.round(ent.locX) & 15);
int z = ((int) Math.round(ent.locZ) & 15);
int y = (int) Math.round(ent.locY);
int i = FaweCache.CACHE_I[y][x][z];
char[] array = fs.getIdArray(i);
if (array == null) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
String id = EntityTypes.b(ent);
if (id != null) {
NBTTagCompound tag = new NBTTagCompound();
ent.e(tag); // readEntityIntoTag
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
Map<String, Tag> map = ReflectionUtils.getMap(nativeTag.getValue());
map.put("Id", new StringTag(id));
previous.setEntity(nativeTag);
}
}
}
}
}
return previous;
}
final BukkitChunk_1_9 fs = (BukkitChunk_1_9) pc; @Override
public boolean setComponents(final FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
final BukkitChunk_1_9 fs = (BukkitChunk_1_9) fc;
final Chunk chunk = (Chunk) fs.getChunk(); final Chunk chunk = (Chunk) fs.getChunk();
final World world = chunk.getWorld(); final World world = chunk.getWorld();
chunk.load(true); chunk.load(true);
try { try {
final boolean flag = world.getEnvironment() == Environment.NORMAL; final boolean flag = world.getEnvironment() == Environment.NORMAL;
net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
// Sections net.minecraft.server.v1_9_R2.World nmsWorld = nmsChunk.world;
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle(); ChunkSection[] sections = nmsChunk.getSections();
net.minecraft.server.v1_9_R1.World w = c.world; Class<? extends net.minecraft.server.v1_9_R2.Chunk> clazzChunk = nmsChunk.getClass();
ChunkSection[] sections = c.getSections();
Class<? extends net.minecraft.server.v1_9_R1.Chunk> clazzChunk = c.getClass();
final Field ef = clazzChunk.getDeclaredField("entitySlices"); final Field ef = clazzChunk.getDeclaredField("entitySlices");
final Collection<?>[] entities = (Collection<?>[]) ef.get(c); final Collection<Entity>[] entities = (Collection<Entity>[]) ef.get(nmsChunk);
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
// Trim tiles // Remove entities
boolean removed = false; for (int i = 0; i < entities.length; i++) {
Map<BlockPosition, TileEntity> tiles = c.getTileEntities(); int count = fs.getCount(i);
if (fs.getTotalCount() >= 65536) { if (count == 0) {
tiles.clear(); continue;
removed = true; } else if (count >= 4096) {
} else {
Iterator<Entry<BlockPosition, TileEntity>> iter = tiles.entrySet().iterator();
while (iter.hasNext()) {
Entry<BlockPosition, TileEntity> tile = iter.next();
BlockPosition pos = tile.getKey();
final int lx = pos.getX() & 15;
final int ly = pos.getY();
final int lz = pos.getZ() & 15;
final int j = FaweCache.CACHE_I[ly][lx][lz];
final int k = FaweCache.CACHE_J[ly][lx][lz];
final char[] array = fs.getIdArray(j);
if (array == null) {
continue;
}
if (array[k] != 0) {
removed = true;
iter.remove();
}
}
}
// Trim entities
for (int i = 0; i < 16; i++) {
if ((entities[i] != null) && (fs.getCount(i) >= 4096)) {
entities[i].clear(); entities[i].clear();
} else {
char[] array = fs.getIdArray(i);
Collection<Entity> ents = new ArrayList<>(entities[i]);
for (Entity entity : ents) {
if (entity instanceof EntityPlayer) {
continue;
}
int x = ((int) Math.round(entity.locX) & 15);
int z = ((int) Math.round(entity.locZ) & 15);
int y = (int) Math.round(entity.locY);
if (array == null || y < 0 || y > 255) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
nmsWorld.removeEntity(entity);
}
}
} }
} }
// Efficiently merge sections HashSet<UUID> entsToRemove = fs.getEntityRemoves();
if (entsToRemove.size() > 0) {
for (int i = 0; i < entities.length; i++) {
Collection<Entity> ents = new ArrayList<>(entities[i]);
for (Entity entity : ents) {
if (entsToRemove.contains(entity.getUniqueID())) {
nmsWorld.removeEntity(entity);
}
}
}
}
// Set entities
Set<UUID> createdEntities = new HashSet<>();
Set<CompoundTag> entitiesToSpawn = fs.getEntities();
for (CompoundTag nativeTag : entitiesToSpawn) {
Map<String, Tag> entityTagMap = ReflectionUtils.getMap(nativeTag.getValue());
StringTag idTag = (StringTag) entityTagMap.get("Id");
ListTag posTag = (ListTag) entityTagMap.get("Pos");
ListTag rotTag = (ListTag) entityTagMap.get("Rotation");
if (idTag == null || posTag == null || rotTag == null) {
Fawe.debug("Unknown entity tag: " + nativeTag);
continue;
}
double x = posTag.getDouble(0);
double y = posTag.getDouble(1);
double z = posTag.getDouble(2);
float yaw = rotTag.getFloat(0);
float pitch = rotTag.getFloat(1);
String id = idTag.getValue();
Entity entity = EntityTypes.createEntityByName(id, nmsWorld);
if (entity != null) {
UUID uuid = entity.getUniqueID();
entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits()));
entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits()));
if (nativeTag != null) {
NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag);
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
entity.f(tag);
}
entity.setLocation(x, y, z, yaw, pitch);
nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM);
createdEntities.add(entity.getUniqueID());
}
}
// Change task?
if (changeTask != null) {
CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false);
changeTask.run(previous);
}
// Trim tiles
Set<Entry<BlockPosition, TileEntity>> entryset = tiles.entrySet();
Iterator<Map.Entry<BlockPosition, TileEntity>> iterator = entryset.iterator();
while (iterator.hasNext()) {
Map.Entry<BlockPosition, TileEntity> tile = iterator.next();
BlockPosition pos = tile.getKey();
int lx = pos.getX() & 15;
int ly = pos.getY();
int lz = pos.getZ() & 15;
int j = FaweCache.CACHE_I[ly][lx][lz];
char[] array = fs.getIdArray(j);
if (array == null) {
continue;
}
int k = FaweCache.CACHE_J[ly][lx][lz];
if (array[k] != 0) {
iterator.remove();
}
}
// Set blocks
for (int j = 0; j < sections.length; j++) { for (int j = 0; j < sections.length; j++) {
int count = fs.getCount(j); int count = fs.getCount(j);
if (count == 0) { if (count == 0) {
@ -367,7 +527,38 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
} }
setCount(0, nonEmptyBlockCount, section); setCount(0, nonEmptyBlockCount, section);
} }
// Clear // Set biomes
int[][] biomes = fs.biomes;
if (biomes != null) {
for (int x = 0; x < 16; x++) {
int[] array = biomes[x];
if (array == null) {
continue;
}
for (int z = 0; z < 16; z++) {
int biome = array[z];
if (biome == 0) {
continue;
}
nmsChunk.getBiomeIndex()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome;
}
}
}
// Set tiles
Map<BytePair, CompoundTag> tilesToSpawn = fs.getTiles();
int bx = fs.getX() << 4;
int bz = fs.getZ() << 4;
for (Map.Entry<BytePair, CompoundTag> entry : tilesToSpawn.entrySet()) {
CompoundTag nativeTag = entry.getValue();
BytePair pair = entry.getKey();
BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
if (tileEntity != null) {
NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag);
tileEntity.a(tag); // ReadTagIntoTile
}
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -392,117 +583,9 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
return true; return true;
} }
/** @Deprecated
* This method is called when the server is < 1% available memory (i.e. likely to crash)<br>
* - You can disable this in the conifg<br>
* - Will try to free up some memory<br>
* - Clears the queue<br>
* - Clears worldedit history<br>
* - Clears entities<br>
* - Unloads chunks in vacant worlds<br>
* - Unloads non visible chunks<br>
*/
@Override
public void saveMemory() {
super.saveMemory();
// Clear the queue
super.clear();
ArrayDeque<Chunk> toUnload = new ArrayDeque<>();
final int distance = Bukkit.getViewDistance() + 2;
HashMap<String, HashMap<IntegerPair, Integer>> players = new HashMap<>();
for (final Player player : Bukkit.getOnlinePlayers()) {
// Clear history
final FawePlayer<Object> fp = FawePlayer.wrap(player);
final LocalSession s = fp.getSession();
if (s != null) {
s.clearHistory();
s.setClipboard(null);
}
final Location loc = player.getLocation();
final World worldObj = loc.getWorld();
final String world = worldObj.getName();
HashMap<IntegerPair, Integer> map = players.get(world);
if (map == null) {
map = new HashMap<>();
players.put(world, map);
}
final IntegerPair origin = new IntegerPair(loc.getBlockX() >> 4, loc.getBlockZ() >> 4);
Integer val = map.get(origin);
int check;
if (val != null) {
if (val == distance) {
continue;
}
check = distance - val;
} else {
check = distance;
map.put(origin, distance);
}
for (int x = -distance; x <= distance; x++) {
if ((x >= check) || (-x >= check)) {
continue;
}
for (int z = -distance; z <= distance; z++) {
if ((z >= check) || (-z >= check)) {
continue;
}
final int weight = distance - Math.max(Math.abs(x), Math.abs(z));
final IntegerPair chunk = new IntegerPair(x + origin.x, z + origin.z);
val = map.get(chunk);
if ((val == null) || (val < weight)) {
map.put(chunk, weight);
}
}
}
}
Fawe.get().getWorldEdit().clearSessions();
for (final World world : Bukkit.getWorlds()) {
final String name = world.getName();
final HashMap<IntegerPair, Integer> map = players.get(name);
if ((map == null) || (map.size() == 0)) {
final boolean save = world.isAutoSave();
world.setAutoSave(false);
for (final Chunk chunk : world.getLoadedChunks()) {
this.unloadChunk(name, chunk);
}
world.setAutoSave(save);
continue;
}
final Chunk[] chunks = world.getLoadedChunks();
for (final Chunk chunk : chunks) {
final int x = chunk.getX();
final int z = chunk.getZ();
if (!map.containsKey(new IntegerPair(x, z))) {
toUnload.add(chunk);
} else if (chunk.getEntities().length > 4096) {
for (final Entity ent : chunk.getEntities()) {
ent.remove();
}
}
}
}
// GC again
System.gc();
System.gc();
// If still critical memory
int free = MemUtil.calculateMemory();
if (free <= 1) {
for (final Chunk chunk : toUnload) {
this.unloadChunk(chunk.getWorld().getName(), chunk);
}
} else if (free == Integer.MAX_VALUE) {
for (final Chunk chunk : toUnload) {
chunk.unload(true, false);
}
} else {
return;
}
toUnload = null;
players = null;
}
public boolean unloadChunk(final String world, final Chunk chunk) { public boolean unloadChunk(final String world, final Chunk chunk) {
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle(); net.minecraft.server.v1_9_R2.Chunk c = ((CraftChunk) chunk).getHandle();
c.mustSave = false; c.mustSave = false;
if (chunk.isLoaded()) { if (chunk.isLoaded()) {
chunk.unload(false, false); chunk.unload(false, false);
@ -510,133 +593,6 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
return true; return true;
} }
public ChunkGenerator setGenerator(final World world, final ChunkGenerator newGen) {
try {
final ChunkGenerator gen = world.getGenerator();
final Class<? extends World> clazz = world.getClass();
final Field generator = clazz.getDeclaredField("generator");
generator.setAccessible(true);
generator.set(world, newGen);
final Field wf = clazz.getDeclaredField("world");
wf.setAccessible(true);
final Object w = wf.get(world);
final Class<?> clazz2 = w.getClass().getSuperclass();
final Field generator2 = clazz2.getDeclaredField("generator");
generator2.set(w, newGen);
return gen;
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
public List<BlockPopulator> setPopulator(final World world, final List<BlockPopulator> newPop) {
try {
final List<BlockPopulator> pop = world.getPopulators();
final Field populators = world.getClass().getDeclaredField("populators");
populators.setAccessible(true);
populators.set(world, newPop);
return pop;
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
public void setEntitiesAndTiles(final Chunk chunk, final List<?>[] entities, final Map<?, ?> tiles) {
try {
final Class<? extends Chunk> clazz = chunk.getClass();
final Method handle = clazz.getMethod("getHandle");
final Object c = handle.invoke(chunk);
final Class<? extends Object> clazz2 = c.getClass();
if (tiles.size() > 0) {
final Field tef = clazz2.getDeclaredField("tileEntities");
final Map<?, ?> te = (Map<?, ?>) tef.get(c);
final Method put = te.getClass().getMethod("putAll", Map.class);
put.invoke(te, tiles);
}
final Field esf = clazz2.getDeclaredField("entitySlices");
esf.setAccessible(true);
esf.set(c, entities);
} catch (final Exception e) {
e.printStackTrace();
}
}
public Object getProvider(final World world) {
try {
// Provider 1
final Class<? extends World> clazz = world.getClass();
final Field wf = clazz.getDeclaredField("world");
wf.setAccessible(true);
final Object w = wf.get(world);
final Field provider = w.getClass().getSuperclass().getDeclaredField("chunkProvider");
provider.setAccessible(true);
// ChunkProviderServer
final Class<? extends Object> clazz2 = w.getClass();
final Field wpsf = clazz2.getDeclaredField("chunkProviderServer");
// Store old provider server
final Object worldProviderServer = wpsf.get(w);
// Store the old provider
final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider");
return cp.get(worldProviderServer);
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
public Object setProvider(final World world, Object newProvider) {
try {
// Provider 1
final Class<? extends World> clazz = world.getClass();
final Field wf = clazz.getDeclaredField("world");
wf.setAccessible(true);
final Object w = wf.get(world);
// ChunkProviderServer
final Class<? extends Object> clazz2 = w.getClass();
final Field wpsf = clazz2.getDeclaredField("chunkProviderServer");
// Store old provider server
final Object worldProviderServer = wpsf.get(w);
// Store the old provider
final Field cp = worldProviderServer.getClass().getDeclaredField("chunkProvider");
final Object oldProvider = cp.get(worldProviderServer);
// Provider 2
final Class<? extends Object> clazz3 = worldProviderServer.getClass();
final Field provider2 = clazz3.getDeclaredField("chunkProvider");
// If the provider needs to be calculated
if (newProvider == null) {
Method k;
try {
k = clazz2.getDeclaredMethod("k");
} catch (final Throwable e) {
try {
k = clazz2.getDeclaredMethod("j");
} catch (final Throwable e2) {
e2.printStackTrace();
return null;
}
}
k.setAccessible(true);
final Object tempProviderServer = k.invoke(w);
newProvider = cp.get(tempProviderServer);
// Restore old provider
wpsf.set(w, worldProviderServer);
}
// Set provider for provider server
provider2.set(worldProviderServer, newProvider);
// Return the previous provider
return oldProvider;
} catch (final Exception e) {
e.printStackTrace();
}
return null;
}
@Override @Override
public FaweChunk getChunk(int x, int z) { public FaweChunk getChunk(int x, int z) {
return new BukkitChunk_1_9(this, x, z); return new BukkitChunk_1_9(this, x, z);

View File

@ -0,0 +1,373 @@
package com.boydti.fawe.bukkit.v1_9;
import com.google.common.base.Preconditions;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.EndTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.internal.Constants;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import net.minecraft.server.v1_9_R2.BiomeBase;
import net.minecraft.server.v1_9_R2.BlockPosition;
import net.minecraft.server.v1_9_R2.EntityTypes;
import net.minecraft.server.v1_9_R2.NBTBase;
import net.minecraft.server.v1_9_R2.NBTTagByte;
import net.minecraft.server.v1_9_R2.NBTTagByteArray;
import net.minecraft.server.v1_9_R2.NBTTagCompound;
import net.minecraft.server.v1_9_R2.NBTTagDouble;
import net.minecraft.server.v1_9_R2.NBTTagEnd;
import net.minecraft.server.v1_9_R2.NBTTagFloat;
import net.minecraft.server.v1_9_R2.NBTTagInt;
import net.minecraft.server.v1_9_R2.NBTTagIntArray;
import net.minecraft.server.v1_9_R2.NBTTagList;
import net.minecraft.server.v1_9_R2.NBTTagLong;
import net.minecraft.server.v1_9_R2.NBTTagShort;
import net.minecraft.server.v1_9_R2.NBTTagString;
import net.minecraft.server.v1_9_R2.TileEntity;
import net.minecraft.server.v1_9_R2.World;
import net.minecraft.server.v1_9_R2.WorldServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_9_R2.CraftServer;
import org.bukkit.craftbukkit.v1_9_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_9_R2.block.CraftBlock;
import org.bukkit.craftbukkit.v1_9_R2.entity.CraftEntity;
import org.bukkit.event.entity.CreatureSpawnEvent;
public final class FaweAdapter_1_9 implements BukkitImplAdapter
{
private final Logger logger = Logger.getLogger(getClass().getCanonicalName());
private final Field nbtListTagListField;
private final Method nbtCreateTagMethod;
public FaweAdapter_1_9()
throws NoSuchFieldException, NoSuchMethodException
{
CraftServer.class.cast(Bukkit.getServer());
this.nbtListTagListField = NBTTagList.class.getDeclaredField("list");
this.nbtListTagListField.setAccessible(true);
this.nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", new Class[] { Byte.TYPE });
this.nbtCreateTagMethod.setAccessible(true);
}
private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity)
{
tileEntity.a(tag);
}
private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag)
{
tileEntity.save(tag);
}
@Nullable
private static String getEntityId(net.minecraft.server.v1_9_R2.Entity entity)
{
return EntityTypes.b(entity);
}
@Nullable
private static net.minecraft.server.v1_9_R2.Entity createEntityFromId(String id, World world)
{
return EntityTypes.createEntityByName(id, world);
}
private static void readTagIntoEntity(NBTTagCompound tag, net.minecraft.server.v1_9_R2.Entity entity)
{
entity.f(tag);
}
private static void readEntityIntoTag(net.minecraft.server.v1_9_R2.Entity entity, NBTTagCompound tag)
{
entity.e(tag);
}
public int getBlockId(Material material)
{
return material.getId();
}
public Material getMaterial(int id)
{
return Material.getMaterial(id);
}
public int getBiomeId(Biome biome)
{
BiomeBase mcBiome = CraftBlock.biomeToBiomeBase(biome);
return mcBiome != null ? BiomeBase.a(mcBiome) : 0;
}
public Biome getBiome(int id)
{
BiomeBase mcBiome = BiomeBase.getBiome(id);
return CraftBlock.biomeBaseToBiome(mcBiome);
}
public BaseBlock getBlock(Location location)
{
Preconditions.checkNotNull(location);
CraftWorld craftWorld = (CraftWorld)location.getWorld();
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
Block bukkitBlock = location.getBlock();
BaseBlock block = new BaseBlock(bukkitBlock.getTypeId(), bukkitBlock.getData());
TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
if (te != null)
{
NBTTagCompound tag = new NBTTagCompound();
readTileEntityIntoTag(te, tag);
block.setNbtData((CompoundTag)toNative(tag));
}
return block;
}
public boolean setBlock(Location location, BaseBlock block, boolean notifyAndLight)
{
Preconditions.checkNotNull(location);
Preconditions.checkNotNull(block);
CraftWorld craftWorld = (CraftWorld)location.getWorld();
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
boolean changed = location.getBlock().setTypeIdAndData(block.getId(), (byte)block.getData(), notifyAndLight);
CompoundTag nativeTag = block.getNbtData();
if (nativeTag != null)
{
TileEntity tileEntity = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
if (tileEntity != null)
{
NBTTagCompound tag = (NBTTagCompound)fromNative(nativeTag);
tag.set("x", new NBTTagInt(x));
tag.set("y", new NBTTagInt(y));
tag.set("z", new NBTTagInt(z));
readTagIntoTileEntity(tag, tileEntity);
}
}
return changed;
}
public BaseEntity getEntity(org.bukkit.entity.Entity entity)
{
Preconditions.checkNotNull(entity);
CraftEntity craftEntity = (CraftEntity)entity;
net.minecraft.server.v1_9_R2.Entity mcEntity = craftEntity.getHandle();
String id = getEntityId(mcEntity);
if (id != null)
{
NBTTagCompound tag = new NBTTagCompound();
readEntityIntoTag(mcEntity, tag);
return new BaseEntity(id, (CompoundTag)toNative(tag));
}
return null;
}
@Nullable
public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state)
{
Preconditions.checkNotNull(location);
Preconditions.checkNotNull(state);
CraftWorld craftWorld = (CraftWorld)location.getWorld();
WorldServer worldServer = craftWorld.getHandle();
net.minecraft.server.v1_9_R2.Entity createdEntity = createEntityFromId(state.getTypeId(), craftWorld.getHandle());
if (createdEntity != null)
{
CompoundTag nativeTag = state.getNbtData();
if (nativeTag != null)
{
NBTTagCompound tag = (NBTTagCompound)fromNative(nativeTag);
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
readTagIntoEntity(tag, createdEntity);
}
createdEntity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
worldServer.addEntity(createdEntity, CreatureSpawnEvent.SpawnReason.CUSTOM);
return createdEntity.getBukkitEntity();
}
return null;
}
private Tag toNative(NBTBase foreign)
{
if (foreign == null) {
return null;
}
if ((foreign instanceof NBTTagCompound))
{
Map<String, Tag> values = new HashMap();
Set<String> foreignKeys = ((NBTTagCompound)foreign).c();
for (String str : foreignKeys)
{
NBTBase base = ((NBTTagCompound)foreign).get(str);
values.put(str, toNative(base));
}
return new CompoundTag(values);
}
if ((foreign instanceof NBTTagByte)) {
return new ByteTag(((NBTTagByte)foreign).f());
}
if ((foreign instanceof NBTTagByteArray)) {
return new ByteArrayTag(((NBTTagByteArray)foreign).c());
}
if ((foreign instanceof NBTTagDouble)) {
return new DoubleTag(((NBTTagDouble)foreign).g());
}
if ((foreign instanceof NBTTagFloat)) {
return new FloatTag(((NBTTagFloat)foreign).h());
}
if ((foreign instanceof NBTTagInt)) {
return new IntTag(((NBTTagInt)foreign).d());
}
if ((foreign instanceof NBTTagIntArray)) {
return new IntArrayTag(((NBTTagIntArray)foreign).c());
}
if ((foreign instanceof NBTTagList)) {
try
{
return toNativeList((NBTTagList)foreign);
}
catch (Throwable e)
{
this.logger.log(Level.WARNING, "Failed to convert NBTTagList", e);
return new ListTag(ByteTag.class, new ArrayList());
}
}
if ((foreign instanceof NBTTagLong)) {
return new LongTag(((NBTTagLong)foreign).c());
}
if ((foreign instanceof NBTTagShort)) {
return new ShortTag(((NBTTagShort)foreign).e());
}
if ((foreign instanceof NBTTagString)) {
return new StringTag(((NBTTagString)foreign).a_());
}
if ((foreign instanceof NBTTagEnd)) {
return new EndTag();
}
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
}
private ListTag toNativeList(NBTTagList foreign)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
{
List<Tag> values = new ArrayList();
int type = foreign.d();
List foreignList = (List)this.nbtListTagListField.get(foreign);
for (int i = 0; i < foreign.size(); i++)
{
NBTBase element = (NBTBase)foreignList.get(i);
values.add(toNative(element));
}
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
return new ListTag(cls, values);
}
private NBTBase fromNative(Tag foreign)
{
if (foreign == null) {
return null;
}
Map.Entry<String, Tag> entry;
if ((foreign instanceof CompoundTag))
{
NBTTagCompound tag = new NBTTagCompound();
for (Iterator localIterator = ((CompoundTag)foreign)
.getValue().entrySet().iterator(); localIterator.hasNext();)
{
entry = (Map.Entry)localIterator.next();
tag.set((String)entry.getKey(), fromNative((Tag)entry.getValue()));
}
return tag;
}
if ((foreign instanceof ByteTag)) {
return new NBTTagByte(((ByteTag)foreign).getValue().byteValue());
}
if ((foreign instanceof ByteArrayTag)) {
return new NBTTagByteArray(((ByteArrayTag)foreign).getValue());
}
if ((foreign instanceof DoubleTag)) {
return new NBTTagDouble(((DoubleTag)foreign).getValue().doubleValue());
}
if ((foreign instanceof FloatTag)) {
return new NBTTagFloat(((FloatTag)foreign).getValue().floatValue());
}
if ((foreign instanceof IntTag)) {
return new NBTTagInt(((IntTag)foreign).getValue().intValue());
}
if ((foreign instanceof IntArrayTag)) {
return new NBTTagIntArray(((IntArrayTag)foreign).getValue());
}
if ((foreign instanceof ListTag))
{
NBTTagList tag = new NBTTagList();
ListTag foreignList = (ListTag)foreign;
for (Tag t : foreignList.getValue()) {
tag.add(fromNative(t));
}
return tag;
}
if ((foreign instanceof LongTag)) {
return new NBTTagLong(((LongTag)foreign).getValue().longValue());
}
if ((foreign instanceof ShortTag)) {
return new NBTTagShort(((ShortTag)foreign).getValue().shortValue());
}
if ((foreign instanceof StringTag)) {
return new NBTTagString(((StringTag)foreign).getValue());
}
if ((foreign instanceof EndTag)) {
try
{
return (NBTBase)this.nbtCreateTagMethod.invoke(null, new Object[] { Byte.valueOf((byte) 0) });
}
catch (Exception e)
{
return null;
}
}
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
}
}

View File

@ -21,9 +21,9 @@ commands:
fawe: fawe:
description: (FAWE) Reload the plugin description: (FAWE) Reload the plugin
aliases: [/fawe,/fawereload] aliases: [/fawe,/fawereload]
wrg: select:
description: (FAWE) Select your current WorldEdit Region. description: (FAWE) Select your current WorldEdit Region.
aliases: [/wrg,wer,/wer,worldeditregion,/worldeditregion,/region] aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region]
frb: frb:
description: (FAWE) Rollback an edit description: (FAWE) Rollback an edit
aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb]

View File

@ -200,7 +200,7 @@ public class Fawe {
this.IMP.setupCommand("wea", new Wea()); this.IMP.setupCommand("wea", new Wea());
this.IMP.setupCommand("fixlighting", new FixLighting()); this.IMP.setupCommand("fixlighting", new FixLighting());
this.IMP.setupCommand("stream", new Stream()); this.IMP.setupCommand("stream", new Stream());
this.IMP.setupCommand("wrg", new WorldEditRegion()); this.IMP.setupCommand("select", new WorldEditRegion());
this.IMP.setupCommand("fawe", new Reload()); this.IMP.setupCommand("fawe", new Reload());
this.IMP.setupCommand("frb", new Rollback()); this.IMP.setupCommand("frb", new Rollback());
this.IMP.setupCommand("fcancel", new Cancel()); this.IMP.setupCommand("fcancel", new Cancel());

View File

@ -18,6 +18,7 @@ import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.ShortTag; import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
@ -39,6 +40,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import javax.annotation.Nonnull;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
@ -51,6 +53,31 @@ import org.bukkit.Location;
*/ */
public class FaweAPI { public class FaweAPI {
/**
* Get a new EditSessionfor a player<br>
* - The EditSession can be used from another thread<br>
* - FAWE will handle
* @see com.boydti.fawe.object.FawePlayer#wrap(Object)
* @param player
* @return
*/
public EditSession getNewEditSession(@Nonnull FawePlayer player) {
if (player == null) {
throw new IllegalArgumentException("Player may not be null");
}
return player.getNewEditSession();
}
/**
* Get a new non-player EditSession
* @see #getWorld(String)
* @param world
* @return
*/
public EditSession getNewEditSession(World world) {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(world, -1);
}
/** /**
* The TaskManager has some useful methods for doing things asynchronously * The TaskManager has some useful methods for doing things asynchronously
* @return TaskManager * @return TaskManager
@ -133,7 +160,7 @@ public class FaweAPI {
* Cancel the edit with the following extent<br> * Cancel the edit with the following extent<br>
* - The extent must be the one being used by an EditSession, otherwise an error may be thrown <br> * - The extent must be the one being used by an EditSession, otherwise an error may be thrown <br>
* - Insert an extent into the EditSession using the EditSessionEvent: http://wiki.sk89q.com/wiki/WorldEdit/API/Hooking_EditSession <br> * - Insert an extent into the EditSession using the EditSessionEvent: http://wiki.sk89q.com/wiki/WorldEdit/API/Hooking_EditSession <br>
* @see com.sk89q.worldedit.EditSession#getFaweExtent() To get the FaweExtent for an EditSession * @see com.sk89q.worldedit.EditSession#getRegionExtent() To get the FaweExtent for an EditSession
* @param extent * @param extent
* @param reason * @param reason
*/ */
@ -229,7 +256,11 @@ public class FaweAPI {
Collections.sort(files, new Comparator<File>() { Collections.sort(files, new Comparator<File>() {
@Override @Override
public int compare(File a, File b) { public int compare(File a, File b) {
long value = a.lastModified() - b.lastModified(); String aName = a.getName();
String bName = b.getName();
int aI = Integer.parseInt(aName.substring(0, aName.length() - 3));
int bI = Integer.parseInt(bName.substring(0, bName.length() - 3));
long value = aI - bI;
return value == 0 ? 0 : value < 0 ? -1 : 1; return value == 0 ? 0 : value < 0 ? -1 : 1;
} }
}); });
@ -292,21 +323,21 @@ public class FaweAPI {
* @param world * @param world
* @param x * @param x
* @param z * @param z
* @param fixAll * @param mode
*/ */
public static void fixLighting(String world, int x, int z, final boolean fixAll) { public static void fixLighting(String world, int x, int z, FaweQueue.RelightMode mode) {
FaweQueue queue = SetQueue.IMP.getNewQueue(world, true, false); FaweQueue queue = SetQueue.IMP.getNewQueue(world, true, false);
queue.fixLighting(queue.getChunk(x, z), fixAll); queue.fixLighting(queue.getChunk(x, z), mode);
} }
/** /**
* Fix the lighting in a chunk * Fix the lighting in a chunk
* @param chunk * @param chunk
* @param fixAll * @param mode
*/ */
public static void fixLighting(final Chunk chunk, final boolean fixAll) { public static void fixLighting(final Chunk chunk, FaweQueue.RelightMode mode) {
FaweQueue queue = SetQueue.IMP.getNewQueue(chunk.getWorld().getName(), true, false); FaweQueue queue = SetQueue.IMP.getNewQueue(chunk.getWorld().getName(), true, false);
queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), fixAll); queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), mode);
} }
/** /**
@ -318,6 +349,7 @@ public class FaweAPI {
* @param loc * @param loc
* @return * @return
*/ */
@Deprecated
public static void streamSchematic(final File file, final Location loc) { public static void streamSchematic(final File file, final Location loc) {
final FaweLocation fl = new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); final FaweLocation fl = new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
streamSchematic(file, fl); streamSchematic(file, fl);
@ -331,6 +363,7 @@ public class FaweAPI {
* @param loc * @param loc
* @return * @return
*/ */
@Deprecated
public static void streamSchematic(final File file, final FaweLocation loc) { public static void streamSchematic(final File file, final FaweLocation loc) {
try { try {
final FileInputStream is = new FileInputStream(file); final FileInputStream is = new FileInputStream(file);
@ -347,6 +380,7 @@ public class FaweAPI {
* @param url * @param url
* @param loc * @param loc
*/ */
@Deprecated
public static void streamSchematic(final URL url, final FaweLocation loc) { public static void streamSchematic(final URL url, final FaweLocation loc) {
try { try {
final ReadableByteChannel rbc = Channels.newChannel(url.openStream()); final ReadableByteChannel rbc = Channels.newChannel(url.openStream());
@ -366,6 +400,7 @@ public class FaweAPI {
* @param loc * @param loc
* @throws IOException * @throws IOException
*/ */
@Deprecated
public static void streamSchematic(final InputStream is, final FaweLocation loc) throws IOException { public static void streamSchematic(final InputStream is, final FaweLocation loc) throws IOException {
final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is)); final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is));
Tag tag = stream.readNamedTag().getTag(); Tag tag = stream.readNamedTag().getTag();
@ -403,79 +438,10 @@ public class FaweAPI {
final int i = i2 + x; final int i = i2 + x;
final int xx = x_offset + x; final int xx = x_offset + x;
final short id = (short) (ids[i] & 0xFF); final short id = (short) (ids[i] & 0xFF);
switch (id) { if (FaweCache.hasData(id)) {
case 0: queue.setBlock(xx, yy, zz, id, datas[i]);
case 2: } else {
case 4: queue.setBlock(xx, yy, zz, id, (byte) 0);
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 51:
case 56:
case 57:
case 58:
case 60:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 121:
case 122:
case 129:
case 133:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192:
queue.setBlock(xx, yy, zz, id, (byte) 0);
break;
default: {
queue.setBlock(xx, yy, zz, id, datas[i]);
break;
}
} }
} }
} }

View File

@ -6,26 +6,56 @@ import com.sk89q.worldedit.blocks.BaseBlock;
public class FaweCache { public class FaweCache {
/** /**
* y | x | z * [ y | x | z ] => index
*/ */
public final static short[][][] CACHE_I = new short[256][16][16]; public final static short[][][] CACHE_I = new short[256][16][16];
/** /**
* y | x | z * [ y | x | z ] => index
*/ */
public final static short[][][] CACHE_J = new short[256][16][16]; public final static short[][][] CACHE_J = new short[256][16][16];
/**
* [ i | j ] => x
*/
public final static byte[][] CACHE_X = new byte[16][4096]; public final static byte[][] CACHE_X = new byte[16][4096];
/**
* [ i | j ] => y
*/
public final static short[][] CACHE_Y = new short[16][4096]; public final static short[][] CACHE_Y = new short[16][4096];
/**
* [ i | j ] => z
*/
public final static byte[][] CACHE_Z = new byte[16][4096]; public final static byte[][] CACHE_Z = new byte[16][4096];
/**
* [ combined ] => id
* (combined >> 4) = id
*/
public final static short[] CACHE_ID = new short[65535]; public final static short[] CACHE_ID = new short[65535];
/**
* [ combined ] => data
* (combined & 0xF) = data
*/
public final static byte[] CACHE_DATA = new byte[65535]; public final static byte[] CACHE_DATA = new byte[65535];
/**
* Immutable BaseBlock cache
* [ combined ] => block
*/
public final static BaseBlock[] CACHE_BLOCK = new BaseBlock[Short.MAX_VALUE]; public final static BaseBlock[] CACHE_BLOCK = new BaseBlock[Short.MAX_VALUE];
// Faster than java random (since the game just needs to look random) /**
* Faster than java random (since it just needs to look random)
*/
public final static PseudoRandom RANDOM = new PseudoRandom(); public final static PseudoRandom RANDOM = new PseudoRandom();
/**
* Get the cached BaseBlock object for an id/data<br>
* - The block is immutable
* @param id
* @param data
* @return
*/
public static BaseBlock getBlock(int id, int data) { public static BaseBlock getBlock(int id, int data) {
return CACHE_BLOCK[(id << 4) + data]; return CACHE_BLOCK[(id << 4) + data];
} }
@ -85,6 +115,11 @@ public class FaweCache {
} }
} }
/**
* Check if an id might have data
* @param id
* @return
*/
public static boolean hasData(int id) { public static boolean hasData(int id) {
switch (id) { switch (id) {
case 0: case 0:
@ -168,6 +203,11 @@ public class FaweCache {
} }
} }
/**
* Check if an id might have nbt
* @param id
* @return
*/
public static boolean hasNBT(int id) { public static boolean hasNBT(int id) {
switch (id) { switch (id) {
case 54: case 54:

View File

@ -28,7 +28,7 @@ public class Cancel extends FaweCommand {
for (FaweQueue queue : queues) { for (FaweQueue queue : queues) {
Set<EditSession> sessions = queue.getEditSessions(); Set<EditSession> sessions = queue.getEditSessions();
for (EditSession session : sessions) { for (EditSession session : sessions) {
Actor actor = session.actor; Actor actor = session.getActor();
if (actor == null) { if (actor == null) {
continue; continue;
} }

View File

@ -2,11 +2,13 @@ package com.boydti.fawe.command;
import com.boydti.fawe.FaweAPI; import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweCommand; import com.boydti.fawe.object.FaweCommand;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
public class FixLighting extends FaweCommand { public class FixLighting extends FaweCommand {
@ -21,27 +23,27 @@ public class FixLighting extends FaweCommand {
return false; return false;
} }
final FaweLocation loc = player.getLocation(); final FaweLocation loc = player.getLocation();
final Region selection = player.getSelection();
if (selection == null) {
FaweAPI.fixLighting(loc.world, loc.x >> 4, loc.z >> 4, Settings.FIX_ALL_LIGHTING);
BBC.FIX_LIGHTING_CHUNK.send(player);
return true;
}
final int cx = loc.x >> 4; final int cx = loc.x >> 4;
final int cz = loc.z >> 4; final int cz = loc.z >> 4;
Region selection = player.getSelection();
if (selection == null) {
selection = new CuboidRegion(new Vector(cx - 8, 0, cz - 8).multiply(16), new Vector(cx + 8, 0, cz + 8).multiply(16));
}
final Vector bot = selection.getMinimumPoint(); final Vector bot = selection.getMinimumPoint();
final Vector top = selection.getMaximumPoint(); final Vector top = selection.getMaximumPoint();
final int minX = Math.max(cx - 8, bot.getBlockX() >> 4); final int minX = bot.getBlockX() >> 4;
final int minZ = Math.max(cz - 8, bot.getBlockZ() >> 4); final int minZ = bot.getBlockZ() >> 4;
final int maxX = Math.min(cx + 8, top.getBlockX() >> 4); final int maxX = top.getBlockX() >> 4;
final int maxZ = Math.min(cz + 8, top.getBlockZ() >> 4); final int maxZ = top.getBlockZ() >> 4;
int count = 0; int count = 0;
FaweQueue queue = SetQueue.IMP.getNewQueue(loc.world, true, false);
for (int x = minX; x <= maxX; x++) { for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) { for (int z = minZ; z <= maxZ; z++) {
FaweAPI.fixLighting(loc.world, x, z, Settings.FIX_ALL_LIGHTING); queue.fixLighting(queue.getChunk(x, z), FaweQueue.RelightMode.ALL);
count++; count++;
} }
} }

View File

@ -11,7 +11,6 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.blocks.ItemType;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -102,7 +101,7 @@ public class Rollback extends FaweCommand {
EditSession session = edit.toEditSession(null); EditSession session = edit.toEditSession(null);
session.undo(session); session.undo(session);
edit.deleteFiles(); edit.deleteFiles();
SetQueue.IMP.addTask(this); session.getQueue().addNotifyTask(this);
} }
}; };
task.run(); task.run();

View File

@ -17,7 +17,7 @@ public enum BBC {
* Things to note about this class: * Things to note about this class:
* Can use multiple arguments %s, %s1, %s2, %s3 etc * Can use multiple arguments %s, %s1, %s2, %s3 etc
*/ */
PREFIX("&8(&5&lFAWE&8)&7", "Info"), PREFIX("&8(&4&lFAWE&8)&7", "Info"),
SCHEMATIC_PASTING("&7The schematic is pasting. This cannot be undone.", "Info"), SCHEMATIC_PASTING("&7The schematic is pasting. This cannot be undone.", "Info"),
FIX_LIGHTING_CHUNK("&7Lighting has been fixed in your current chunk. Relog to see the affect.", "Info"), FIX_LIGHTING_CHUNK("&7Lighting has been fixed in your current chunk. Relog to see the affect.", "Info"),
FIX_LIGHTING_SELECTION("&7Lighting has been fixed in %s0 chunks. Relog to see the affect.", "Info"), FIX_LIGHTING_SELECTION("&7Lighting has been fixed in %s0 chunks. Relog to see the affect.", "Info"),
@ -95,6 +95,8 @@ public enum BBC {
SELECTOR_CUBOID_POS1("First position set to %s0 %s1.", "WorldEdit.Selector"), SELECTOR_CUBOID_POS1("First position set to %s0 %s1.", "WorldEdit.Selector"),
SELECTOR_CUBOID_POS2("Second position set to %s0 %s1.", "WorldEdit.Selector"), SELECTOR_CUBOID_POS2("Second position set to %s0 %s1.", "WorldEdit.Selector"),
PROGRESS_MESSAGE("[ Queue: %s0 | Dispatched: %s1 ]", "Progress"),
PROGRESS_DONE ("[ Took: %s0s ]", "Progress"),
COMMAND_SYNTAX("&cUsage: &7%s0", "Error"), COMMAND_SYNTAX("&cUsage: &7%s0", "Error"),

View File

@ -14,33 +14,37 @@ import java.util.Map.Entry;
public class Settings { public class Settings {
// public static boolean REQUIRE_SELECTION = false;
// public static boolean COMMAND_PROCESSOR = false;
// public static List<String> WE_BLACKLIST = Arrays.asList("cs", ".s", "restore", "snapshot", "delchunks", "listchunks");
public static long MEM_FREE = 95; public static long MEM_FREE = 95;
public static boolean ENABLE_HARD_LIMIT = true; public static boolean ENABLE_HARD_LIMIT = true;
public static boolean STORE_HISTORY_ON_DISK = false; public static boolean STORE_HISTORY_ON_DISK = false;
public static boolean STORE_CLIPBOARD_ON_DISK = false; public static boolean STORE_CLIPBOARD_ON_DISK = false;
public static int DELETE_HISTORY_AFTER_DAYS = 7; public static int DELETE_HISTORY_AFTER_DAYS = 7;
public static boolean CLEAN_HISTORY_ON_LOGOUT = true;
public static int DELETE_CLIPBOARD_AFTER_DAYS = 1; public static int DELETE_CLIPBOARD_AFTER_DAYS = 1;
public static int COMPRESSION_LEVEL = 0;
public static int BUFFER_SIZE = 531441;
public static boolean METRICS = true; public static boolean METRICS = true;
public static int CHUNK_WAIT = 100; public static int CHUNK_WAIT = 100;
public static boolean REGION_RESTRICTIONS = true; public static boolean REGION_RESTRICTIONS = true;
public static int ALLOCATE = 0; public static int ALLOCATE = 0;
public static int QUEUE_SIZE = 64; public static int QUEUE_SIZE = 64;
public static int QUEUE_MAX_WAIT = 1000; public static int QUEUE_MAX_WAIT = 1000;
public static int QUEUE_DISCARD_AFTER = 60000; public static boolean DISPLAY_PROGRESS = false;
public static int DISPLAY_PROGRESS_INTERVAL = 1;
public static List<String> ALLOWED_3RDPARTY_EXTENTS; public static List<String> ALLOWED_3RDPARTY_EXTENTS;
public static boolean EXTENT_DEBUG = true; public static boolean EXTENT_DEBUG = true;
public static int UNSAFE_PARALLEL_THREADS = 1;
public static boolean FIX_ALL_LIGHTING = true; public static boolean FIX_ALL_LIGHTING = true;
public static boolean ASYNC_LIGHTING = true; public static boolean ASYNC_LIGHTING = true;
public static int PHYSICS_PER_TICK = 500000; public static int PHYSICS_PER_TICK = 500000;
public static int ITEMS_PER_TICK = 50000; public static int ITEMS_PER_TICK = 50000;
public static boolean COMBINE_HISTORY_STAGE = true; // Maybe confusing?
// - `compression: false` just uses cheaper compression, but still compresses
public static int COMPRESSION_LEVEL = 0;
public static boolean COMBINE_HISTORY_STAGE = false;
public static int PARALLEL_THREADS = 1;
// Non configurable (yet / shouldn't be?)
public static int BUFFER_SIZE = 531441;
public static int QUEUE_DISCARD_AFTER = 60000;
public static HashMap<String, FaweLimit> limits; public static HashMap<String, FaweLimit> limits;
@ -77,13 +81,11 @@ public class Settings {
} }
} }
final YamlConfiguration config = YamlConfiguration.loadConfiguration(file); final YamlConfiguration config = YamlConfiguration.loadConfiguration(file);
config.set("DOCUMENTATION","https://github.com/boy0001/FastAsyncWorldedit/wiki/Configuration");
limits = new HashMap<>(); limits = new HashMap<>();
final Map<String, Object> options = new HashMap<>(); final Map<String, Object> options = new HashMap<>();
// options.put("require-selection-in-mask", REQUIRE_SELECTION);
// options.put("command-blacklist", WE_BLACKLIST);
// options.put("command-processor", COMMAND_PROCESSOR);
options.put("max-memory-percent", MEM_FREE); options.put("max-memory-percent", MEM_FREE);
options.put("crash-mitigation", ENABLE_HARD_LIMIT); options.put("crash-mitigation", ENABLE_HARD_LIMIT);
options.put("lighting.fix-all", FIX_ALL_LIGHTING); options.put("lighting.fix-all", FIX_ALL_LIGHTING);
@ -93,18 +95,22 @@ public class Settings {
options.put("history.use-disk", STORE_HISTORY_ON_DISK); options.put("history.use-disk", STORE_HISTORY_ON_DISK);
options.put("history.compress", false); options.put("history.compress", false);
options.put("history.chunk-wait-ms", CHUNK_WAIT); options.put("history.chunk-wait-ms", CHUNK_WAIT);
// options.put("history.buffer-size", BUFFER_SIZE);
options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS); options.put("history.delete-after-days", DELETE_HISTORY_AFTER_DAYS);
options.put("history.delete-on-logout", CLEAN_HISTORY_ON_LOGOUT);
options.put("region-restrictions", REGION_RESTRICTIONS); options.put("region-restrictions", REGION_RESTRICTIONS);
options.put("queue.parallel-threads", UNSAFE_PARALLEL_THREADS);
options.put("queue.extra-time-ms", ALLOCATE); options.put("queue.extra-time-ms", ALLOCATE);
options.put("queue.progress.display", DISPLAY_PROGRESS);
options.put("queue.progress.interval", DISPLAY_PROGRESS_INTERVAL);
options.put("queue.target-size", QUEUE_SIZE); options.put("queue.target-size", QUEUE_SIZE);
options.put("queue.max-wait-ms", QUEUE_MAX_WAIT); options.put("queue.max-wait-ms", QUEUE_MAX_WAIT);
// options.put("queue.discard-after-ms", QUEUE_DISCARD_AFTER);
options.put("extent.allowed-plugins", new ArrayList<String>()); options.put("extent.allowed-plugins", new ArrayList<String>());
options.put("extent.debug", EXTENT_DEBUG); options.put("extent.debug", EXTENT_DEBUG);
options.put("metrics", METRICS); options.put("metrics", METRICS);
// Possibly confusing? - leave configurable since not entirely stable yet
options.put("history.combine-stages", COMBINE_HISTORY_STAGE);
options.put("queue.parallel-threads", Math.max(1, Runtime.getRuntime().availableProcessors()));
if (config.getInt("tick-limiter.physics") == 1337) { if (config.getInt("tick-limiter.physics") == 1337) {
config.set("tick-limiter.physics", PHYSICS_PER_TICK); config.set("tick-limiter.physics", PHYSICS_PER_TICK);
} }
@ -130,21 +136,19 @@ public class Settings {
FIX_ALL_LIGHTING = config.getBoolean("lighting.fix-all"); FIX_ALL_LIGHTING = config.getBoolean("lighting.fix-all");
ASYNC_LIGHTING = config.getBoolean("lighting.async"); ASYNC_LIGHTING = config.getBoolean("lighting.async");
MEM_FREE = config.getInt("max-memory-percent"); MEM_FREE = config.getInt("max-memory-percent");
// COMMAND_PROCESSOR = config.getBoolean("command-processor");
// REQUIRE_SELECTION = config.getBoolean("require-selection-in-mask");
// WE_BLACKLIST = config.getStringList("command-blacklist");
ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation"); ENABLE_HARD_LIMIT = config.getBoolean("crash-mitigation");
REGION_RESTRICTIONS = config.getBoolean("region-restrictions"); REGION_RESTRICTIONS = config.getBoolean("region-restrictions");
METRICS = config.getBoolean("metrics"); METRICS = config.getBoolean("metrics");
COMPRESSION_LEVEL = config.getInt("history.compression-level", config.getBoolean("history.compress") ? 1 : 0); COMPRESSION_LEVEL = config.getInt("history.compression-level", config.getBoolean("history.compress") ? 1 : 0);
DELETE_HISTORY_AFTER_DAYS = config.getInt("history.delete-after-days"); DELETE_HISTORY_AFTER_DAYS = config.getInt("history.delete-after-days");
BUFFER_SIZE = config.getInt("history.buffer-size", BUFFER_SIZE); CLEAN_HISTORY_ON_LOGOUT = config.getBoolean("history.delete-on-logout");
CHUNK_WAIT = config.getInt("history.chunk-wait-ms"); CHUNK_WAIT = config.getInt("history.chunk-wait-ms");
ALLOCATE = config.getInt("queue.extra-time-ms"); ALLOCATE = config.getInt("queue.extra-time-ms");
QUEUE_SIZE = config.getInt("queue.target-size"); QUEUE_SIZE = config.getInt("queue.target-size");
QUEUE_MAX_WAIT = config.getInt("queue.max-wait-ms"); QUEUE_MAX_WAIT = config.getInt("queue.max-wait-ms");
UNSAFE_PARALLEL_THREADS = config.getInt("queue.parallel-threads"); DISPLAY_PROGRESS = config.getBoolean("queue.progress.display");
QUEUE_DISCARD_AFTER = config.getInt("queue.discard-after-ms", QUEUE_DISCARD_AFTER); DISPLAY_PROGRESS_INTERVAL = config.getInt("queue.progress.interval");
PARALLEL_THREADS = config.getInt("queue.parallel-threads", Math.max(1, Runtime.getRuntime().availableProcessors()));
ALLOWED_3RDPARTY_EXTENTS = config.getStringList("extent.allowed-plugins"); ALLOWED_3RDPARTY_EXTENTS = config.getStringList("extent.allowed-plugins");
EXTENT_DEBUG = config.getBoolean("extent.debug"); EXTENT_DEBUG = config.getBoolean("extent.debug");
STORE_CLIPBOARD_ON_DISK = config.getBoolean("clipboard.use-disk"); STORE_CLIPBOARD_ON_DISK = config.getBoolean("clipboard.use-disk");
@ -152,6 +156,11 @@ public class Settings {
PHYSICS_PER_TICK = config.getInt("tick-limiter.physics"); PHYSICS_PER_TICK = config.getInt("tick-limiter.physics");
ITEMS_PER_TICK = config.getInt("tick-limiter.items"); ITEMS_PER_TICK = config.getInt("tick-limiter.items");
// Not usually configurable
BUFFER_SIZE = config.getInt("history.buffer-size", BUFFER_SIZE);
QUEUE_DISCARD_AFTER = config.getInt("queue.discard-after-ms", QUEUE_DISCARD_AFTER);
COMBINE_HISTORY_STAGE = config.getBoolean("history.combine-stages", COMBINE_HISTORY_STAGE);
if (STORE_HISTORY_ON_DISK = config.getBoolean("history.use-disk")) { if (STORE_HISTORY_ON_DISK = config.getBoolean("history.use-disk")) {
LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE;
} }

View File

@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
public abstract class CharFaweChunk<T> extends FaweChunk<T> { public abstract class CharFaweChunk<T> extends FaweChunk<T> {
@ -111,7 +112,7 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return this.ids[i]; return this.ids[i];
} }
public char[][] getIdArrays() { public char[][] getCombinedIdArrays() {
return this.ids; return this.ids;
} }
@ -153,8 +154,12 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return entities == null ? new HashSet<CompoundTag>() : entities; return entities == null ? new HashSet<CompoundTag>() : entities;
} }
public HashSet<CompoundTag> entities; public HashSet<CompoundTag> entities;
public HashSet<UUID> entityRemoves;
@Override @Override
public void setEntity(CompoundTag tag) { public void setEntity(CompoundTag tag) {
if (entities == null) { if (entities == null) {
@ -163,6 +168,19 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
entities.add(tag); entities.add(tag);
} }
@Override
public void removeEntity(UUID uuid) {
if (entityRemoves == null) {
entityRemoves = new HashSet<>();
}
entityRemoves.add(uuid);
}
@Override
public HashSet<UUID> getEntityRemoves() {
return entityRemoves == null ? new HashSet<UUID>() : entityRemoves;
}
@Override @Override
public void setBlock(final int x, final int y, final int z, final int id, byte data) { public void setBlock(final int x, final int y, final int z, final int id, byte data) {
final int i = FaweCache.CACHE_I[y][x][z]; final int i = FaweCache.CACHE_I[y][x][z];

View File

@ -14,6 +14,7 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
@ -25,7 +26,15 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
* Map of chunks in the queue * Map of chunks in the queue
*/ */
private ConcurrentHashMap<Long, FaweChunk> blocks = new ConcurrentHashMap<>(); private ConcurrentHashMap<Long, FaweChunk> blocks = new ConcurrentHashMap<>();
private LinkedBlockingDeque<FaweChunk> chunks = new LinkedBlockingDeque<>(); private LinkedBlockingDeque<FaweChunk> chunks = new LinkedBlockingDeque<FaweChunk>() {
@Override
public boolean add(FaweChunk o) {
if (progressTask != null) {
progressTask.run(ProgressType.QUEUE, size() + 1);
}
return super.add(o);
}
};
private ArrayDeque<Runnable> tasks = new ArrayDeque<>(); private ArrayDeque<Runnable> tasks = new ArrayDeque<>();
@Override @Override
@ -53,6 +62,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
@Override @Override
public void addNotifyTask(Runnable runnable) { public void addNotifyTask(Runnable runnable) {
this.tasks.add(runnable); this.tasks.add(runnable);
size();
} }
public MappedFaweQueue(final String world) { public MappedFaweQueue(final String world) {
@ -72,9 +82,6 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
@Override @Override
public abstract FaweChunk getChunk(int x, int z); public abstract FaweChunk getChunk(int x, int z);
@Override
public abstract boolean fixLighting(FaweChunk fc, boolean fixAll);
public abstract boolean loadChunk(WORLD world, int x, int z, boolean generate); public abstract boolean loadChunk(WORLD world, int x, int z, boolean generate);
public abstract CHUNK getCachedChunk(WORLD world, int cx, int cz); public abstract CHUNK getCachedChunk(WORLD world, int cx, int cz);
@ -202,6 +209,33 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
lastWrappedChunk.setEntity(tag); lastWrappedChunk.setEntity(tag);
} }
@Override
public void removeEntity(int x, int y, int z, UUID uuid) {
if ((y > 255) || (y < 0)) {
return;
}
int cx = x >> 4;
int cz = z >> 4;
if (cx != lastX || cz != lastZ) {
lastX = cx;
lastZ = cz;
long pair = (long) (cx) << 32 | (cz) & 0xFFFFFFFFL;
lastWrappedChunk = this.blocks.get(pair);
if (lastWrappedChunk == null) {
lastWrappedChunk = this.getChunk(x >> 4, z >> 4);
lastWrappedChunk.removeEntity(uuid);
FaweChunk previous = this.blocks.put(pair, lastWrappedChunk);
if (previous == null) {
chunks.add(lastWrappedChunk);
return;
}
this.blocks.put(pair, previous);
lastWrappedChunk = previous;
}
}
lastWrappedChunk.removeEntity(uuid);
}
@Override @Override
public boolean setBiome(int x, int z, BaseBiome biome) { public boolean setBiome(int x, int z, BaseBiome biome) {
long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL; long pair = (long) (x >> 4) << 32 | (z >> 4) & 0xFFFFFFFFL;
@ -243,15 +277,23 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
} }
public void runTasks() { public void runTasks() {
for (Runnable run : tasks) { if (progressTask != null) {
run.run(); progressTask.run(ProgressType.DONE, 1);
} }
ArrayDeque<Runnable> tmp = new ArrayDeque<>(tasks);
tasks.clear(); tasks.clear();
for (Runnable run : tmp) {
try {
run.run();
} catch (Throwable e) {
e.printStackTrace();
}
}
} }
@Override @Override
public int size() { public int size() {
if (chunks.size() == 0 && SetQueue.IMP.isStage(this, SetQueue.QueueStage.ACTIVE)) { if (chunks.size() == 0 && SetQueue.IMP.getStage(this) != SetQueue.QueueStage.INACTIVE) {
runTasks(); runTasks();
} }
return chunks.size(); return chunks.size();
@ -259,11 +301,17 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
private LinkedBlockingDeque<FaweChunk> toUpdate = new LinkedBlockingDeque<>(); private LinkedBlockingDeque<FaweChunk> toUpdate = new LinkedBlockingDeque<>();
private int dispatched = 0;
public boolean execute(final FaweChunk fc) { public boolean execute(final FaweChunk fc) {
if (fc == null) { if (fc == null) {
return false; return false;
} }
// Set blocks / entities / biome // Set blocks / entities / biome
if (progressTask != null) {
progressTask.run(ProgressType.QUEUE, chunks.size());
progressTask.run(ProgressType.DISPATCH, ++dispatched);
}
if (getChangeTask() != null) { if (getChangeTask() != null) {
if (!this.setComponents(fc, new RunnableVal<FaweChunk>() { if (!this.setComponents(fc, new RunnableVal<FaweChunk>() {
@Override @Override
@ -284,6 +332,7 @@ public abstract class MappedFaweQueue<WORLD, CHUNK, SECTION> extends FaweQueue {
public void clear() { public void clear() {
this.blocks.clear(); this.blocks.clear();
this.chunks.clear(); this.chunks.clear();
runTasks();
} }
@Override @Override

View File

@ -2,7 +2,12 @@ package com.boydti.fawe.example;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweChunk; import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> extends MappedFaweQueue<WORLD, CHUNKSECTION, SECTION> { public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> extends MappedFaweQueue<WORLD, CHUNKSECTION, SECTION> {
public NMSMappedFaweQueue(String world) { public NMSMappedFaweQueue(String world) {
@ -14,12 +19,12 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
TaskManager.IMP.taskSyncSoon(new Runnable() { TaskManager.IMP.taskSyncSoon(new Runnable() {
@Override @Override
public void run() { public void run() {
final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING) || !Settings.ASYNC_LIGHTING; final boolean result = fixLighting(fc, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL) || !Settings.ASYNC_LIGHTING;
TaskManager.IMP.taskSyncNow(new Runnable() { TaskManager.IMP.taskSyncNow(new Runnable() {
@Override @Override
public void run() { public void run() {
if (!result) { if (!result) {
fixLighting(fc, Settings.FIX_ALL_LIGHTING); fixLighting(fc, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL);
} }
CHUNK chunk = (CHUNK) fc.getChunk(); CHUNK chunk = (CHUNK) fc.getChunk();
refreshChunk(getWorld(), chunk); refreshChunk(getWorld(), chunk);
@ -31,6 +36,5 @@ public abstract class NMSMappedFaweQueue<WORLD, CHUNK, CHUNKSECTION, SECTION> ex
public abstract void refreshChunk(WORLD world, CHUNK chunk); public abstract void refreshChunk(WORLD world, CHUNK chunk);
@Override public abstract CharFaweChunk getPrevious(CharFaweChunk fs, CHUNKSECTION sections, Map<?, ?> tiles, Collection<?>[] entities, Set<UUID> createdEntities, boolean all) throws Exception;
public abstract boolean fixLighting(FaweChunk fc, boolean fixAll);
} }

View File

@ -9,6 +9,14 @@ public class BytePair {
int hash; int hash;
public byte get0() {
return pair[0];
}
public byte get1() {
return pair[1];
}
@Override @Override
public int hashCode() { public int hashCode() {
return pair[0] + (pair[1] << 8); return pair[0] + (pair[1] << 8);

View File

@ -117,7 +117,7 @@ public class EditSessionWrapper {
return minY; return minY;
} }
public Extent getHistoryExtent(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer<?> player) { public FaweChangeSet wrapChangeSet(EditSession session, FaweLimit limit, Extent parent, FaweChangeSet set, FaweQueue queue, FawePlayer<?> player) {
return new HistoryExtent(session, limit, parent, set, queue); return set;
} }
} }

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object; package com.boydti.fawe.object;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.FaweQueue;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
@ -7,6 +8,7 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
public abstract class FaweChunk<T> { public abstract class FaweChunk<T> {
@ -24,12 +26,23 @@ public abstract class FaweChunk<T> {
this.z = z; this.z = z;
} }
/**
* Change the chunk's location<br>
* - E.g. if you are cloning a chunk and want to set multiple
* @param parent
* @param x
* @param z
*/
public void setLoc(FaweQueue parent, int x, int z) { public void setLoc(FaweQueue parent, int x, int z) {
this.parent = parent; this.parent = parent;
this.x = x; this.x = x;
this.z = z; this.z = z;
} }
/**
* Get the parent queue this chunk belongs to
* @return
*/
public FaweQueue getParent() { public FaweQueue getParent() {
return parent; return parent;
} }
@ -42,24 +55,65 @@ public abstract class FaweChunk<T> {
return z; return z;
} }
/**
* Get a unique hashcode for this chunk
* @return
*/
public long longHash() { public long longHash() {
return (long) x << 32 | z & 0xFFFFFFFFL; return (long) x << 32 | z & 0xFFFFFFFFL;
} }
/**
* Get a hashcode; unique below abs(x/z) < Short.MAX_VALUE
* @return
*/
@Override @Override
public int hashCode() { public int hashCode() {
return x << 16 | z & 0xFFFF; return x << 16 | z & 0xFFFF;
} }
/**
* Add the chunk to the queue
*/
public void addToQueue() { public void addToQueue() {
parent.setChunk(this); parent.setChunk(this);
} }
/**
* Fix the lighting in this chunk
*/
public void fixLighting() { public void fixLighting() {
parent.fixLighting(this, Settings.FIX_ALL_LIGHTING); parent.fixLighting(this, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL);
} }
public abstract char[][] getIdArrays(); /**
* This may return the raw value or constructed depending on the implementation<br>
* - The first index (i) is the layer (layer = y >> 4) (16 layers)<br>
* - The second array is length 4096 and contains the combined ids (cast to an int if you want)
*
* @see com.boydti.fawe.FaweCache#CACHE_I
* @see com.boydti.fawe.FaweCache#CACHE_J
* @see com.boydti.fawe.FaweCache#CACHE_X
* @see com.boydti.fawe.FaweCache#CACHE_Y
* @see com.boydti.fawe.FaweCache#CACHE_Z
*
* @return Combined id arrays
*/
public abstract char[][] getCombinedIdArrays();
/**
* Get the combined block id at a location<br>
* combined = (id <<<< 4) + data
* @param x
* @param y
* @param z
* @return The combined id
*/
public int getBlockCombinedId(int x, int y, int z) {
char[][] arrays = getCombinedIdArrays();
char[] array = arrays[y >> 4];
return array != null ? (array[FaweCache.CACHE_J[y][x][z]]) : 0;
}
/** /**
* Fill this chunk with a block * Fill this chunk with a block
@ -91,6 +145,10 @@ public abstract class FaweChunk<T> {
} }
} }
/**
* Add a task to run when this chunk is dispatched
* @param run
*/
public void addNotifyTask(Runnable run) { public void addNotifyTask(Runnable run) {
if (run != null) { if (run != null) {
tasks.add(run); tasks.add(run);
@ -108,22 +166,61 @@ public abstract class FaweChunk<T> {
tasks.clear(); tasks.clear();
} }
/**
* Get the underlying chunk object
* @return
*/
public abstract T getChunk(); public abstract T getChunk();
/**
* Set a tile entity at a location<br>
* - May throw an error if an invalid block is at the location
* @param x
* @param y
* @param z
* @param tile
*/
public abstract void setTile(int x, int y, int z, CompoundTag tile); public abstract void setTile(int x, int y, int z, CompoundTag tile);
public abstract void setEntity(CompoundTag entity); public abstract void setEntity(CompoundTag entity);
public abstract void setBlock(final int x, final int y, final int z, final int id, final byte data); public abstract void removeEntity(UUID uuid);
public abstract CompoundTag getTile(int x, int y, int z); public abstract void setBlock(final int x, final int y, final int z, final int id, final byte data);
public abstract Set<CompoundTag> getEntities(); public abstract Set<CompoundTag> getEntities();
/**
* Get the UUID of entities being removed
* @return
*/
public abstract Set<UUID> getEntityRemoves();
/**
* Get the map of location to tile entity<br>
* - The byte pair represents the location in the chunk<br>
* @see com.boydti.fawe.util.MathMan#unpair16x (get0) => x
* @see com.boydti.fawe.util.MathMan#unpair16y (get0) => z
* get1 => y
* @return
*/
public abstract Map<BytePair, CompoundTag> getTiles(); public abstract Map<BytePair, CompoundTag> getTiles();
/**
* Get the tile at a location
* @param x
* @param y
* @param z
* @return
*/
public abstract CompoundTag getTile(int x, int y, int z);
public abstract void setBiome(final int x, final int z, final BaseBiome biome); public abstract void setBiome(final int x, final int z, final BaseBiome biome);
/**
* Spend time now so that the chunk can be more efficiently dispatched later<br>
* - Modifications after this call will be ignored
*/
public void optimize() {} public void optimize() {}
@Override @Override

View File

@ -21,24 +21,29 @@ public abstract class FaweCommand<T> {
} }
public boolean executeSafe(final FawePlayer<T> player, final String... args) { public boolean executeSafe(final FawePlayer<T> player, final String... args) {
if (player == null || !safe) { try {
execute(player, args); if (player == null || !safe) {
return true; execute(player, args);
} else {
if (player.getMeta("fawe_action") != null) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(player);
return true; return true;
} } else {
player.setMeta("fawe_action", true); if (player.getMeta("fawe_action") != null) {
TaskManager.IMP.async(new Runnable() { BBC.WORLDEDIT_COMMAND_LIMIT.send(player);
@Override return true;
public void run() {
execute(player, args);
player.deleteMeta("fawe_action");
} }
}); player.setMeta("fawe_action", true);
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
execute(player, args);
player.deleteMeta("fawe_action");
}
});
}
return true;
} catch (Throwable e) {
e.printStackTrace();
} }
return true; return false;
} }
public abstract boolean execute(final FawePlayer<T> player, final String... args); public abstract boolean execute(final FawePlayer<T> player, final String... args);

View File

@ -173,7 +173,7 @@ public abstract class FawePlayer<T> {
FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index); FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index);
EditSession edit = set.toEditSession(getPlayer()); EditSession edit = set.toEditSession(getPlayer());
if (world.equals(getWorld())) { if (world.equals(getWorld())) {
session.remember(edit, false); session.remember(edit, false, false);
} else { } else {
return; return;
} }
@ -185,7 +185,16 @@ public abstract class FawePlayer<T> {
} }
/** /**
* Get the player's limit * Send a title
* @param head
* @param sub
*/
public abstract void sendTitle(String head, String sub);
public abstract void resetTitle();
/**
* Get the player's limit
* @return * @return
*/ */
public FaweLimit getLimit() { public FaweLimit getLimit() {
@ -370,4 +379,11 @@ public abstract class FawePlayer<T> {
WorldEdit.getInstance().removeSession(getPlayer()); WorldEdit.getInstance().removeSession(getPlayer());
Fawe.get().unregister(getName()); Fawe.get().unregister(getName());
} }
/**
* Get a new EditSession from this player
*/
public EditSession getNewEditSession() {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(getWorld(), -1, getPlayer());
}
} }

View File

@ -3,10 +3,15 @@ package com.boydti.fawe.object;
import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
public class NullChangeSet extends FaweChangeSet { public class NullChangeSet extends FaweChangeSet {
public NullChangeSet(World world) {
super(world);
}
@Override @Override
public boolean flush() { public boolean flush() {
return false; return false;

View File

@ -0,0 +1,4 @@
package com.boydti.fawe.object.brush;
public class AbstractDelegateBrush {
}

View File

@ -1,30 +1,23 @@
package com.boydti.fawe.object.brush; package com.boydti.fawe.object.brush;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.IntegerPair; import com.boydti.fawe.object.brush.heightmap.ScalableHeightMap;
import com.boydti.fawe.object.brush.heightmap.ArrayHeightMap;
import com.boydti.fawe.object.brush.heightmap.HeightMap;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.command.tool.BrushTool; import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.command.tool.brush.Brush; import com.sk89q.worldedit.command.tool.brush.Brush;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.Pattern;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import javax.imageio.ImageIO;
public class HeightBrush implements Brush { public class HeightBrush implements Brush {
public final HeightMap heightMap; public final ScalableHeightMap heightMap;
private final int rotation; private final int rotation;
double yscale = 1; double yscale = 1;
private final BrushTool tool; private final BrushTool tool;
@ -36,57 +29,13 @@ public class HeightBrush implements Brush {
if (file == null || !file.exists()) { if (file == null || !file.exists()) {
// Since I can't be bothered using separate args, we'll get it from the filename // Since I can't be bothered using separate args, we'll get it from the filename
if (file.getName().equalsIgnoreCase("#clipboard.png") && clipboard != null) { if (file.getName().equalsIgnoreCase("#clipboard.png") && clipboard != null) {
Vector dim = clipboard.getDimensions(); heightMap = ScalableHeightMap.fromClipboard(clipboard);
byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()];
int minX = clipboard.getMinimumPoint().getBlockX();
int minZ = clipboard.getMinimumPoint().getBlockZ();
int minY = clipboard.getMinimumPoint().getBlockY();
int maxY = clipboard.getMaximumPoint().getBlockY();
int clipHeight = maxY - minY + 1;
HashSet<IntegerPair> visited = new HashSet<>();
for (Vector pos : clipboard.getRegion()) {
IntegerPair pair = new IntegerPair((int) pos.x, (int) pos.z);
if (visited.contains(pair)) {
continue;
}
visited.add(pair);
int xx = pos.getBlockX();
int zz = pos.getBlockZ();
int highestY = 0;
for (int y = minY; y <= maxY; y++) {
pos.y = y;
BaseBlock block = clipboard.getBlock(pos);
if (block != EditSession.nullBlock) {
highestY = y + 1;
}
}
int pointHeight = Math.min(255, (256 * (highestY - minY)) / clipHeight);
int x = xx - minX;
int z = zz - minZ;
heightArray[x][z] = (byte) pointHeight;
}
heightMap = new ArrayHeightMap(heightArray);
} else { } else {
heightMap = new HeightMap(); heightMap = new ScalableHeightMap();
} }
} else { } else {
try { try {
BufferedImage heightFile = ImageIO.read(file); heightMap = ScalableHeightMap.fromPNG(file);
int width = heightFile.getWidth();
int length = heightFile.getHeight();
Raster data = heightFile.getData();
byte[][] array = new byte[width][length];
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
int pixel = heightFile.getRGB(x, z);
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = (pixel >> 0) & 0xFF;
int intensity = (red + green + blue) / 3;
array[x][z] = (byte) intensity;
}
}
heightMap = new ArrayHeightMap(array);
} catch (IOException e) { } catch (IOException e) {
throw new FaweException(BBC.BRUSH_HEIGHT_INVALID); throw new FaweException(BBC.BRUSH_HEIGHT_INVALID);
} }
@ -101,69 +50,6 @@ public class HeightBrush implements Brush {
} }
int size = (int) sizeDouble; int size = (int) sizeDouble;
heightMap.setSize(size); heightMap.setSize(size);
int size2 = size * size; heightMap.apply(editSession, mask, position, size, rotation, yscale, true);
int startY = position.getBlockY() + size;
int endY = position.getBlockY() - size;
int cx = position.getBlockX();
int cz = position.getBlockZ();
Vector mutablePos = new Vector(0, 0, 0);
for (int x = -size; x <= size; x++) {
int xx = cx + x;
mutablePos.x = xx;
for (int z = -size; z <= size; z++) {
int zz = cz + z;
int raise;
switch (rotation) {
default:
raise = heightMap.getHeight(x, z);
break;
case 1:
raise = heightMap.getHeight(z, x);
break;
case 2:
raise = heightMap.getHeight(-x, -z);
break;
case 3:
raise = heightMap.getHeight(-z, -x);
break;
}
raise = (int) (yscale * raise);
if (raise == 0) {
continue;
}
mutablePos.z = zz;
int foundHeight = Integer.MAX_VALUE;
BaseBlock block = null;
for (int y = startY; y >= endY; y--) {
block = editSession.getLazyBlock(xx, y, zz);
if (block != EditSession.nullBlock) {
if (mask != null) {
mutablePos.y = y;
if (!mask.test(mutablePos)) {
continue;
}
}
foundHeight = y;
break;
}
}
if (foundHeight == Integer.MAX_VALUE) {
continue;
}
if (raise > 0) {
for (int y = foundHeight + 1; y <= foundHeight + raise; y++) {
mutablePos.y = y;
editSession.setBlock(mutablePos, block);
}
} else {
for (int y = foundHeight; y > foundHeight + raise; y--) {
mutablePos.y = y;
editSession.setBlock(mutablePos, EditSession.nullBlock);
}
mutablePos.y = foundHeight + raise;
editSession.setBlock(mutablePos, block);
}
}
}
} }
} }

View File

@ -1,6 +1,6 @@
package com.boydti.fawe.object.brush.heightmap; package com.boydti.fawe.object.brush.heightmap;
public class ArrayHeightMap extends HeightMap { public class ArrayHeightMap extends ScalableHeightMap {
// The heights // The heights
private final byte[][] height; private final byte[][] height;
// The height map width/length // The height map width/length
@ -19,15 +19,15 @@ public class ArrayHeightMap extends HeightMap {
public void setSize(int size) { public void setSize(int size) {
super.setSize(size); super.setSize(size);
this.rx = (double) width / (size << 1); this.rx = (double) width / (size << 1);
this.rz = (double) width / (size << 1); this.rz = (double) length / (size << 1);
} }
@Override @Override
public int getHeight(int x, int z) { public double getHeight(int x, int z) {
x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx)); x = (int) Math.max(0, Math.min(width - 1, (x + size) * rx));
z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz)); z = (int) Math.max(0, Math.min(length - 1, (z + size) * rz));
return (((int) height[x][z] & 0xFF) * size) / 256; return ((height[x][z] & 0xFF) * size) / 256d;
} }
} }

View File

@ -1,31 +0,0 @@
package com.boydti.fawe.object.brush.heightmap;
import com.boydti.fawe.util.MathMan;
public class HeightMap {
public int size2;
public int size;
public HeightMap() {
setSize(5);
}
public HeightMap(int size) {
setSize(size);
}
public void setSize(int size) {
this.size = size;
this.size2 = size * size;
}
public int getHeight(int x, int z) {
int dx = Math.abs(x);
int dz = Math.abs(z);
int d2 = dx * dx + dz * dz;
if (d2 > size2) {
return 0;
}
return size - MathMan.sqrt(d2);
}
}

View File

@ -0,0 +1,152 @@
package com.boydti.fawe.object.brush.heightmap;
import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldVector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.internal.LocalWorldAdapter;
import com.sk89q.worldedit.math.convolution.GaussianKernel;
import com.sk89q.worldedit.math.convolution.HeightMap;
import com.sk89q.worldedit.math.convolution.HeightMapFilter;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import javax.imageio.ImageIO;
public class ScalableHeightMap {
public int size2;
public int size;
public ScalableHeightMap() {
setSize(5);
}
public ScalableHeightMap(int size) {
setSize(size);
}
public void setSize(int size) {
this.size = size;
this.size2 = size * size;
}
public double getHeight(int x, int z) {
int dx = Math.abs(x);
int dz = Math.abs(z);
int d2 = dx * dx + dz * dz;
if (d2 > size2) {
return 0;
}
return size - MathMan.sqrtApprox(d2);
}
public static ScalableHeightMap fromClipboard(Clipboard clipboard) {
Vector dim = clipboard.getDimensions();
byte[][] heightArray = new byte[dim.getBlockX()][dim.getBlockZ()];
int minX = clipboard.getMinimumPoint().getBlockX();
int minZ = clipboard.getMinimumPoint().getBlockZ();
int minY = clipboard.getMinimumPoint().getBlockY();
int maxY = clipboard.getMaximumPoint().getBlockY();
int clipHeight = maxY - minY + 1;
HashSet<IntegerPair> visited = new HashSet<>();
for (Vector pos : clipboard.getRegion()) {
IntegerPair pair = new IntegerPair((int) pos.x, (int) pos.z);
if (visited.contains(pair)) {
continue;
}
visited.add(pair);
int xx = pos.getBlockX();
int zz = pos.getBlockZ();
int highestY = minY;
for (int y = minY; y <= maxY; y++) {
pos.y = y;
BaseBlock block = clipboard.getBlock(pos);
if (block != EditSession.nullBlock) {
highestY = y + 1;
}
}
int pointHeight = Math.min(255, (256 * (highestY - minY)) / clipHeight);
int x = xx - minX;
int z = zz - minZ;
heightArray[x][z] = (byte) pointHeight;
}
return new ArrayHeightMap(heightArray);
}
public static ScalableHeightMap fromPNG(File file) throws IOException {
BufferedImage heightFile = ImageIO.read(file);
int width = heightFile.getWidth();
int length = heightFile.getHeight();
Raster data = heightFile.getData();
byte[][] array = new byte[width][length];
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
int pixel = heightFile.getRGB(x, z);
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = (pixel >> 0) & 0xFF;
int intensity = (red + green + blue) / 3;
array[x][z] = (byte) intensity;
}
}
return new ArrayHeightMap(array);
}
public void apply(EditSession session, Mask mask, Vector pos, int size, int rotationMode, double yscale, boolean smooth) throws MaxChangedBlocksException {
int diameter = 2 * size + 1;
int centerX = pos.getBlockX();
int centerZ = pos.getBlockZ();
int endY = pos.getBlockY() + size;
int startY = pos.getBlockY() - size;
int[] newData = new int[diameter * diameter];
Vector mutablePos = new Vector(0, 0, 0);
for (int x = -size; x <= size; x++) {
int xx = centerX + x;
mutablePos.x = xx;
for (int z = -size; z <= size; z++) {
int index = (z + size) * diameter + (x + size);
int zz = centerZ + z;
double raise;
switch (rotationMode) {
default:
raise = getHeight(x, z);
break;
case 1:
raise = getHeight(z, x);
break;
case 2:
raise = getHeight(-x, -z);
break;
case 3:
raise = getHeight(-z, -x);
break;
}
raise = (yscale * raise);
int random = PseudoRandom.random.random(256) < (int) ((raise - (int) raise) * 256) ? 1 : 0;
int height = session.getHighestTerrainBlock(xx, zz, 0, 255, true) + (int) raise + random;
newData[index] = height;
}
}
int iterations = 1;
WorldVector min = new WorldVector(LocalWorldAdapter.adapt(session.getWorld()), pos.subtract(size, 255, size));
Vector max = pos.add(size, 255, size);
Region region = new CuboidRegion(session.getWorld(), min, max);
HeightMap heightMap = new HeightMap(session, region, true);
if (smooth) {
HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1));
newData = filter.filter(newData, diameter, diameter);
// MainUtil.smoothArray(newData, diameter, 1, 4);
}
heightMap.apply(newData);
}
}

View File

@ -0,0 +1,44 @@
package com.boydti.fawe.object.change;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
public class MutableChunkChange implements Change {
public FaweChunk from;
public FaweChunk to;
public MutableChunkChange(FaweChunk from, FaweChunk to) {
this.from = from;
this.to = to;
}
@Override
public void undo(UndoContext context) throws WorldEditException {
create(context, true);
}
@Override
public void redo(UndoContext context) throws WorldEditException {
create(context, false);
}
public void create(UndoContext context, boolean undo) {
Extent extent = context.getExtent();
if (extent.getClass() == FastWorldEditExtent.class) {
FastWorldEditExtent fwee = (FastWorldEditExtent) extent;
if (undo) {
fwee.getQueue().setChunk(from);
} else {
fwee.getQueue().setChunk(to);
}
} else {
Fawe.debug("FAWE doesn't support: " + context + " for " + getClass());
}
}
}

View File

@ -4,6 +4,7 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.extent.FastWorldEditExtent; import com.boydti.fawe.object.extent.FastWorldEditExtent;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag; import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.Extent;
@ -11,6 +12,7 @@ import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.Change;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
public class MutableEntityChange implements Change { public class MutableEntityChange implements Change {
@ -26,6 +28,8 @@ public class MutableEntityChange implements Change {
public void undo(UndoContext context) throws WorldEditException { public void undo(UndoContext context) throws WorldEditException {
if (!create) { if (!create) {
create(context); create(context);
} else {
delete(context);
} }
} }
@ -33,6 +37,36 @@ public class MutableEntityChange implements Change {
public void redo(UndoContext context) throws WorldEditException { public void redo(UndoContext context) throws WorldEditException {
if (create) { if (create) {
create(context); create(context);
} else {
delete(context);
}
}
public void delete(UndoContext context) {
Extent extent = context.getExtent();
if (extent.getClass() == FastWorldEditExtent.class) {
FastWorldEditExtent fwee = (FastWorldEditExtent) extent;
Map<String, Tag> map = tag.getValue();
long most;
long least;
if (map.containsKey("UUIDMost")) {
most = ((LongTag) map.get("UUIDMost")).getValue();
least = ((LongTag) map.get("UUIDLeast")).getValue();
} else if (map.containsKey("PersistentIDMSB")) {
most = ((LongTag) map.get("PersistentIDMSB")).getValue();
least = ((LongTag) map.get("PersistentIDLSB")).getValue();
} else {
Fawe.debug("Skipping entity without uuid.");
return;
}
List<DoubleTag> pos = (List<DoubleTag>) map.get("Pos").getValue();
int x = (int) Math.round(pos.get(0).getValue());
int y = (int) Math.round(pos.get(1).getValue());
int z = (int) Math.round(pos.get(2).getValue());
UUID uuid = new UUID(most, least);
fwee.getQueue().removeEntity(x, y, z, uuid);
} else {
Fawe.debug("FAWE doesn't support: " + context + " for " + getClass() + " (bug Empire92)");
} }
} }

View File

@ -0,0 +1,72 @@
package com.boydti.fawe.object.changeset;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.object.change.MutableChunkChange;
import com.boydti.fawe.util.FaweQueue;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Iterator;
public class CPUOptimizedChangeSet extends FaweChangeSet {
public CPUOptimizedChangeSet(World world) {
super(world);
}
private ArrayList<Change> changes = new ArrayList<>();
public void addChangeTask(FaweQueue queue) {
queue.setChangeTask(new RunnableVal2<FaweChunk, FaweChunk>() {
@Override
public void run(final FaweChunk previous, final FaweChunk next) {
char[][] previousIds = previous.getCombinedIdArrays();
char[][] nextIds = next.getCombinedIdArrays();
for (int i = 0; i < nextIds.length; i++) {
if (nextIds[i] != null && previousIds[i] == null) {
previous.fillCuboid(0, 15, i << 4, (i << 4) + 15, 0, 15, 0, (byte) 0);
}
}
changes.add(new MutableChunkChange(previous, next));
}
});
}
@Override
public void add(int x, int y, int z, int combinedFrom, int combinedTo) {
throw new UnsupportedOperationException("Invalid mode");
}
@Override
public void addTileCreate(CompoundTag tag) {
throw new UnsupportedOperationException("Invalid mode");
}
@Override
public void addTileRemove(CompoundTag tag) {
throw new UnsupportedOperationException("Invalid mode");
}
@Override
public void addEntityRemove(CompoundTag tag) {
throw new UnsupportedOperationException("Invalid mode");
}
@Override
public void addEntityCreate(CompoundTag tag) {
throw new UnsupportedOperationException("Invalid mode");
}
@Override
public Iterator<Change> getIterator(boolean redo) {
return changes.iterator();
}
@Override
public int size() {
return changes.size() * 65536; // num chunks * 65536 (guess of 65536 changes per chunk)
}
}

View File

@ -16,8 +16,6 @@ import java.io.OutputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPOutputStream;
import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4InputStream; import net.jpountz.lz4.LZ4InputStream;
@ -56,20 +54,15 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
// NBT From // NBT From
private NBTOutputStream osNBTF; private NBTOutputStream osNBTF;
private GZIPOutputStream osNBTFG;
private AtomicInteger osNBTFI;
// NBT To // NBT To
private NBTOutputStream osNBTT; private NBTOutputStream osNBTT;
private GZIPOutputStream osNBTTG;
// Entity Create From // Entity Create From
private NBTOutputStream osENTCF; private NBTOutputStream osENTCF;
private GZIPOutputStream osENTCFG;
// Entity Create To // Entity Create To
private NBTOutputStream osENTCT; private NBTOutputStream osENTCT;
private GZIPOutputStream osENTCTG;
private World world; private World world;
@ -82,6 +75,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
public DiskStorageHistory(World world, UUID uuid) { public DiskStorageHistory(World world, UUID uuid) {
super(world);
String base = "history" + File.separator + world.getName() + File.separator + uuid; String base = "history" + File.separator + world.getName() + File.separator + uuid;
File folder = new File(Fawe.imp().getDirectory(), base); File folder = new File(Fawe.imp().getDirectory(), base);
int max = 0; int max = 0;
@ -100,6 +94,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
public DiskStorageHistory(World world, UUID uuid, int index) { public DiskStorageHistory(World world, UUID uuid, int index) {
super(world);
init(world, uuid, index); init(world, uuid, index);
} }
@ -125,27 +120,28 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
@Override @Override
public boolean flush() { public boolean flush() {
boolean flushed = false; super.flush();
boolean flushed = osBD != null || osNBTF != null || osNBTT != null && osENTCF != null || osENTCT != null;
try { try {
if (osBD != null) { if (osBD != null) {
flushed = true;
osBD.flush();
osBD.close(); osBD.close();
osBD = null; osBD = null;
} }
if (osNBTF != null) { if (osNBTF != null) {
flushed = true;
osNBTFG.flush();
osNBTF.close(); osNBTF.close();
osNBTF = null; osNBTF = null;
osNBTFG = null;
} }
if (osNBTT != null) { if (osNBTT != null) {
flushed = true;
osNBTTG.flush();
osNBTT.close(); osNBTT.close();
osNBTT = null; osNBTT = null;
osNBTTG = null; }
if (osENTCF != null) {
osENTCF.close();
osENTCF = null;
}
if (osENTCT != null) {
osENTCT.close();
osENTCT = null;
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -185,8 +181,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
enttFile.getParentFile().mkdirs(); enttFile.getParentFile().mkdirs();
enttFile.createNewFile(); enttFile.createNewFile();
osENTCTG = new GZIPOutputStream(new FileOutputStream(enttFile), true); osENTCT = new NBTOutputStream(getCompressedOS(new FileOutputStream(enttFile)));
osENTCT = new NBTOutputStream(osENTCTG);
return osENTCT; return osENTCT;
} }
@ -197,8 +192,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
entfFile.getParentFile().mkdirs(); entfFile.getParentFile().mkdirs();
entfFile.createNewFile(); entfFile.createNewFile();
osENTCFG = new GZIPOutputStream(new FileOutputStream(entfFile), true); osENTCF = new NBTOutputStream(getCompressedOS(new FileOutputStream(entfFile)));
osENTCF = new NBTOutputStream(osENTCFG);
return osENTCF; return osENTCF;
} }
@ -209,8 +203,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
nbttFile.getParentFile().mkdirs(); nbttFile.getParentFile().mkdirs();
nbttFile.createNewFile(); nbttFile.createNewFile();
osNBTTG = new GZIPOutputStream(new FileOutputStream(nbttFile), true); osNBTT = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbttFile)));
osNBTT = new NBTOutputStream(osNBTTG);
return osNBTT; return osNBTT;
} }
@ -221,9 +214,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
} }
nbtfFile.getParentFile().mkdirs(); nbtfFile.getParentFile().mkdirs();
nbtfFile.createNewFile(); nbtfFile.createNewFile();
osNBTFG = new GZIPOutputStream(new FileOutputStream(nbtfFile), true); osNBTF = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbtfFile)));
osNBTF = new NBTOutputStream(osNBTFG);
osNBTFI = new AtomicInteger();
return osNBTF; return osNBTF;
} }

View File

@ -7,6 +7,7 @@ import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RunnableVal2; import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
@ -20,12 +21,36 @@ import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.change.EntityCreate; import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove; import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.world.World;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class FaweChangeSet implements ChangeSet { public abstract class FaweChangeSet implements ChangeSet {
public abstract boolean flush();
private final World world;
public FaweChangeSet(World world) {
this.world = world;
}
public World getWorld() {
return world;
}
public boolean flush() {
try {
while (waiting.get() > 0) {
synchronized (lock) {
lock.wait(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
public abstract void add(int x, int y, int z, int combinedFrom, int combinedTo); public abstract void add(int x, int y, int z, int combinedFrom, int combinedTo);
@ -47,7 +72,7 @@ public abstract class FaweChangeSet implements ChangeSet {
public EditSession toEditSession(Player player) { public EditSession toEditSession(Player player) {
EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory(); EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory();
EditSession edit = factory.getEditSession(player.getWorld(), -1, null, player); EditSession edit = factory.getEditSession(world, -1, null, player);
edit.setChangeSet(this); edit.setChangeSet(this);
edit.dequeue(); edit.dequeue();
return edit; return edit;
@ -131,91 +156,111 @@ public abstract class FaweChangeSet implements ChangeSet {
} }
} }
private AtomicInteger waiting = new AtomicInteger(0);
private Object lock = new Object();
public void addChangeTask(FaweQueue queue) { public void addChangeTask(FaweQueue queue) {
queue.setChangeTask(new RunnableVal2<FaweChunk, FaweChunk>() { queue.setChangeTask(new RunnableVal2<FaweChunk, FaweChunk>() {
@Override @Override
public void run(final FaweChunk previous, FaweChunk next) { public void run(final FaweChunk previous, final FaweChunk next) {
/** waiting.incrementAndGet();
* TODO cache NBT TaskManager.IMP.async(new Runnable() {
* - Counter variable for nbt changes @Override
* - Record biome changes public void run() {
*/ try {
int cx = previous.getX(); int cx = previous.getX();
int cz = previous.getZ(); int cz = previous.getZ();
int bx = cx << 4; int bx = cx << 4;
int bz = cz << 4; int bz = cz << 4;
// Biome changes
// Biome changes {
{ // TODO
// TODO }
} // Block changes
{
// Block changes // Current blocks
{ char[][] currentIds = next.getCombinedIdArrays();
// Current blocks // Previous blocks in modified sections (i.e. we skip sections that weren't modified)
char[][] currentIds = next.getIdArrays(); char[][] previousIds = previous.getCombinedIdArrays();
// Previous blocks in modified sections (i.e. we skip sections that weren't modified) for (int layer = 0; layer < currentIds.length; layer++) {
char[][] previousIds = previous.getIdArrays(); char[] currentLayer = currentIds[layer];
for (int layer = 0; layer < currentIds.length; layer++) { char[] previousLayer = previousIds[layer];
char[] currentLayer = currentIds[layer]; if (currentLayer == null) {
char[] previousLayer = previousIds[layer]; continue;
if (currentLayer == null) { }
continue; int startY = layer << 4;
} for (int y = 0; y < 16; y++) {
int startY = layer << 4; short[][] i1 = FaweCache.CACHE_J[y];
for (int y = 0; y < 16; y++) { int yy = y + startY;
short[][] i1 = FaweCache.CACHE_J[y]; for (int x = 0; x < 16; x++) {
int yy = y + startY; int xx = x + bx;
for (int x = 0; x < 16; x++) { short[] i2 = i1[x];
int xx = x + bx; for (int z = 0; z < 16; z++) {
short[] i2 = i1[x]; int zz = z + bz;
for (int z = 0; z < 16; z++) { int index = i2[z];
int zz = z + bz; int combinedIdCurrent = currentLayer[index];
int index = i2[z]; switch (combinedIdCurrent) {
int combinedIdCurrent = currentLayer[index]; case 0:
switch (combinedIdCurrent) { continue;
case 0: case 1:
continue; combinedIdCurrent = 0;
case 1: default:
combinedIdCurrent = 0; char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0;
default: if (combinedIdCurrent != combinedIdPrevious) {
char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0; synchronized (lock) {
if (combinedIdCurrent != combinedIdPrevious) { add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent);
add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent); }
}
}
} }
}
} }
} }
} }
// Tile changes
{
// Tiles created
Map<BytePair, CompoundTag> tiles = next.getTiles();
for (Map.Entry<BytePair, CompoundTag> entry : tiles.entrySet()) {
synchronized (lock) {
addTileCreate(entry.getValue());
}
}
// Tiles removed
tiles = previous.getTiles();
for (Map.Entry<BytePair, CompoundTag> entry : tiles.entrySet()) {
synchronized (lock) {
addTileRemove(entry.getValue());
}
}
}
// Entity changes
{
// Entities created
Set<CompoundTag> entities = next.getEntities();
for (CompoundTag entityTag : entities) {
synchronized (lock) {
addEntityCreate(entityTag);
}
}
// Entities removed
entities = previous.getEntities();
for (CompoundTag entityTag : entities) {
synchronized (lock) {
addEntityRemove(entityTag);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
waiting.decrementAndGet();
synchronized (lock) {
lock.notifyAll();
}
} }
} }
} });
// Tile changes
{
// Tiles created
Map<BytePair, CompoundTag> tiles = next.getTiles();
for (Map.Entry<BytePair, CompoundTag> entry : tiles.entrySet()) {
addTileCreate(entry.getValue());
}
// Tiles removed
tiles = previous.getTiles();
for (Map.Entry<BytePair, CompoundTag> entry : tiles.entrySet()) {
addTileRemove(entry.getValue());
}
}
// Entity changes
{
// Entities created
Set<CompoundTag> entities = next.getEntities();
for (CompoundTag entityTag : entities) {
addEntityCreate(entityTag);
}
// Entities removed
entities = previous.getEntities();
for (CompoundTag entityTag : entities) {
addEntityRemove(entityTag);
}
}
} }
}); });
} }

View File

@ -4,11 +4,11 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.change.MutableBlockChange; import com.boydti.fawe.object.change.MutableBlockChange;
import com.boydti.fawe.object.change.MutableEntityChange; import com.boydti.fawe.object.change.MutableEntityChange;
import com.boydti.fawe.object.change.MutableTileChange; import com.boydti.fawe.object.change.MutableTileChange;
import com.google.common.collect.Iterators;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -21,9 +21,12 @@ import net.jpountz.lz4.LZ4OutputStream;
public abstract class FaweStreamChangeSet extends FaweChangeSet { public abstract class FaweStreamChangeSet extends FaweChangeSet {
public FaweStreamChangeSet(World world) {
super(world);
}
@Override @Override
public int size() { public int size() {
System.out.println("SIZE: " + blockSize);
// Flush so we can accurately get the size // Flush so we can accurately get the size
flush(); flush();
return blockSize; return blockSize;
@ -172,7 +175,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
if (read0 == -1) { if (read0 == -1) {
return null; return null;
} }
System.out.println("r0: " + read0);
int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX; int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX;
int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ; int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ;
int y = is.read() & 0xff; int y = is.read() & 0xff;
@ -192,7 +194,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
change.id = (short) ((from2 << 4) + (from1 >> 4)); change.id = (short) ((from2 << 4) + (from1 >> 4));
change.data = (byte) (from1 & 0xf); change.data = (byte) (from1 & 0xf);
} }
System.out.println("CHANGE: " + change.id);
return change; return change;
} catch (Exception ignoreEOF) { } catch (Exception ignoreEOF) {
ignoreEOF.printStackTrace(); ignoreEOF.printStackTrace();
@ -206,10 +207,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
last = read(); last = read();
} }
if (last != null) { if (last != null) {
System.out.println("HAS NEXT!");
return true; return true;
} }
System.out.println("NO NEXT");
try { try {
is.close(); is.close();
} catch (IOException e) { } catch (IOException e) {
@ -333,18 +332,43 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
} }
public Iterator<Change> getIterator(final boolean dir) { public Iterator<Change> getIterator(final boolean dir) {
System.out.println("GET ITERATOR: " + dir);
flush(); flush();
try { try {
Iterator<MutableTileChange> tileCreate = getTileIterator(getTileCreateIS(), true, dir); final Iterator<MutableTileChange> tileCreate = getTileIterator(getTileCreateIS(), true, dir);
Iterator<MutableTileChange> tileRemove = getTileIterator(getTileRemoveIS(), false, dir); final Iterator<MutableTileChange> tileRemove = getTileIterator(getTileRemoveIS(), false, dir);
Iterator<MutableEntityChange> entityCreate = getEntityIterator(getEntityCreateIS(), true, dir); final Iterator<MutableEntityChange> entityCreate = getEntityIterator(getEntityCreateIS(), true, dir);
Iterator<MutableEntityChange> entityRemove = getEntityIterator(getEntityRemoveIS(), false, dir); final Iterator<MutableEntityChange> entityRemove = getEntityIterator(getEntityRemoveIS(), false, dir);
Iterator<MutableBlockChange> blockChange = getBlockIterator(dir); final Iterator<MutableBlockChange> blockChange = getBlockIterator(dir);
return Iterators.concat(tileCreate, tileRemove, entityCreate, entityRemove, blockChange); return new Iterator<Change>() {
Iterator<Change>[] iterators = new Iterator[]{tileCreate, tileRemove, entityCreate, entityRemove, blockChange};
int i = 0;
Iterator<Change> current = iterators[0];
@Override
public boolean hasNext() {
if (current.hasNext()) {
return true;
} else if (i >= iterators.length - 1) {
return false;
} else {
current = iterators[++i];
}
return hasNext();
}
@Override
public void remove() {
current.remove();
}
@Override
public Change next() {
return current.next();
}
};
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -1,17 +1,14 @@
package com.boydti.fawe.object.changeset; package com.boydti.fawe.object.changeset;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.world.World;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4InputStream;
import net.jpountz.lz4.LZ4OutputStream;
/** /**
* ChangeSet optimized for low memory usage * ChangeSet optimized for low memory usage
@ -21,26 +18,67 @@ import net.jpountz.lz4.LZ4OutputStream;
*/ */
public class MemoryOptimizedHistory extends FaweStreamChangeSet { public class MemoryOptimizedHistory extends FaweStreamChangeSet {
private final Actor actor; private byte[] ids;
private ByteArrayOutputStream idsStream;
private OutputStream idsStreamZip;
public MemoryOptimizedHistory(Actor actor) { private byte[] entC;
this.actor = actor; private ByteArrayOutputStream entCStream;
private NBTOutputStream entCStreamZip;
private byte[] entR;
private ByteArrayOutputStream entRStream;
private NBTOutputStream entRStreamZip;
private byte[] tileC;
private ByteArrayOutputStream tileCStream;
private NBTOutputStream tileCStreamZip;
private byte[] tileR;
private ByteArrayOutputStream tileRStream;
private NBTOutputStream tileRStreamZip;
public MemoryOptimizedHistory(World world) {
super(world);
} }
@Override @Override
public boolean flush() { public boolean flush() {
if (idsStreamZip != null) { super.flush();
try { try {
idsStream.flush(); if (idsStream != null) {
idsStreamZip.flush();
idsStreamZip.close(); idsStreamZip.close();
ids = idsStream.toByteArray(true); ids = idsStream.toByteArray();
idsStream = null; idsStream = null;
idsStreamZip = null; idsStreamZip = null;
} catch (IOException e) { }
e.printStackTrace(); if (entCStream != null) {
entCStreamZip.close();
entC = entCStream.toByteArray();
entCStream = null;
entCStreamZip = null;
}
if (entRStream != null) {
entRStreamZip.close();
entR = entRStream.toByteArray();
entRStream = null;
entRStreamZip = null;
}
if (tileCStream != null) {
tileCStreamZip.close();
tileC = tileCStream.toByteArray();
tileCStream = null;
tileCStreamZip = null;
}
if (tileRStream != null) {
tileRStreamZip.close();
tileR = tileRStream.toByteArray();
tileRStream = null;
tileRStreamZip = null;
} }
return true; return true;
} catch (IOException e) {
e.printStackTrace();
} }
return false; return false;
} }
@ -50,75 +88,74 @@ public class MemoryOptimizedHistory extends FaweStreamChangeSet {
return ids == null ? 0 : ids.length; return ids == null ? 0 : ids.length;
} }
private byte[] ids;
private FastByteArrayOutputStream idsStream;
private OutputStream idsStreamZip;
@Override @Override
public OutputStream getBlockOS(int x, int y, int z) throws IOException { public OutputStream getBlockOS(int x, int y, int z) throws IOException {
if (idsStreamZip != null) { if (idsStreamZip != null) {
return idsStreamZip; return idsStreamZip;
} }
LZ4Factory factory = LZ4Factory.fastestInstance();
idsStream = new FastByteArrayOutputStream(Settings.BUFFER_SIZE);
idsStreamZip = new LZ4OutputStream(idsStream, Settings.BUFFER_SIZE, factory.fastCompressor());
if (Settings.COMPRESSION_LEVEL > 0) {
idsStreamZip = new LZ4OutputStream(idsStreamZip, Settings.BUFFER_SIZE, factory.highCompressor());
}
setOrigin(x, z); setOrigin(x, z);
return idsStreamZip; idsStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE);
} return idsStreamZip = getCompressedOS(idsStream);
@Override
public NBTOutputStream getEntityCreateOS() throws IOException {
return null;
}
@Override
public NBTOutputStream getEntityRemoveOS() throws IOException {
return null;
}
@Override
public NBTOutputStream getTileCreateOS() throws IOException {
return null;
}
@Override
public NBTOutputStream getTileRemoveOS() throws IOException {
return null;
} }
@Override @Override
public InputStream getBlockIS() { public InputStream getBlockIS() {
if (ids == null) { return ids == null ? null : getCompressedIS(new ByteArrayInputStream(ids));
return null; }
@Override
public NBTOutputStream getEntityCreateOS() throws IOException {
if (entCStreamZip != null) {
return entCStreamZip;
} }
InputStream is = new ByteArrayInputStream(ids); entCStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE);
is = new LZ4InputStream(is); return entCStreamZip = new NBTOutputStream(getCompressedOS(entCStream));
if (Settings.COMPRESSION_LEVEL > 0) { }
is = new LZ4InputStream(is);
@Override
public NBTOutputStream getEntityRemoveOS() throws IOException {
if (entRStreamZip != null) {
return entRStreamZip;
} }
return is; entRStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE);
return entRStreamZip = new NBTOutputStream(getCompressedOS(entRStream));
}
@Override
public NBTOutputStream getTileCreateOS() throws IOException {
if (tileCStreamZip != null) {
return tileCStreamZip;
}
tileCStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE);
return tileCStreamZip = new NBTOutputStream(getCompressedOS(tileCStream));
}
@Override
public NBTOutputStream getTileRemoveOS() throws IOException {
if (tileRStreamZip != null) {
return tileRStreamZip;
}
tileRStream = new ByteArrayOutputStream(Settings.BUFFER_SIZE);
return tileRStreamZip = new NBTOutputStream(getCompressedOS(tileRStream));
} }
@Override @Override
public NBTInputStream getEntityCreateIS() throws IOException { public NBTInputStream getEntityCreateIS() throws IOException {
return null; return entC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entC)));
} }
@Override @Override
public NBTInputStream getEntityRemoveIS() throws IOException { public NBTInputStream getEntityRemoveIS() throws IOException {
return null; return entR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(entR)));
} }
@Override @Override
public NBTInputStream getTileCreateIS() throws IOException { public NBTInputStream getTileCreateIS() throws IOException {
return null; return tileC == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileC)));
} }
@Override @Override
public NBTInputStream getTileRemoveIS() throws IOException { public NBTInputStream getTileRemoveIS() throws IOException {
return null; return tileR == null ? null : new NBTInputStream(getCompressedIS(new ByteArrayInputStream(tileR)));
} }
} }

View File

@ -154,6 +154,7 @@ public class FastWorldEditExtent extends AbstractDelegateExtent {
case 178: { case 178: {
if (block.hasNbtData()) { if (block.hasNbtData()) {
CompoundTag nbt = block.getNbtData(); CompoundTag nbt = block.getNbtData();
MainUtil.setPosition(nbt, x, y, z);
queue.setTile(x, y, z, nbt); queue.setTile(x, y, z, nbt);
} }
queue.setBlock(x, y, z, id, (byte) block.getData()); queue.setBlock(x, y, z, id, (byte) block.getData());

View File

@ -75,12 +75,12 @@ public class ProcessedWEExtent extends FaweRegionExtent {
} }
if (WEManager.IMP.maskContains(this.mask, (int) location.x, (int) location.z)) { if (WEManager.IMP.maskContains(this.mask, (int) location.x, (int) location.z)) {
if (limit.MAX_CHANGES-- < 0) { if (limit.MAX_CHANGES-- < 0) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_CHANGES);
return false; return false;
} }
return super.setBlock(location, block); return super.setBlock(location, block);
} else if (limit.MAX_FAILS-- < 0) { } else if (limit.MAX_FAILS-- < 0) {
WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_TILES); WEManager.IMP.cancelEdit(this, BBC.WORLDEDIT_CANCEL_REASON_MAX_FAILS);
} }
return false; return false;
} }

View File

@ -0,0 +1,76 @@
package com.boydti.fawe.object.progress;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import org.bukkit.Bukkit;
public class DefaultProgressTracker extends RunnableVal2<FaweQueue.ProgressType, Integer> {
private final FawePlayer player;
private final long start;
public DefaultProgressTracker(FawePlayer player) {
this.start = System.currentTimeMillis();
this.player = player;
}
private int amountQueue = 0;
private int amountDispatch = 0;
private long lastTick = 0;
@Override
public void run(FaweQueue.ProgressType type, Integer amount) {
switch (type) {
case DISPATCH:
amountDispatch = amount;
break;
case QUEUE:
amountQueue = amount;
break;
case DONE:
if (amountDispatch > 64) {
done();
}
return;
}
if (amountQueue > 64 || amountDispatch > 64) {
send();
}
}
private void done() {
TaskManager.IMP.task(new Runnable() {
@Override
public void run() {
final long time = System.currentTimeMillis() - start;
player.sendTitle("", BBC.PROGRESS_DONE.format(time / 1000d));
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
player.resetTitle();
}
}, 60);
}
});
}
public void send() {
TaskManager.IMP.task(new Runnable() {
@Override
public void run() {
long currentTick = Bukkit.getServer().getWorlds().get(0).getFullTime();
if (currentTick > lastTick + Settings.DISPLAY_PROGRESS_INTERVAL) {
lastTick = currentTick;
String queue = StringMan.padRight("" + amountQueue, 3);
String dispatch = StringMan.padRight("" + amountDispatch, 3);
player.sendTitle("", BBC.PROGRESS_MESSAGE.format(queue, dispatch));
}
}
});
}
}

View File

@ -10,14 +10,28 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
public abstract class FaweQueue { public abstract class FaweQueue {
public static enum ProgressType {
QUEUE,
DISPATCH,
DONE,
}
public enum RelightMode {
MINIMAL,
OPTIMAL,
ALL,
}
public final String world; public final String world;
public LinkedBlockingDeque<EditSession> sessions; public LinkedBlockingDeque<EditSession> sessions;
public long modified = System.currentTimeMillis(); public long modified = System.currentTimeMillis();
public RunnableVal2<FaweChunk, FaweChunk> changeTask; public RunnableVal2<FaweChunk, FaweChunk> changeTask;
public RunnableVal2<ProgressType, Integer> progressTask;
public FaweQueue(String world) { public FaweQueue(String world) {
this.world = world; this.world = world;
@ -33,6 +47,16 @@ public abstract class FaweQueue {
sessions.add(session); sessions.add(session);
} }
/**
* Add a progress task<br>
* - Progress type
* - Amount of type
* @param progressTask
*/
public void setProgressTracker(RunnableVal2<ProgressType, Integer> progressTask) {
this.progressTask = progressTask;
}
public Set<EditSession> getEditSessions() { public Set<EditSession> getEditSessions() {
return sessions == null ? new HashSet<EditSession>() : new HashSet<>(sessions); return sessions == null ? new HashSet<EditSession>() : new HashSet<>(sessions);
} }
@ -53,13 +77,15 @@ public abstract class FaweQueue {
public abstract void setEntity(int x, int y, int z, CompoundTag tag); public abstract void setEntity(int x, int y, int z, CompoundTag tag);
public abstract void removeEntity(int x, int y, int z, UUID uuid);
public abstract boolean setBiome(final int x, final int z, final BaseBiome biome); public abstract boolean setBiome(final int x, final int z, final BaseBiome biome);
public abstract FaweChunk<?> getChunk(int x, int z); public abstract FaweChunk<?> getChunk(int x, int z);
public abstract void setChunk(final FaweChunk<?> chunk); public abstract void setChunk(final FaweChunk<?> chunk);
public abstract boolean fixLighting(final FaweChunk<?> chunk, final boolean fixAll); public abstract boolean fixLighting(final FaweChunk<?> chunk, RelightMode mode);
public abstract boolean isChunkLoaded(final int x, final int z); public abstract boolean isChunkLoaded(final int x, final int z);

View File

@ -70,6 +70,9 @@ public class MainUtil {
try { try {
int elements = set.size(); int elements = set.size();
int compressedSize = set.getCompressedSize(); int compressedSize = set.getCompressedSize();
if (compressedSize == 0) {
return;
}
/* /*
* BlockVector * BlockVector
* - reference to the object --> 8 bytes * - reference to the object --> 8 bytes
@ -90,10 +93,9 @@ public class MainUtil {
* This compares FAWE's usage to standard WE. * This compares FAWE's usage to standard WE.
*/ */
int total = 128 * elements; int total = 128 * elements;
int current = compressedSize;
int ratio = total / current; int ratio = total / compressedSize;
int saved = total - current; int saved = total - compressedSize;
if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive()) { if (ratio > 3 && Thread.currentThread() != Fawe.get().getMainThread() && actor != null && actor.isPlayer() && actor.getSessionKey().isActive()) {
BBC.COMPRESSED.send(actor, saved, ratio); BBC.COMPRESSED.send(actor, saved, ratio);
@ -103,6 +105,31 @@ public class MainUtil {
} }
} }
public static void smoothArray(int[] data, int width, int radius, int weight) {
int[] copy = data.clone();
int length = data.length / width;
int diameter = 2 * radius + 1;
weight += diameter * diameter - 1;
for (int x = 0; x < width; x++) {
for (int y = 0; y < length; y++) {
int index = x + width * y;
int value = 0;
int count = 0;
for (int x2 = Math.max(0, x - radius); x2 <= Math.min(width - 1, x + radius); x2++) {
for (int y2 = Math.max(0, y - radius); y2 <= Math.min(length - 1, y + radius); y2++) {
count++;
int index2 = x2 + width * y2;
value += data[index2];
}
}
value += data[index] * (weight - count);
value = value / (weight);
data[index] = value;
}
}
}
public static void warnDeprecated(Class... alternatives) { public static void warnDeprecated(Class... alternatives) {
StackTraceElement[] stack = new RuntimeException().getStackTrace(); StackTraceElement[] stack = new RuntimeException().getStackTrace();
if (stack.length > 1) { if (stack.length > 1) {

View File

@ -89,11 +89,11 @@ public class SetQueue {
// Disable the async catcher as it can't discern async vs parallel // Disable the async catcher as it can't discern async vs parallel
SET_TASK.value2.startSet(true); SET_TASK.value2.startSet(true);
try { try {
if (Settings.UNSAFE_PARALLEL_THREADS <= 1) { if (Settings.PARALLEL_THREADS <= 1) {
SET_TASK.run(); SET_TASK.run();
} else { } else {
ArrayList<Thread> threads = new ArrayList<Thread>(); ArrayList<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < Settings.UNSAFE_PARALLEL_THREADS; i++) { for (int i = 0; i < Settings.PARALLEL_THREADS; i++) {
threads.add(new Thread(SET_TASK)); threads.add(new Thread(SET_TASK));
} }
for (Thread thread : threads) { for (Thread thread : threads) {
@ -197,7 +197,6 @@ public class SetQueue {
inactiveQueues.remove(queue); inactiveQueues.remove(queue);
} }
} }
return null;
} }
} }
if (Settings.QUEUE_SIZE != -1) { if (Settings.QUEUE_SIZE != -1) {

View File

@ -40,6 +40,14 @@ public class StringMan {
return count; return count;
} }
public static String padRight(String s, int n) {
return String.format("%1$-" + n + "s", s);
}
public static String padLeft(String s, int n) {
return String.format("%1$" + n + "s", s);
}
public static String getString(final Object obj) { public static String getString(final Object obj) {
if (obj == null) { if (obj == null) {
return "null"; return "null";

View File

@ -104,7 +104,6 @@ public abstract class TaskManager {
/** /**
* Break up a task and run it in fragments of 5ms.<br> * Break up a task and run it in fragments of 5ms.<br>
* - Each task will run on the main thread.<br> * - Each task will run on the main thread.<br>
* - Usualy wait time is around 25ms<br>
* @param objects - The list of objects to run the task for * @param objects - The list of objects to run the task for
* @param task - The task to run on each object * @param task - The task to run on each object
* @param whenDone - When the object task completes * @param whenDone - When the object task completes
@ -133,6 +132,7 @@ public abstract class TaskManager {
/** /**
* Quickly run a task on the main thread, and wait for execution to finish:<br> * Quickly run a task on the main thread, and wait for execution to finish:<br>
* - Useful if you need to access something from the Bukkit API from another thread<br> * - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usualy wait time is around 25ms<br>
* @param function * @param function
* @param <T> * @param <T>
* @return * @return
@ -144,6 +144,7 @@ public abstract class TaskManager {
/** /**
* Quickly run a task on the main thread, and wait for execution to finish:<br> * Quickly run a task on the main thread, and wait for execution to finish:<br>
* - Useful if you need to access something from the Bukkit API from another thread<br> * - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usualy wait time is around 25ms<br>
* @param function * @param function
* @param timeout - How long to wait for execution * @param timeout - How long to wait for execution
* @param <T> * @param <T>

View File

@ -5,6 +5,7 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweLocation; import com.boydti.fawe.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.regions.FaweMask; import com.boydti.fawe.regions.FaweMask;
@ -58,36 +59,50 @@ public class WEManager {
} }
} }
/**
* Get a player's mask
* @param player
* @return
*/
public HashSet<RegionWrapper> getMask(final FawePlayer<?> player) { public HashSet<RegionWrapper> getMask(final FawePlayer<?> player) {
final HashSet<RegionWrapper> regions = new HashSet<>(); return TaskManager.IMP.sync(new RunnableVal<HashSet<RegionWrapper>>() {
if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) { @Override
regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE)); public void run(HashSet<RegionWrapper> value) {
player.deleteMeta("lastmask"); final HashSet<RegionWrapper> regions = new HashSet<>();
return regions; if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) {
} regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE));
for (final FaweMaskManager manager : this.managers) { player.deleteMeta("lastmask");
if (player.hasPermission("fawe." + manager.getKey())) { this.value = regions;
final FaweMask mask = manager.getMask(player); return;
if (mask != null) {
regions.addAll(mask.getRegions());
} }
} for (final FaweMaskManager manager : managers) {
} if (player.hasPermission("fawe." + manager.getKey())) {
if (regions.size() == 0) { final FaweMask mask = manager.getMask(player);
HashSet<RegionWrapper> mask = player.<HashSet<RegionWrapper>>getMeta("lastmask"); if (mask != null) {
if (mask != null) { regions.addAll(mask.getRegions());
FaweLocation loc = player.getLocation(); }
for (RegionWrapper region : mask) {
if (region.isIn(loc.x, loc.z)) {
player.deleteMeta("lastmask");
return regions;
} }
} }
return mask; if (regions.size() == 0) {
HashSet<RegionWrapper> mask = player.<HashSet<RegionWrapper>>getMeta("lastmask");
if (mask != null) {
FaweLocation loc = player.getLocation();
for (RegionWrapper region : mask) {
if (region.isIn(loc.x, loc.z)) {
player.deleteMeta("lastmask");
this.value = regions;
return;
}
}
this.value = mask;
return;
}
}
player.setMeta("lastmask", regions);
this.value = regions;
return;
} }
} }, 1000);
player.setMeta("lastmask", regions);
return regions;
} }
public boolean intersects(final RegionWrapper region1, final RegionWrapper region2) { public boolean intersects(final RegionWrapper region1, final RegionWrapper region2) {

View File

@ -205,7 +205,7 @@ public class PlayerWrapper implements Player {
TaskManager.IMP.sync(new RunnableVal<Object>() { TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override @Override
public void run(Object value) { public void run(Object value) {
edit.queue.next(); edit.getQueue().next();
setPosition(new Vector(x + 0.5, y, z + 0.5)); setPosition(new Vector(x + 0.5, y, z + 0.5));
} }
}); });

View File

@ -229,6 +229,7 @@ public class WorldWrapper extends AbstractWorld {
@Override @Override
public boolean regenerate(final Region region, final EditSession session) { public boolean regenerate(final Region region, final EditSession session) {
final FaweQueue queue = session.getQueue(); final FaweQueue queue = session.getQueue();
queue.setChangeTask(null);
final FaweChangeSet fcs = (FaweChangeSet) session.getChangeSet(); final FaweChangeSet fcs = (FaweChangeSet) session.getChangeSet();
final FaweRegionExtent fe = session.getRegionExtent(); final FaweRegionExtent fe = session.getRegionExtent();
session.setChangeSet(fcs); session.setChangeSet(fcs);
@ -274,22 +275,25 @@ public class WorldWrapper extends AbstractWorld {
} }
} }
} else { } else {
Vector mutable = new Vector(0,0,0);
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
int xx = x + bx; int xx = x + bx;
mutable.x = xx;
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
int zz = z + bz; int zz = z + bz;
mutable.z = zz;
for (int y = 0; y < getMaxY() + 1; y++) { for (int y = 0; y < getMaxY() + 1; y++) {
final Vector loc = new Vector(xx, y, zz); mutable.y = y;
int from = queue.getCombinedId4Data(xx, y, zz); int from = queue.getCombinedId4Data(xx, y, zz);
boolean contains = (fe != null && fe.contains(xx, y, zz)) && region.contains(loc); boolean contains = (fe == null || fe.contains(xx, y, zz)) && region.contains(mutable);
if (contains) { if (contains) {
if (fcs != null) { if (fcs != null) {
if (!FaweCache.hasNBT(from >> 4)) { if (!FaweCache.hasNBT(from >> 4)) {
fcs.add(xx, y, zz, from, 0); fcs.add(xx, y, zz, from, 0);
} else { } else {
try { try {
BaseBlock block = getLazyBlock(loc); BaseBlock block = getLazyBlock(mutable);
fcs.add(loc, block, FaweCache.CACHE_BLOCK[0]); fcs.add(mutable, block, FaweCache.CACHE_BLOCK[0]);
} catch (Throwable e) { } catch (Throwable e) {
fcs.add(xx, y, zz, from, 0); fcs.add(xx, y, zz, from, 0);
} }
@ -298,30 +302,14 @@ public class WorldWrapper extends AbstractWorld {
} else { } else {
short id = (short) (from >> 4); short id = (short) (from >> 4);
byte data = (byte) (from & 0xf); byte data = (byte) (from & 0xf);
if (!FaweCache.hasNBT(id)) { queue.setBlock(xx, y, zz, id, data);
queue.setBlock(xx, y, zz, id, data); if (FaweCache.hasNBT(id)) {
} else { BaseBlock block = getBlock(new Vector(xx, y, zz));
try { if (block.hasNbtData()) {
final BaseBlock block = getBlock(loc); queue.setTile(xx, y, zz, block.getNbtData());
final Vector v = new Vector(loc.x, loc.y, loc.z);
queue.addNotifyTask(cx, cz, new Runnable() {
@Override
public void run() {
try {
setBlock(v, block, false);
} catch (WorldEditException e) {
e.printStackTrace();
}
}
});
} catch (Throwable e) {
e.printStackTrace();
queue.setBlock(xx, y, zz, id, data);
} }
} }
} }
} }
} }
} }

View File

@ -26,9 +26,11 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.EditSessionWrapper; import com.boydti.fawe.object.EditSessionWrapper;
import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.HistoryExtent;
import com.boydti.fawe.object.NullChangeSet; import com.boydti.fawe.object.NullChangeSet;
import com.boydti.fawe.object.RegionWrapper; import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.CPUOptimizedChangeSet;
import com.boydti.fawe.object.changeset.DiskStorageHistory; import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet; import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.changeset.MemoryOptimizedHistory; import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
@ -38,6 +40,7 @@ import com.boydti.fawe.object.extent.FaweRegionExtent;
import com.boydti.fawe.object.extent.MemoryCheckingExtent; import com.boydti.fawe.object.extent.MemoryCheckingExtent;
import com.boydti.fawe.object.extent.NullExtent; import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.object.extent.ProcessedWEExtent; import com.boydti.fawe.object.extent.ProcessedWEExtent;
import com.boydti.fawe.object.progress.DefaultProgressTracker;
import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.Perm; import com.boydti.fawe.util.Perm;
@ -158,21 +161,21 @@ public class EditSession implements Extent {
BEFORE_HISTORY, BEFORE_REORDER, BEFORE_CHANGE BEFORE_HISTORY, BEFORE_REORDER, BEFORE_CHANGE
} }
public World world; private World world;
public Actor actor; private Actor actor;
public FaweChangeSet changeSet; private FaweChangeSet changeSet;
public EditSessionWrapper wrapper; private EditSessionWrapper wrapper;
public MaskingExtent maskingExtent; private MaskingExtent maskingExtent;
public FaweRegionExtent regionExtent; private FaweRegionExtent regionExtent;
public Extent primaryExtent; private Extent primaryExtent;
public Extent bypassReorderHistory; private Extent bypassReorderHistory;
public Extent bypassHistory; private Extent bypassHistory;
public Extent bypassNone; private Extent bypassNone;
public SurvivalModeExtent lazySurvivalExtent; private SurvivalModeExtent lazySurvivalExtent;
public boolean fastmode; private boolean fastmode;
public Mask oldMask; private Mask oldMask;
public FaweLimit limit = FaweLimit.MAX.copy(); private FaweLimit limit = FaweLimit.MAX.copy();
public FaweQueue queue; private FaweQueue queue;
public static BaseBiome nullBiome = new BaseBiome(0); public static BaseBiome nullBiome = new BaseBiome(0);
public static BaseBlock nullBlock = FaweCache.CACHE_BLOCK[0]; public static BaseBlock nullBlock = FaweCache.CACHE_BLOCK[0];
@ -229,13 +232,13 @@ public class EditSession implements Extent {
this.bypassReorderHistory = extent; this.bypassReorderHistory = extent;
this.bypassHistory = extent; this.bypassHistory = extent;
this.bypassNone = extent; this.bypassNone = extent;
this.changeSet = new NullChangeSet(); this.changeSet = new NullChangeSet(world);
this.wrapper = Fawe.imp().getEditSessionWrapper(this); this.wrapper = Fawe.imp().getEditSessionWrapper(this);
return; return;
} }
// Wrap the world // Wrap the world
this.world = (world = new WorldWrapper((AbstractWorld) world)); this.world = (world instanceof WorldWrapper) ? world : (world = new WorldWrapper((AbstractWorld) world));
// Delegate some methods to an implementation specific class // Delegate some methods to an implementation specific class
this.wrapper = Fawe.imp().getEditSessionWrapper(this); this.wrapper = Fawe.imp().getEditSessionWrapper(this);
@ -252,7 +255,7 @@ public class EditSession implements Extent {
this.bypassReorderHistory = extent; this.bypassReorderHistory = extent;
this.bypassHistory = extent; this.bypassHistory = extent;
this.bypassNone = extent; this.bypassNone = extent;
this.changeSet = new NullChangeSet(); this.changeSet = new NullChangeSet(world);
return; return;
} }
@ -261,8 +264,10 @@ public class EditSession implements Extent {
final FawePlayer fp = FawePlayer.wrap(actor); final FawePlayer fp = FawePlayer.wrap(actor);
final LocalSession session = fp.getSession(); final LocalSession session = fp.getSession();
this.fastmode = session.hasFastMode(); this.fastmode = session.hasFastMode();
if (fp.hasWorldEditBypass()) { boolean bypass = fp.hasWorldEditBypass();
this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), true, true); this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), bypass, true);
queue.setProgressTracker(new DefaultProgressTracker(fp));
if (bypass) {
queue.addEditSession(this); queue.addEditSession(this);
// Bypass skips processing and area restrictions // Bypass skips processing and area restrictions
extent = primaryExtent = new FastWorldEditExtent(world, queue); extent = primaryExtent = new FastWorldEditExtent(world, queue);
@ -278,7 +283,6 @@ public class EditSession implements Extent {
} }
mask = null; mask = null;
} else { } else {
this.queue = SetQueue.IMP.getNewQueue(Fawe.imp().getWorldName(world), false, true);
queue.addEditSession(this); queue.addEditSession(this);
this.limit = fp.getLimit(); this.limit = fp.getLimit();
mask = WEManager.IMP.getMask(fp); mask = WEManager.IMP.getMask(fp);
@ -326,13 +330,13 @@ public class EditSession implements Extent {
extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER); extent = this.wrapExtent(extent, eventBus, event, Stage.BEFORE_REORDER);
// History // History
this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : new MemoryOptimizedHistory(actor); this.changeSet = Settings.STORE_HISTORY_ON_DISK ? new DiskStorageHistory(world, actor.getUniqueId()) : (Settings.COMBINE_HISTORY_STAGE && Settings.COMPRESSION_LEVEL == 0) ? new CPUOptimizedChangeSet(world) : new MemoryOptimizedHistory(world);
this.changeSet = this.wrapper.wrapChangeSet(this, limit, extent, this.changeSet, queue, fp);
if (Settings.COMBINE_HISTORY_STAGE) { if (Settings.COMBINE_HISTORY_STAGE) {
changeSet.addChangeTask(queue); changeSet.addChangeTask(queue);
} else { } else {
extent = this.wrapper.getHistoryExtent(this, limit, extent, this.changeSet, queue, fp); extent = new HistoryExtent(this, limit, extent, changeSet, queue);
} }
// Region restrictions if mask is not null // Region restrictions if mask is not null
if (mask != null) { if (mask != null) {
extent = this.regionExtent = new ProcessedWEExtent(extent, mask, limit); extent = this.regionExtent = new ProcessedWEExtent(extent, mask, limit);
@ -367,6 +371,15 @@ public class EditSession implements Extent {
return regionExtent; return regionExtent;
} }
/**
* Get the actor
* @return
*/
@Nullable
public Actor getActor() {
return actor;
}
public boolean cancel() { public boolean cancel() {
// Cancel this // Cancel this
if (primaryExtent != null && queue != null) { if (primaryExtent != null && queue != null) {
@ -388,6 +401,12 @@ public class EditSession implements Extent {
} }
} }
public void addNotifyTask(Runnable whenDone) {
if (queue != null) {
queue.addNotifyTask(whenDone);
}
}
public FastWorldEditExtent getPrimaryExtent() { public FastWorldEditExtent getPrimaryExtent() {
return (FastWorldEditExtent) primaryExtent; return (FastWorldEditExtent) primaryExtent;
} }

View File

@ -26,6 +26,7 @@ import com.boydti.fawe.object.clipboard.DiskOptimizedClipboard;
import com.boydti.fawe.util.FaweQueue; import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.SetQueue; import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jchronic.Chronic; import com.sk89q.jchronic.Chronic;
import com.sk89q.jchronic.Options; import com.sk89q.jchronic.Options;
import com.sk89q.jchronic.utils.Span; import com.sk89q.jchronic.utils.Span;
@ -199,10 +200,16 @@ public class LocalSession {
* @param editSession the edit session * @param editSession the edit session
*/ */
public void remember(EditSession editSession) { public void remember(EditSession editSession) {
remember(editSession, true); remember(editSession, true, false);
} }
public void remember(final EditSession editSession, final boolean append) { public void remember(final EditSession editSession, final boolean append, final boolean sendMessage) {
if (editSession == null) {
return;
}
if (Settings.STORE_HISTORY_ON_DISK) {
MAX_HISTORY_SIZE = Integer.MAX_VALUE;
}
// Enqueue it // Enqueue it
if (editSession.getQueue() != null) { if (editSession.getQueue() != null) {
FaweQueue queue = editSession.getQueue(); FaweQueue queue = editSession.getQueue();
@ -215,8 +222,10 @@ public class LocalSession {
if (editSession.size() == 0 || editSession.hasFastMode()) return; if (editSession.size() == 0 || editSession.hasFastMode()) return;
// Destroy any sessions after this undo point // Destroy any sessions after this undo point
while (historyPointer < history.size()) { if (append) {
history.remove(historyPointer); while (historyPointer < history.size()) {
history.remove(historyPointer);
}
} }
ChangeSet set = editSession.getChangeSet(); ChangeSet set = editSession.getChangeSet();
if (set instanceof FaweStreamChangeSet) { if (set instanceof FaweStreamChangeSet) {
@ -225,14 +234,19 @@ public class LocalSession {
editSession.getQueue().addNotifyTask(new Runnable() { editSession.getQueue().addNotifyTask(new Runnable() {
@Override @Override
public void run() { public void run() {
if (fcs.flush() && append) { TaskManager.IMP.async(new Runnable() {
MainUtil.sendCompressedMessage(fcs, editSession.actor); @Override
} public void run() {
if (fcs.flush() && append && sendMessage) {
MainUtil.sendCompressedMessage(fcs, editSession.getActor());
}
}
});
} }
}); });
} else { } else {
if (fcs.flush() && append) { if (fcs.flush() && append && sendMessage) {
MainUtil.sendCompressedMessage(fcs, editSession.actor); MainUtil.sendCompressedMessage(fcs, editSession.getActor());
} }
} }
@ -241,13 +255,15 @@ public class LocalSession {
} }
if (append) { if (append) {
history.add(editSession); history.add(editSession);
historyPointer = history.size();
} else { } else {
history.add(0, editSession); history.add(0, editSession);
historyPointer++;
} }
while (history.size() > MAX_HISTORY_SIZE) { while (history.size() > MAX_HISTORY_SIZE) {
history.remove(0); history.remove(0);
historyPointer--;
} }
historyPointer = append ? history.size() : historyPointer + 1;
} }
/** /**
@ -278,7 +294,6 @@ public class LocalSession {
newEditSession.enableQueue(); newEditSession.enableQueue();
newEditSession.setFastMode(fastMode); newEditSession.setFastMode(fastMode);
editSession.undo(newEditSession); editSession.undo(newEditSession);
System.out.println("UNDO: " + historyPointer + " | " + history.size());
return editSession; return editSession;
} else { } else {
historyPointer = 0; historyPointer = 0;
@ -306,7 +321,6 @@ public class LocalSession {
*/ */
public EditSession redo(@Nullable BlockBag newBlockBag, Player player) { public EditSession redo(@Nullable BlockBag newBlockBag, Player player) {
checkNotNull(player); checkNotNull(player);
System.out.println("CHECK REDO: " + historyPointer + " | " + history.size());
if (historyPointer < history.size()) { if (historyPointer < history.size()) {
EditSession editSession = history.get(historyPointer); EditSession editSession = history.get(historyPointer);
EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory() EditSession newEditSession = WorldEdit.getInstance().getEditSessionFactory()
@ -315,10 +329,7 @@ public class LocalSession {
newEditSession.setFastMode(fastMode); newEditSession.setFastMode(fastMode);
editSession.redo(newEditSession); editSession.redo(newEditSession);
++historyPointer; ++historyPointer;
System.out.println("CAN REDO");
return editSession; return editSession;
} else {
System.out.println("POINTER");
} }
return null; return null;

View File

@ -155,7 +155,7 @@ public class SelectionCommand extends SimpleCommand<Operation> {
} }
}); });
queue.enqueue(); queue.enqueue();
editSession.setChangeSet(new NullChangeSet()); editSession.setChangeSet(new NullChangeSet(null));
BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea())); BBC.OPERATION.send(actor, BBC.VISITOR_BLOCK.format(cuboid.getArea()));
return null; return null;
} }

View File

@ -22,7 +22,6 @@ package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.config.BBC; import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer; import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.exception.FaweException; import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.wrappers.PlayerWrapper; import com.boydti.fawe.wrappers.PlayerWrapper;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@ -224,7 +223,6 @@ public final class CommandManager {
TaskManager.IMP.async(new Runnable() { TaskManager.IMP.async(new Runnable() {
@Override @Override
public void run() { public void run() {
System.out.println("COMMAND START!");
final Actor actor = platformManager.createProxyActor(event.getActor()); final Actor actor = platformManager.createProxyActor(event.getActor());
String[] split = commandDetection(event.getArguments().split(" ")); String[] split = commandDetection(event.getArguments().split(" "));
@ -233,11 +231,11 @@ public final class CommandManager {
return; return;
} }
LocalSession session = worldEdit.getSessionManager().get(actor); final LocalSession session = worldEdit.getSessionManager().get(actor);
LocalConfiguration config = worldEdit.getConfiguration(); LocalConfiguration config = worldEdit.getConfiguration();
CommandLocals locals = new CommandLocals(); CommandLocals locals = new CommandLocals();
FawePlayer fp = FawePlayer.wrap(actor); final FawePlayer fp = FawePlayer.wrap(actor);
if (fp != null) { if (fp != null) {
if (fp.getMeta("fawe_action") != null) { if (fp.getMeta("fawe_action") != null) {
BBC.WORLDEDIT_COMMAND_LIMIT.send(fp); BBC.WORLDEDIT_COMMAND_LIMIT.send(fp);
@ -285,36 +283,33 @@ public final class CommandManager {
log.log(Level.SEVERE, "An unknown error occurred", e); log.log(Level.SEVERE, "An unknown error occurred", e);
} }
} finally { } finally {
System.out.println("DONE!"); final EditSession editSession = locals.get(EditSession.class);
if (fp != null) { boolean delayed = false;
fp.deleteMeta("fawe_action");
}
EditSession editSession = locals.get(EditSession.class);
if (editSession != null) { if (editSession != null) {
session.remember(editSession);
editSession.flushQueue(); editSession.flushQueue();
final long time = System.currentTimeMillis() - start; worldEdit.flushBlockBag(actor, editSession);
if (time > 5 && editSession.size() != 0) { }
SetQueue.IMP.addTask(new Runnable() { if (fp != null) {
if (editSession != null && editSession.size() > 0 && editSession.getQueue() != null) {
delayed = true;
editSession.getQueue().addNotifyTask(new Runnable() {
@Override @Override
public void run() { public void run() {
session.remember(editSession, true, true);
fp.deleteMeta("fawe_action");
final long time = System.currentTimeMillis() - start; final long time = System.currentTimeMillis() - start;
BBC.ACTION_COMPLETE.send(actor, (time / 1000d)); if (time > 5) {
BBC.ACTION_COMPLETE.send(actor, (time / 1000d));
}
} }
}); });
} }
if (config.profile) { }
int changed = editSession.getBlockChangeCount(); if (!delayed) {
if (time > 0) { if (fp != null) {
double throughput = changed / (time / 1000.0); fp.deleteMeta("fawe_action");
actor.printDebug((time / 1000.0) + "s elapsed (history: " + changed + " changed; " + Math.round(throughput) + " blocks/sec).");
} else {
actor.printDebug((time / 1000.0) + "s elapsed.");
}
} }
session.remember(editSession, true, true);
worldEdit.flushBlockBag(actor, editSession);
} }
} }
} }

View File

@ -29,6 +29,13 @@
</includes> </includes>
<directory>bukkit18/src/main/resources/</directory> <directory>bukkit18/src/main/resources/</directory>
</resource> </resource>
<resource>
<filtering>false</filtering>
<includes>
<include>**/*.*</include>
</includes>
<directory>core/src/main/resources/</directory>
</resource>
</resources> </resources>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -47,11 +47,11 @@ public class SpongeCommand implements CommandCallable {
@Override @Override
public Optional<? extends Text> getHelp(final CommandSource cmd) { public Optional<? extends Text> getHelp(final CommandSource cmd) {
return Optional.of(Text.of("/<fixlighting|stream|wea|wrg>")); return Optional.of(Text.of("/<fixlighting|stream|wea|select>"));
} }
@Override @Override
public Text getUsage(final CommandSource cmd) { public Text getUsage(final CommandSource cmd) {
return Text.of("/<fixlighting|stream|wea|wrg>"); return Text.of("/<fixlighting|stream|wea|select>");
} }
} }

View File

@ -130,7 +130,7 @@ public class SpongeQueue_ALL extends NMSMappedFaweQueue<World, net.minecraft.wor
net.minecraft.world.chunk.Chunk nmsChunk = fs.getChunk(); net.minecraft.world.chunk.Chunk nmsChunk = fs.getChunk();
Chunk spongeChunk = (Chunk) nmsChunk; Chunk spongeChunk = (Chunk) nmsChunk;
char[][] ids = ((SpongeChunk_1_8) fc).getIdArrays(); char[][] ids = ((SpongeChunk_1_8) fc).getCombinedIdArrays();
MutableBlockVolumeWorker<? extends Chunk> blockWorker = spongeChunk.getBlockWorker(); MutableBlockVolumeWorker<? extends Chunk> blockWorker = spongeChunk.getBlockWorker();
blockWorker.map(new BlockVolumeMapper() { blockWorker.map(new BlockVolumeMapper() {
@Override @Override