332 lines
11 KiB
Java
332 lines
11 KiB
Java
package com.boydti.fawe;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.net.URL;
|
|
import java.nio.channels.Channels;
|
|
import java.nio.channels.ReadableByteChannel;
|
|
import java.util.Map;
|
|
import java.util.zip.GZIPInputStream;
|
|
|
|
import org.bukkit.Chunk;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
|
|
import com.boydti.fawe.object.ChunkLoc;
|
|
import com.boydti.fawe.object.FaweChunk;
|
|
import com.boydti.fawe.object.FaweLocation;
|
|
import com.boydti.fawe.util.SetBlockQueue;
|
|
import com.boydti.fawe.util.TaskManager;
|
|
import com.sk89q.jnbt.ByteArrayTag;
|
|
import com.sk89q.jnbt.CompoundTag;
|
|
import com.sk89q.jnbt.IntTag;
|
|
import com.sk89q.jnbt.NBTInputStream;
|
|
import com.sk89q.jnbt.NamedTag;
|
|
import com.sk89q.jnbt.ShortTag;
|
|
import com.sk89q.jnbt.Tag;
|
|
import com.sk89q.worldedit.world.biome.BaseBiome;
|
|
|
|
/**
|
|
* The FaweAPI class offers a few useful functions.<br>
|
|
* - This class is not intended to replace the WorldEdit API<br>
|
|
* <br>
|
|
* FaweAPI.[some method]
|
|
*/
|
|
public class FaweAPI {
|
|
|
|
/**
|
|
* Set a block at a location asynchronously
|
|
* @param loc
|
|
* @param m
|
|
*/
|
|
public static void setBlockAsync(final Location loc, final Material m) {
|
|
SetBlockQueue.IMP.setBlock(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), (short) m.getId());
|
|
}
|
|
|
|
/**
|
|
* Set a block at a location asynchronously
|
|
* @param world
|
|
* @param x
|
|
* @param y
|
|
* @param z
|
|
* @param id
|
|
* @param data
|
|
*/
|
|
public static void setBlockAsync(final String world, final int x, final int y, final int z, final short id, final byte data) {
|
|
SetBlockQueue.IMP.setBlock(world, x, y, z, id, data);
|
|
}
|
|
|
|
/**
|
|
* Set a biome at a location asynchronously
|
|
* @param world
|
|
* @param x
|
|
* @param z
|
|
* @param id
|
|
* @param data
|
|
*/
|
|
public static void setBiomeAsync(final String world, final int x, final int z, BaseBiome biome) {
|
|
SetBlockQueue.IMP.setBiome(world, x, z, biome);
|
|
}
|
|
|
|
/**
|
|
* Set a biome at a location asynchronously
|
|
* @param loc
|
|
* @param biome
|
|
*/
|
|
public static void setBiomeAsync(Location loc, BaseBiome biome) {
|
|
SetBlockQueue.IMP.setBiome(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockZ(), biome);
|
|
}
|
|
|
|
/**
|
|
* This will return a FaweChunk object that can be modified.<br>
|
|
* - The FaweChunk object can be reused if you want identical changes across chunks<br>
|
|
* - This is additive modification.<br>
|
|
* - First use {@link FaweChunk#fill(int, byte)} (e.g. with air) for absolute modification<br>
|
|
* When ready, use {@link #setChunk(FaweChunk, ChunkLoc)}
|
|
* @return
|
|
*/
|
|
public static FaweChunk<?> createChunk() {
|
|
return SetBlockQueue.IMP.queue.getChunk(new ChunkLoc(null, 0, 0));
|
|
}
|
|
|
|
/**
|
|
* @see #createChunk()
|
|
* @param data
|
|
* @param location
|
|
*/
|
|
public static void setChunkAsync(FaweChunk<?> data, ChunkLoc location) {
|
|
data.setChunkLoc(location);
|
|
data.addToQueue();
|
|
}
|
|
|
|
/**
|
|
* @see #createChunk()
|
|
* @param data
|
|
* @param chunk
|
|
*/
|
|
public static void setChunkAsync(FaweChunk<?> data, Chunk chunk) {
|
|
ChunkLoc loc = new ChunkLoc(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
|
|
data.setChunkLoc(loc);
|
|
data.addToQueue();
|
|
}
|
|
|
|
/**
|
|
* Fix the lighting at a chunk location.<br>
|
|
* - The fixAll parameter determines if extensive relighting should occur (slow)
|
|
* @param loc
|
|
*/
|
|
public static void fixLighting(ChunkLoc loc, boolean fixAll) {
|
|
SetBlockQueue.IMP.queue.fixLighting(SetBlockQueue.IMP.queue.getChunk(loc), fixAll);
|
|
}
|
|
|
|
/**
|
|
* Fix the lighting at a chunk.<br>
|
|
* - The fixAll parameter determines if extensive relighting should occur (slow)
|
|
* @param chunk
|
|
*/
|
|
public static void fixLighting(Chunk chunk, boolean fixAll) {
|
|
ChunkLoc loc = new ChunkLoc(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
|
|
SetBlockQueue.IMP.queue.fixLighting(SetBlockQueue.IMP.queue.getChunk(loc), fixAll);
|
|
}
|
|
|
|
/**
|
|
* If a schematic is too large to be pasted normally<br>
|
|
* - Skips any block history
|
|
* - Ignores some block data
|
|
* @param file
|
|
* @param loc
|
|
* @return
|
|
*/
|
|
public static void streamSchematicAsync(final File file, final Location loc) {
|
|
FaweLocation fl = new FaweLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
|
streamSchematicAsync(file, fl);
|
|
}
|
|
|
|
/**
|
|
* If a schematic is too large to be pasted normally<br>
|
|
* - Skips any block history
|
|
* - Ignores some block data
|
|
* @param file
|
|
* @param loc
|
|
* @return
|
|
*/
|
|
public static void streamSchematicAsync(final File file, final FaweLocation loc) {
|
|
TaskManager.IMP.async(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
FileInputStream is = new FileInputStream(file);
|
|
streamSchematic(is, loc);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* If a schematic is too large to be pasted normally<br>
|
|
* - Skips any block history
|
|
* - Ignores some block data
|
|
* @param url
|
|
* @param loc
|
|
*/
|
|
public static void streamSchematicAsync(final URL url, final FaweLocation loc) {
|
|
TaskManager.IMP.async(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
ReadableByteChannel rbc = Channels.newChannel(url.openStream());
|
|
final InputStream is = Channels.newInputStream(rbc);
|
|
streamSchematic(is, loc);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* If a schematic is too large to be pasted normally<br>
|
|
* - Skips any block history
|
|
* - Ignores some block data
|
|
* @param is
|
|
* @param loc
|
|
* @throws IOException
|
|
*/
|
|
public static void streamSchematic(InputStream is, FaweLocation loc) throws IOException {
|
|
NBTInputStream stream = new NBTInputStream(new GZIPInputStream(is));
|
|
NamedTag name = stream.readNamedTag();
|
|
stream.close();
|
|
|
|
CompoundTag tag = (CompoundTag) name.getTag();
|
|
Map<String, Tag> tagMap = tag.getValue();
|
|
|
|
short width = ShortTag.class.cast(tagMap.get("Width")).getValue();
|
|
short length = ShortTag.class.cast(tagMap.get("Length")).getValue();
|
|
short height = ShortTag.class.cast(tagMap.get("Height")).getValue();
|
|
byte[] ids = ByteArrayTag.class.cast(tagMap.get("Blocks")).getValue();
|
|
byte[] datas = ByteArrayTag.class.cast(tagMap.get("Data")).getValue();
|
|
|
|
String world = loc.world;
|
|
|
|
int x_offset = loc.x + IntTag.class.cast(tagMap.get("WEOffsetX")).getValue();
|
|
int y_offset = loc.y + IntTag.class.cast(tagMap.get("WEOffsetY")).getValue();
|
|
int z_offset = loc.z + IntTag.class.cast(tagMap.get("WEOffsetZ")).getValue();
|
|
|
|
name = null;
|
|
tagMap = null;
|
|
tag = null;
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
final int yy = y_offset + y;
|
|
if (yy > 255) {
|
|
continue;
|
|
}
|
|
final int i1 = y * width * length;
|
|
for (int z = 0; z < length; z++) {
|
|
final int i2 = (z * width) + i1;
|
|
int zz = z_offset + z;
|
|
for (int x = 0; x < width; x++) {
|
|
final int i = i2 + x;
|
|
int xx = x_offset + x;
|
|
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 50:
|
|
case 51:
|
|
case 55:
|
|
case 56:
|
|
case 57:
|
|
case 58:
|
|
case 60:
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 73:
|
|
case 74:
|
|
case 75:
|
|
case 76:
|
|
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:
|
|
setBlockAsync(world, xx, yy, zz, id, (byte) 0);
|
|
break;
|
|
default: {
|
|
setBlockAsync(world, xx, yy, zz, id, datas[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ids = null;
|
|
datas = null;
|
|
System.gc();
|
|
System.gc();
|
|
}
|
|
|
|
/**
|
|
* Set a task to run when the async queue is empty
|
|
* @param whenDone
|
|
*/
|
|
public static void addTask(final Runnable whenDone) {
|
|
SetBlockQueue.IMP.addTask(whenDone);
|
|
}
|
|
}
|