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.object.FaweLocation;
import com.boydti.fawe.object.FawePlayer;
import java.lang.reflect.Method;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
public class BukkitPlayer extends FawePlayer<Player> {
private static ConsoleCommandSender console;
public BukkitPlayer(final Player 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
public void sendMessage(final String 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("FAWE couldn't find a fast block placer");
debug("Bukkit version: " + Bukkit.getVersion());
debug("Supported NMS versions: 1.8, 1.9");
debug("Supported NMS versions: 1.8.8, 1.9.4");
debug("Fallback placer: " + BukkitQueue_All.class);
debug("=======================================");
hasNMS = false;

View File

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

View File

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

View File

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

View File

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

View File

@ -21,16 +21,19 @@ import com.sk89q.worldedit.internal.Constants;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_8_R3.ChunkSection;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EntityTypes;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.NBTTagInt;
import net.minecraft.server.v1_8_R3.NibbleArray;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import net.minecraft.server.v1_8_R3.PlayerConnection;
import net.minecraft.server.v1_8_R3.TileEntity;
@ -95,6 +98,71 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
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
public boolean setComponents(FaweChunk fc, RunnableVal<FaweChunk> changeTask) {
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();
Collection<net.minecraft.server.v1_8_R3.Entity>[] entities = nmsChunk.getEntitySlices();
// Run change task if applicable
if (changeTask != null) {
CharFaweChunk previous = (CharFaweChunk) getChunk(fc.getX(), fc.getZ());
char[][] idPrevious = new char[16][];
for (int layer = 0; layer < sections.length; layer++) {
if (fs.getCount(layer) != 0) {
ChunkSection section = sections[layer];
if (section != null) {
idPrevious[layer] = section.getIdArray().clone();
// Remove entities
for (int i = 0; i < 16; i++) {
int count = fs.getCount(i);
if (count == 0) {
continue;
} else if (count >= 4096) {
entities[i].clear();
} else {
char[] array = fs.getIdArray(i);
Collection<Entity> ents = new ArrayList<>(entities[i]);
for (Entity entity : ents) {
if (entity instanceof EntityPlayer) {
continue;
}
}
}
previous.ids = idPrevious;
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);
int x = ((int) Math.round(entity.locX) & 15);
int z = ((int) Math.round(entity.locZ) & 15);
int y = (int) Math.round(entity.locY);
if (array == null) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
String id = EntityTypes.b(ent);
if (id != null) {
NBTTagCompound tag = new NBTTagCompound();
ent.e(tag);
CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag);
Map<String, Tag> map = ReflectionUtils.getMap(nativeTag.getValue());
map.put("Id", new StringTag(id));
previous.setEntity(nativeTag);
}
nmsWorld.removeEntity(entity);
}
}
}
}
// 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);
}
// Trim tiles
Set<Map.Entry<BlockPosition, TileEntity>> entryset = tiles.entrySet();
Iterator<Map.Entry<BlockPosition, TileEntity>> iterator = entryset.iterator();
@ -175,31 +259,17 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
iterator.remove();
}
}
// Remove entities
for (int i = 0; i < 16; i++) {
int count = fs.getCount(i);
if (count == 0) {
continue;
} else if (count >= 4096) {
entities[i].clear();
} else {
char[] array = fs.getIdArray(i);
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) {
int x = ((int) Math.round(entity.locX) & 15);
int z = ((int) Math.round(entity.locZ) & 15);
int y = (int) Math.round(entity.locY);
if (array == null) {
continue;
}
int j = FaweCache.CACHE_J[y][x][z];
if (array[j] != 0) {
if (entsToRemove.contains(entity.getUniqueID())) {
nmsWorld.removeEntity(entity);
}
}
}
}
// Set blocks
for (int j = 0; j < sections.length; j++) {
if (fs.getCount(j) == 0) {
@ -256,53 +326,19 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
}
// Set tiles
Map<BytePair, CompoundTag> tilesToSpawn = fs.getTiles();
BlockPosition.MutableBlockPosition mutablePos = new BlockPosition.MutableBlockPosition(0, 0, 0);
int bx = fs.getX() << 4;
int bz = fs.getZ() << 4;
for (Map.Entry<BytePair, CompoundTag> entry : tilesToSpawn.entrySet()) {
CompoundTag nativeTag = entry.getValue();
BytePair pair = entry.getKey();
mutablePos.c(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos
TileEntity tileEntity = nmsWorld.getTileEntity(mutablePos);
BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos
TileEntity tileEntity = nmsWorld.getTileEntity(pos);
if (tileEntity != null) {
NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag);
tag.set("x", new NBTTagInt(mutablePos.getX()));
tag.set("y", new NBTTagInt(mutablePos.getY()));
tag.set("z", new NBTTagInt(mutablePos.getZ()));
tileEntity.a(tag); // ReadTagIntoTile
}
}
// Set entities
Set<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) {
e.printStackTrace();
}
@ -339,8 +375,8 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
FaweLocation loc = fp.getLocation();
int px = loc.x >> 4;
int pz = loc.z >> 4;
int dx = Math.abs(cx - (loc.x >> 4));
int dz = Math.abs(cz - (loc.z >> 4));
int dx = Math.abs(cx - (px));
int dz = Math.abs(cz - (pz));
if ((dx > view) || (dz > view)) {
continue;
}
@ -350,7 +386,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
}
@Override
public boolean fixLighting(FaweChunk chunk, boolean fixAll) {
public boolean fixLighting(FaweChunk chunk, RelightMode mode) {
try {
CharFaweChunk<Chunk> fc = (CharFaweChunk<Chunk>) chunk;
CraftChunk craftChunk = (CraftChunk) fc.getChunk();
@ -358,11 +394,22 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
if (!craftChunk.isLoaded()) {
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();
if (fc.getTotalRelight() == 0 && !fixAll) {
if (fc.getTotalRelight() == 0 && mode == RelightMode.MINIMAL) {
return true;
}
ChunkSection[] sections = nmsChunk.getSections();
net.minecraft.server.v1_8_R3.World nmsWorld = nmsChunk.getWorld();
int X = fc.getX() << 4;
@ -374,7 +421,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
if (section == null) {
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;
}
char[] array = section.getIdArray();
@ -387,7 +434,7 @@ public class BukkitQueue18R3 extends BukkitQueue_0<Chunk, ChunkSection[], char[]
short id = (short) (i >> 4);
switch (id) { // Lighting
default:
if (!fixAll) {
if (mode == RelightMode.MINIMAL) {
continue;
}
if ((k & 1) == l) {

View File

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

View File

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

View File

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

View File

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

View File

@ -4,44 +4,50 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.bukkit.v0.BukkitQueue_0;
import com.boydti.fawe.example.CharFaweChunk;
import com.boydti.fawe.object.BytePair;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.IntegerPair;
import com.boydti.fawe.object.PseudoRandom;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.MemUtil;
import com.sk89q.worldedit.LocalSession;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.internal.Constants;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.minecraft.server.v1_9_R1.Block;
import net.minecraft.server.v1_9_R1.BlockPosition;
import net.minecraft.server.v1_9_R1.Blocks;
import net.minecraft.server.v1_9_R1.ChunkSection;
import net.minecraft.server.v1_9_R1.DataBits;
import net.minecraft.server.v1_9_R1.DataPalette;
import net.minecraft.server.v1_9_R1.DataPaletteBlock;
import net.minecraft.server.v1_9_R1.IBlockData;
import net.minecraft.server.v1_9_R1.TileEntity;
import org.bukkit.Bukkit;
import java.util.Set;
import java.util.UUID;
import net.minecraft.server.v1_9_R2.Block;
import net.minecraft.server.v1_9_R2.BlockPosition;
import net.minecraft.server.v1_9_R2.Blocks;
import net.minecraft.server.v1_9_R2.ChunkSection;
import net.minecraft.server.v1_9_R2.DataBits;
import net.minecraft.server.v1_9_R2.DataPalette;
import net.minecraft.server.v1_9_R2.DataPaletteBlock;
import net.minecraft.server.v1_9_R2.Entity;
import net.minecraft.server.v1_9_R2.EntityPlayer;
import net.minecraft.server.v1_9_R2.EntityTypes;
import net.minecraft.server.v1_9_R2.IBlockData;
import net.minecraft.server.v1_9_R2.NBTTagCompound;
import net.minecraft.server.v1_9_R2.NibbleArray;
import net.minecraft.server.v1_9_R2.TileEntity;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.craftbukkit.v1_9_R1.CraftChunk;
import org.bukkit.entity.Entity;
import org.bukkit.craftbukkit.v1_9_R2.CraftChunk;
import org.bukkit.entity.Player;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.event.entity.CreatureSpawnEvent;
public class BukkitQueue_1_9_R1 extends BukkitQueue_0<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");
fieldAir.setAccessible(true);
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) {
e.printStackTrace();
}
Player player = null;
}
@Override
@ -88,37 +101,47 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
}
@Override
public boolean fixLighting(final FaweChunk pc, final boolean fixAll) {
public boolean fixLighting(final FaweChunk pc, RelightMode mode) {
try {
final CharFaweChunk bc = (CharFaweChunk) pc;
final Chunk chunk = (Chunk) bc.getChunk();
CharFaweChunk bc = (CharFaweChunk) pc;
Chunk chunk = (Chunk) bc.getChunk();
if (!chunk.isLoaded()) {
if (Fawe.get().getMainThread() != Thread.currentThread()) {
return false;
}
chunk.load(false);
}
// Initialize lighting
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle();
net.minecraft.server.v1_9_R2.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();
if (((bc.getTotalRelight() == 0) && !fixAll)) {
if (((bc.getTotalRelight() == 0) && mode == RelightMode.MINIMAL)) {
return true;
}
ChunkSection[] sections = c.getSections();
net.minecraft.server.v1_9_R1.World w = c.world;
if (mode == RelightMode.ALL) {
bc = getPrevious(bc, c.getSections(), null, null, null, true);
}
int total = bc.getTotalCount();
net.minecraft.server.v1_9_R2.World w = c.world;
final int X = chunk.getX() << 4;
final int Z = chunk.getZ() << 4;
BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0);
for (int j = 0; j < sections.length; j++) {
final Object section = sections[j];
if (section == null) {
continue;
}
if (((bc.getRelight(j) == 0) && !fixAll) || (bc.getCount(j) == 0) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0))) {
if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0))) {
continue;
}
final char[] array = bc.getIdArray(j);
@ -136,7 +159,7 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
case 0:
continue;
default:
if (!fixAll) {
if (mode == RelightMode.MINIMAL) {
continue;
}
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 y = FaweCache.CACHE_Y[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;
}
pos.c(X + x, y, Z + z);
@ -249,62 +272,199 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
}
@Override
public boolean setComponents(final FaweChunk pc, RunnableVal<FaweChunk> changeTask) {
// TODO change task
{
// blah, stuff
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());
// 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 World world = chunk.getWorld();
chunk.load(true);
try {
final boolean flag = world.getEnvironment() == Environment.NORMAL;
// Sections
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle();
net.minecraft.server.v1_9_R1.World w = c.world;
ChunkSection[] sections = c.getSections();
Class<? extends net.minecraft.server.v1_9_R1.Chunk> clazzChunk = c.getClass();
net.minecraft.server.v1_9_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle();
net.minecraft.server.v1_9_R2.World nmsWorld = nmsChunk.world;
ChunkSection[] sections = nmsChunk.getSections();
Class<? extends net.minecraft.server.v1_9_R2.Chunk> clazzChunk = nmsChunk.getClass();
final Field ef = clazzChunk.getDeclaredField("entitySlices");
final Collection<?>[] entities = (Collection<?>[]) ef.get(c);
// Trim tiles
boolean removed = false;
Map<BlockPosition, TileEntity> tiles = c.getTileEntities();
if (fs.getTotalCount() >= 65536) {
tiles.clear();
removed = true;
} 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)) {
final Collection<Entity>[] entities = (Collection<Entity>[]) ef.get(nmsChunk);
Map<BlockPosition, TileEntity> tiles = nmsChunk.getTileEntities();
// Remove entities
for (int i = 0; i < entities.length; i++) {
int count = fs.getCount(i);
if (count == 0) {
continue;
} else if (count >= 4096) {
entities[i].clear();
} else {
char[] array = fs.getIdArray(i);
Collection<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++) {
int count = fs.getCount(j);
if (count == 0) {
@ -367,7 +527,38 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
}
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) {
e.printStackTrace();
}
@ -392,117 +583,9 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
return true;
}
/**
* 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;
}
@Deprecated
public boolean unloadChunk(final String world, final Chunk chunk) {
net.minecraft.server.v1_9_R1.Chunk c = ((CraftChunk) chunk).getHandle();
net.minecraft.server.v1_9_R2.Chunk c = ((CraftChunk) chunk).getHandle();
c.mustSave = false;
if (chunk.isLoaded()) {
chunk.unload(false, false);
@ -510,133 +593,6 @@ public class BukkitQueue_1_9_R1 extends BukkitQueue_0<Chunk, ChunkSection[], Dat
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
public FaweChunk getChunk(int x, int 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:
description: (FAWE) Reload the plugin
aliases: [/fawe,/fawereload]
wrg:
select:
description: (FAWE) Select your current WorldEdit Region.
aliases: [/wrg,wer,/wer,worldeditregion,/worldeditregion,/region]
aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region]
frb:
description: (FAWE) Rollback an edit
aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb]

View File

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

View File

@ -18,6 +18,7 @@ import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
@ -39,6 +40,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import javax.annotation.Nonnull;
import org.bukkit.Chunk;
import org.bukkit.Location;
@ -51,6 +53,31 @@ import org.bukkit.Location;
*/
public class FaweAPI {
/**
* Get a new EditSessionfor a player<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
* @return TaskManager
@ -133,7 +160,7 @@ public class FaweAPI {
* 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>
* - 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 reason
*/
@ -229,7 +256,11 @@ public class FaweAPI {
Collections.sort(files, new Comparator<File>() {
@Override
public int compare(File a, File b) {
long value = a.lastModified() - b.lastModified();
String aName = a.getName();
String bName = b.getName();
int aI = Integer.parseInt(aName.substring(0, aName.length() - 3));
int bI = Integer.parseInt(bName.substring(0, bName.length() - 3));
long value = aI - bI;
return value == 0 ? 0 : value < 0 ? -1 : 1;
}
});
@ -292,21 +323,21 @@ public class FaweAPI {
* @param world
* @param x
* @param z
* @param fixAll
* @param mode
*/
public static void fixLighting(String world, int x, int z, final boolean fixAll) {
public static void fixLighting(String world, int x, int z, FaweQueue.RelightMode mode) {
FaweQueue queue = SetQueue.IMP.getNewQueue(world, true, false);
queue.fixLighting(queue.getChunk(x, z), fixAll);
queue.fixLighting(queue.getChunk(x, z), mode);
}
/**
* Fix the lighting in a chunk
* @param chunk
* @param fixAll
* @param mode
*/
public static void fixLighting(final Chunk chunk, final boolean fixAll) {
public static void fixLighting(final Chunk chunk, FaweQueue.RelightMode mode) {
FaweQueue queue = SetQueue.IMP.getNewQueue(chunk.getWorld().getName(), true, false);
queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), fixAll);
queue.fixLighting(queue.getChunk(chunk.getX(), chunk.getZ()), mode);
}
/**
@ -318,6 +349,7 @@ public class FaweAPI {
* @param loc
* @return
*/
@Deprecated
public static void streamSchematic(final File file, final Location loc) {
final FaweLocation fl = new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
streamSchematic(file, fl);
@ -331,6 +363,7 @@ public class FaweAPI {
* @param loc
* @return
*/
@Deprecated
public static void streamSchematic(final File file, final FaweLocation loc) {
try {
final FileInputStream is = new FileInputStream(file);
@ -347,6 +380,7 @@ public class FaweAPI {
* @param url
* @param loc
*/
@Deprecated
public static void streamSchematic(final URL url, final FaweLocation loc) {
try {
final ReadableByteChannel rbc = Channels.newChannel(url.openStream());
@ -366,6 +400,7 @@ public class FaweAPI {
* @param loc
* @throws IOException
*/
@Deprecated
public static void streamSchematic(final InputStream is, final FaweLocation loc) throws IOException {
final NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is));
Tag tag = stream.readNamedTag().getTag();
@ -403,79 +438,10 @@ public class FaweAPI {
final int i = i2 + x;
final int xx = x_offset + x;
final short id = (short) (ids[i] & 0xFF);
switch (id) {
case 0:
case 2:
case 4:
case 13:
case 14:
case 15:
case 20:
case 21:
case 22:
case 30:
case 32:
case 37:
case 39:
case 40:
case 41:
case 42:
case 45:
case 46:
case 47:
case 48:
case 49:
case 51:
case 56:
case 57:
case 58:
case 60:
case 7:
case 8:
case 9:
case 10:
case 11:
case 73:
case 74:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 85:
case 87:
case 88:
case 101:
case 102:
case 103:
case 110:
case 112:
case 113:
case 121:
case 122:
case 129:
case 133:
case 165:
case 166:
case 169:
case 170:
case 172:
case 173:
case 174:
case 181:
case 182:
case 188:
case 189:
case 190:
case 191:
case 192:
queue.setBlock(xx, yy, zz, id, (byte) 0);
break;
default: {
queue.setBlock(xx, yy, zz, id, datas[i]);
break;
}
if (FaweCache.hasData(id)) {
queue.setBlock(xx, yy, zz, id, datas[i]);
} else {
queue.setBlock(xx, yy, zz, id, (byte) 0);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public abstract class CharFaweChunk<T> extends FaweChunk<T> {
@ -111,7 +112,7 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return this.ids[i];
}
public char[][] getIdArrays() {
public char[][] getCombinedIdArrays() {
return this.ids;
}
@ -153,8 +154,12 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
return entities == null ? new HashSet<CompoundTag>() : entities;
}
public HashSet<CompoundTag> entities;
public HashSet<UUID> entityRemoves;
@Override
public void setEntity(CompoundTag tag) {
if (entities == null) {
@ -163,6 +168,19 @@ public abstract class CharFaweChunk<T> extends FaweChunk<T> {
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
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];

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.boydti.fawe.object;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.FaweQueue;
import com.sk89q.jnbt.CompoundTag;
@ -7,6 +8,7 @@ import com.sk89q.worldedit.world.biome.BaseBiome;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public abstract class FaweChunk<T> {
@ -24,12 +26,23 @@ public abstract class FaweChunk<T> {
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) {
this.parent = parent;
this.x = x;
this.z = z;
}
/**
* Get the parent queue this chunk belongs to
* @return
*/
public FaweQueue getParent() {
return parent;
}
@ -42,24 +55,65 @@ public abstract class FaweChunk<T> {
return z;
}
/**
* Get a unique hashcode for this chunk
* @return
*/
public long longHash() {
return (long) x << 32 | z & 0xFFFFFFFFL;
}
/**
* Get a hashcode; unique below abs(x/z) < Short.MAX_VALUE
* @return
*/
@Override
public int hashCode() {
return x << 16 | z & 0xFFFF;
}
/**
* Add the chunk to the queue
*/
public void addToQueue() {
parent.setChunk(this);
}
/**
* Fix the lighting in this chunk
*/
public void fixLighting() {
parent.fixLighting(this, Settings.FIX_ALL_LIGHTING);
parent.fixLighting(this, Settings.FIX_ALL_LIGHTING ? FaweQueue.RelightMode.OPTIMAL : FaweQueue.RelightMode.MINIMAL);
}
public abstract char[][] getIdArrays();
/**
* This may return the raw value or constructed depending on the implementation<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
@ -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) {
if (run != null) {
tasks.add(run);
@ -108,22 +166,61 @@ public abstract class FaweChunk<T> {
tasks.clear();
}
/**
* Get the underlying chunk object
* @return
*/
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 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();
/**
* 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();
/**
* Get the tile at a location
* @param x
* @param y
* @param z
* @return
*/
public abstract CompoundTag getTile(int x, int y, int z);
public abstract void setBiome(final int x, final int z, final BaseBiome biome);
/**
* Spend time now so that the chunk can be more efficiently dispatched later<br>
* - Modifications after this call will be ignored
*/
public void optimize() {}
@Override

View File

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

View File

@ -173,7 +173,7 @@ public abstract class FawePlayer<T> {
FaweStreamChangeSet set = new DiskStorageHistory(world, uuid, index);
EditSession edit = set.toEditSession(getPlayer());
if (world.equals(getWorld())) {
session.remember(edit, false);
session.remember(edit, false, false);
} else {
return;
}
@ -185,7 +185,16 @@ public abstract class FawePlayer<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
*/
public FaweLimit getLimit() {
@ -370,4 +379,11 @@ public abstract class FawePlayer<T> {
WorldEdit.getInstance().removeSession(getPlayer());
Fawe.get().unregister(getName());
}
/**
* Get a new EditSession from this player
*/
public EditSession getNewEditSession() {
return WorldEdit.getInstance().getEditSessionFactory().getEditSession(getWorld(), -1, getPlayer());
}
}

View File

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

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

View File

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

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.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.LongTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
@ -11,6 +12,7 @@ import com.sk89q.worldedit.history.UndoContext;
import com.sk89q.worldedit.history.change.Change;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class MutableEntityChange implements Change {
@ -26,6 +28,8 @@ public class MutableEntityChange implements Change {
public void undo(UndoContext context) throws WorldEditException {
if (!create) {
create(context);
} else {
delete(context);
}
}
@ -33,6 +37,36 @@ public class MutableEntityChange implements Change {
public void redo(UndoContext context) throws WorldEditException {
if (create) {
create(context);
} else {
delete(context);
}
}
public void delete(UndoContext context) {
Extent extent = context.getExtent();
if (extent.getClass() == FastWorldEditExtent.class) {
FastWorldEditExtent fwee = (FastWorldEditExtent) extent;
Map<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.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPOutputStream;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4InputStream;
@ -56,20 +54,15 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
// NBT From
private NBTOutputStream osNBTF;
private GZIPOutputStream osNBTFG;
private AtomicInteger osNBTFI;
// NBT To
private NBTOutputStream osNBTT;
private GZIPOutputStream osNBTTG;
// Entity Create From
private NBTOutputStream osENTCF;
private GZIPOutputStream osENTCFG;
// Entity Create To
private NBTOutputStream osENTCT;
private GZIPOutputStream osENTCTG;
private World world;
@ -82,6 +75,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
public DiskStorageHistory(World world, UUID uuid) {
super(world);
String base = "history" + File.separator + world.getName() + File.separator + uuid;
File folder = new File(Fawe.imp().getDirectory(), base);
int max = 0;
@ -100,6 +94,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
public DiskStorageHistory(World world, UUID uuid, int index) {
super(world);
init(world, uuid, index);
}
@ -125,27 +120,28 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
@Override
public boolean flush() {
boolean flushed = false;
super.flush();
boolean flushed = osBD != null || osNBTF != null || osNBTT != null && osENTCF != null || osENTCT != null;
try {
if (osBD != null) {
flushed = true;
osBD.flush();
osBD.close();
osBD = null;
}
if (osNBTF != null) {
flushed = true;
osNBTFG.flush();
osNBTF.close();
osNBTF = null;
osNBTFG = null;
}
if (osNBTT != null) {
flushed = true;
osNBTTG.flush();
osNBTT.close();
osNBTT = null;
osNBTTG = null;
}
if (osENTCF != null) {
osENTCF.close();
osENTCF = null;
}
if (osENTCT != null) {
osENTCT.close();
osENTCT = null;
}
} catch (Exception e) {
e.printStackTrace();
@ -185,8 +181,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
enttFile.getParentFile().mkdirs();
enttFile.createNewFile();
osENTCTG = new GZIPOutputStream(new FileOutputStream(enttFile), true);
osENTCT = new NBTOutputStream(osENTCTG);
osENTCT = new NBTOutputStream(getCompressedOS(new FileOutputStream(enttFile)));
return osENTCT;
}
@ -197,8 +192,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
entfFile.getParentFile().mkdirs();
entfFile.createNewFile();
osENTCFG = new GZIPOutputStream(new FileOutputStream(entfFile), true);
osENTCF = new NBTOutputStream(osENTCFG);
osENTCF = new NBTOutputStream(getCompressedOS(new FileOutputStream(entfFile)));
return osENTCF;
}
@ -209,8 +203,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
nbttFile.getParentFile().mkdirs();
nbttFile.createNewFile();
osNBTTG = new GZIPOutputStream(new FileOutputStream(nbttFile), true);
osNBTT = new NBTOutputStream(osNBTTG);
osNBTT = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbttFile)));
return osNBTT;
}
@ -221,9 +214,7 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
}
nbtfFile.getParentFile().mkdirs();
nbtfFile.createNewFile();
osNBTFG = new GZIPOutputStream(new FileOutputStream(nbtfFile), true);
osNBTF = new NBTOutputStream(osNBTFG);
osNBTFI = new AtomicInteger();
osNBTF = new NBTOutputStream(getCompressedOS(new FileOutputStream(nbtfFile)));
return osNBTF;
}

View File

@ -7,6 +7,7 @@ import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.FaweQueue;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.EditSession;
@ -20,12 +21,36 @@ import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.world.World;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class FaweChangeSet implements ChangeSet {
public abstract boolean flush();
private final World world;
public FaweChangeSet(World world) {
this.world = world;
}
public World getWorld() {
return world;
}
public boolean flush() {
try {
while (waiting.get() > 0) {
synchronized (lock) {
lock.wait(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
public abstract void add(int x, int y, int z, int combinedFrom, int combinedTo);
@ -47,7 +72,7 @@ public abstract class FaweChangeSet implements ChangeSet {
public EditSession toEditSession(Player player) {
EditSessionFactory factory = WorldEdit.getInstance().getEditSessionFactory();
EditSession edit = factory.getEditSession(player.getWorld(), -1, null, player);
EditSession edit = factory.getEditSession(world, -1, null, player);
edit.setChangeSet(this);
edit.dequeue();
return edit;
@ -131,91 +156,111 @@ public abstract class FaweChangeSet implements ChangeSet {
}
}
private AtomicInteger waiting = new AtomicInteger(0);
private Object lock = new Object();
public void addChangeTask(FaweQueue queue) {
queue.setChangeTask(new RunnableVal2<FaweChunk, FaweChunk>() {
@Override
public void run(final FaweChunk previous, FaweChunk next) {
/**
* TODO cache NBT
* - Counter variable for nbt changes
* - Record biome changes
*/
int cx = previous.getX();
int cz = previous.getZ();
int bx = cx << 4;
int bz = cz << 4;
// Biome changes
{
// TODO
}
// Block changes
{
// Current blocks
char[][] currentIds = next.getIdArrays();
// Previous blocks in modified sections (i.e. we skip sections that weren't modified)
char[][] previousIds = previous.getIdArrays();
for (int layer = 0; layer < currentIds.length; layer++) {
char[] currentLayer = currentIds[layer];
char[] previousLayer = previousIds[layer];
if (currentLayer == null) {
continue;
}
int startY = layer << 4;
for (int y = 0; y < 16; y++) {
short[][] i1 = FaweCache.CACHE_J[y];
int yy = y + startY;
for (int x = 0; x < 16; x++) {
int xx = x + bx;
short[] i2 = i1[x];
for (int z = 0; z < 16; z++) {
int zz = z + bz;
int index = i2[z];
int combinedIdCurrent = currentLayer[index];
switch (combinedIdCurrent) {
case 0:
continue;
case 1:
combinedIdCurrent = 0;
default:
char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0;
if (combinedIdCurrent != combinedIdPrevious) {
add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent);
public void run(final FaweChunk previous, final FaweChunk next) {
waiting.incrementAndGet();
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
try {
int cx = previous.getX();
int cz = previous.getZ();
int bx = cx << 4;
int bz = cz << 4;
// Biome changes
{
// TODO
}
// Block changes
{
// Current blocks
char[][] currentIds = next.getCombinedIdArrays();
// Previous blocks in modified sections (i.e. we skip sections that weren't modified)
char[][] previousIds = previous.getCombinedIdArrays();
for (int layer = 0; layer < currentIds.length; layer++) {
char[] currentLayer = currentIds[layer];
char[] previousLayer = previousIds[layer];
if (currentLayer == null) {
continue;
}
int startY = layer << 4;
for (int y = 0; y < 16; y++) {
short[][] i1 = FaweCache.CACHE_J[y];
int yy = y + startY;
for (int x = 0; x < 16; x++) {
int xx = x + bx;
short[] i2 = i1[x];
for (int z = 0; z < 16; z++) {
int zz = z + bz;
int index = i2[z];
int combinedIdCurrent = currentLayer[index];
switch (combinedIdCurrent) {
case 0:
continue;
case 1:
combinedIdCurrent = 0;
default:
char combinedIdPrevious = previousLayer != null ? previousLayer[index] : 0;
if (combinedIdCurrent != combinedIdPrevious) {
synchronized (lock) {
add(xx, yy, zz, combinedIdPrevious, combinedIdCurrent);
}
}
}
}
}
}
}
}
// Tile changes
{
// Tiles created
Map<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.MutableEntityChange;
import com.boydti.fawe.object.change.MutableTileChange;
import com.google.common.collect.Iterators;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.worldedit.history.change.Change;
import com.sk89q.worldedit.world.World;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -21,9 +21,12 @@ import net.jpountz.lz4.LZ4OutputStream;
public abstract class FaweStreamChangeSet extends FaweChangeSet {
public FaweStreamChangeSet(World world) {
super(world);
}
@Override
public int size() {
System.out.println("SIZE: " + blockSize);
// Flush so we can accurately get the size
flush();
return blockSize;
@ -172,7 +175,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
if (read0 == -1) {
return null;
}
System.out.println("r0: " + read0);
int x = ((byte) read0 & 0xFF) + ((byte) is.read() << 8) + originX;
int z = ((byte) is.read() & 0xFF) + ((byte) is.read() << 8) + originZ;
int y = is.read() & 0xff;
@ -192,7 +194,6 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
change.id = (short) ((from2 << 4) + (from1 >> 4));
change.data = (byte) (from1 & 0xf);
}
System.out.println("CHANGE: " + change.id);
return change;
} catch (Exception ignoreEOF) {
ignoreEOF.printStackTrace();
@ -206,10 +207,8 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
last = read();
}
if (last != null) {
System.out.println("HAS NEXT!");
return true;
}
System.out.println("NO NEXT");
try {
is.close();
} catch (IOException e) {
@ -333,18 +332,43 @@ public abstract class FaweStreamChangeSet extends FaweChangeSet {
}
public Iterator<Change> getIterator(final boolean dir) {
System.out.println("GET ITERATOR: " + dir);
flush();
try {
Iterator<MutableTileChange> tileCreate = getTileIterator(getTileCreateIS(), true, dir);
Iterator<MutableTileChange> tileRemove = getTileIterator(getTileRemoveIS(), false, dir);
final Iterator<MutableTileChange> tileCreate = getTileIterator(getTileCreateIS(), true, dir);
final Iterator<MutableTileChange> tileRemove = getTileIterator(getTileRemoveIS(), false, dir);
Iterator<MutableEntityChange> entityCreate = getEntityIterator(getEntityCreateIS(), true, dir);
Iterator<MutableEntityChange> entityRemove = getEntityIterator(getEntityRemoveIS(), false, dir);
final Iterator<MutableEntityChange> entityCreate = getEntityIterator(getEntityCreateIS(), true, 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) {
e.printStackTrace();
}

View File

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

View File

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

View File

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

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 java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingDeque;
public abstract class FaweQueue {
public static enum ProgressType {
QUEUE,
DISPATCH,
DONE,
}
public enum RelightMode {
MINIMAL,
OPTIMAL,
ALL,
}
public final String world;
public LinkedBlockingDeque<EditSession> sessions;
public long modified = System.currentTimeMillis();
public RunnableVal2<FaweChunk, FaweChunk> changeTask;
public RunnableVal2<ProgressType, Integer> progressTask;
public FaweQueue(String world) {
this.world = world;
@ -33,6 +47,16 @@ public abstract class FaweQueue {
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() {
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 removeEntity(int x, int y, int z, UUID uuid);
public abstract boolean setBiome(final int x, final int z, final BaseBiome biome);
public abstract FaweChunk<?> getChunk(int x, int z);
public abstract void setChunk(final FaweChunk<?> chunk);
public abstract boolean fixLighting(final FaweChunk<?> chunk, final boolean fixAll);
public abstract boolean fixLighting(final FaweChunk<?> chunk, RelightMode mode);
public abstract boolean isChunkLoaded(final int x, final int z);

View File

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

View File

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

View File

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

View File

@ -104,7 +104,6 @@ public abstract class TaskManager {
/**
* Break up a task and run it in fragments of 5ms.<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 task - The task to run on each object
* @param whenDone - When the object task completes
@ -133,6 +132,7 @@ public abstract class TaskManager {
/**
* Quickly run a task on the main thread, and wait for execution to finish:<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 <T>
* @return
@ -144,6 +144,7 @@ public abstract class TaskManager {
/**
* 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>
* - Usualy wait time is around 25ms<br>
* @param function
* @param timeout - How long to wait for execution
* @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.FawePlayer;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.exception.FaweException;
import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.regions.FaweMask;
@ -58,36 +59,50 @@ public class WEManager {
}
}
/**
* Get a player's mask
* @param player
* @return
*/
public HashSet<RegionWrapper> getMask(final FawePlayer<?> player) {
final HashSet<RegionWrapper> regions = new HashSet<>();
if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) {
regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE));
player.deleteMeta("lastmask");
return regions;
}
for (final FaweMaskManager manager : this.managers) {
if (player.hasPermission("fawe." + manager.getKey())) {
final FaweMask mask = manager.getMask(player);
if (mask != null) {
regions.addAll(mask.getRegions());
return TaskManager.IMP.sync(new RunnableVal<HashSet<RegionWrapper>>() {
@Override
public void run(HashSet<RegionWrapper> value) {
final HashSet<RegionWrapper> regions = new HashSet<>();
if (player.hasPermission("fawe.bypass") || !Settings.REGION_RESTRICTIONS) {
regions.add(new RegionWrapper(Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE));
player.deleteMeta("lastmask");
this.value = regions;
return;
}
}
}
if (regions.size() == 0) {
HashSet<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");
return regions;
for (final FaweMaskManager manager : managers) {
if (player.hasPermission("fawe." + manager.getKey())) {
final FaweMask mask = manager.getMask(player);
if (mask != null) {
regions.addAll(mask.getRegions());
}
}
}
return mask;
if (regions.size() == 0) {
HashSet<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;
}
}
player.setMeta("lastmask", regions);
return regions;
}, 1000);
}
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>() {
@Override
public void run(Object value) {
edit.queue.next();
edit.getQueue().next();
setPosition(new Vector(x + 0.5, y, z + 0.5));
}
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -47,11 +47,11 @@ public class SpongeCommand implements CommandCallable {
@Override
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
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();
Chunk spongeChunk = (Chunk) nmsChunk;
char[][] ids = ((SpongeChunk_1_8) fc).getIdArrays();
char[][] ids = ((SpongeChunk_1_8) fc).getCombinedIdArrays();
MutableBlockVolumeWorker<? extends Chunk> blockWorker = spongeChunk.getBlockWorker();
blockWorker.map(new BlockVolumeMapper() {
@Override