FAWE texture support
- Put any mods or client jars in `FastAsyncWorldEdit/textures`
- These textures can be used in patterns and commands (WIP)
- Adds #color:color pattern
Added random flip to #fullcopy:<schem>:<random-rotate>:<random-flip>
Improved parsing for clipboard loading
FRB now only works in regions you are the owner of (or if you have admin
bypass)
This commit is contained in:
Jesse Boyd 2017-04-28 05:28:03 +10:00
parent 45827b611d
commit 17fb559f10
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
19 changed files with 760 additions and 55 deletions

3
.gitignore vendored
View File

@ -26,4 +26,5 @@ gradle.log
build
/mvn
spigot-1.10
wiki_permissions.md
wiki_permissions.md
/textures

View File

@ -0,0 +1,243 @@
package com.boydti.fawe.bukkit.v1_11;
import com.boydti.fawe.object.FaweInputStream;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.server.v1_11_R1.Block;
import net.minecraft.server.v1_11_R1.Chunk;
import net.minecraft.server.v1_11_R1.ChunkSection;
import net.minecraft.server.v1_11_R1.Entity;
import net.minecraft.server.v1_11_R1.ExceptionWorldConflict;
import net.minecraft.server.v1_11_R1.IAsyncChunkSaver;
import net.minecraft.server.v1_11_R1.IChunkLoader;
import net.minecraft.server.v1_11_R1.MinecraftKey;
import net.minecraft.server.v1_11_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_11_R1.NBTReadLimiter;
import net.minecraft.server.v1_11_R1.NBTTagCompound;
import net.minecraft.server.v1_11_R1.NBTTagList;
import net.minecraft.server.v1_11_R1.NextTickListEntry;
import net.minecraft.server.v1_11_R1.NibbleArray;
import net.minecraft.server.v1_11_R1.TileEntity;
import net.minecraft.server.v1_11_R1.World;
public class FaweChunkLoader implements IChunkLoader, IAsyncChunkSaver {
private final File folder;
private Long2ObjectMap<Long> hashes = new Long2ObjectOpenHashMap<>();
public FaweChunkLoader(File folder) {
this.folder = folder;
System.out.println(folder);
}
// writeNextIO (save)
@Override
public boolean c() {
return false;
}
// loadChunk
@Nullable
@Override
public Chunk a(World world, int x, int z) throws IOException {
long pair = MathMan.pairInt(x, z);
Long hash = hashes.get(pair);
if (hash == null) {
return null;
}
File file = new File(folder, hash.toString());
int length = (int) file.length();
try (FaweInputStream in = MainUtil.getCompressedIS(new FileInputStream(file), Math.min(length, 8192))) {
NBTTagCompound nbttagcompound = NBTCompressedStreamTools.a(in, NBTReadLimiter.a);
return readChunkFromNBT(world, nbttagcompound);
}
}
private Chunk readChunkFromNBT(World world, NBTTagCompound nbttagcompound) {
int i = nbttagcompound.getInt("xPos");
int j = nbttagcompound.getInt("zPos");
Chunk chunk = new Chunk(world, i, j);
chunk.a(nbttagcompound.getIntArray("HeightMap"));
chunk.d(nbttagcompound.getBoolean("TerrainPopulated"));
chunk.e(nbttagcompound.getBoolean("LightPopulated"));
chunk.c(nbttagcompound.getLong("InhabitedTime"));
NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10);
ChunkSection[] achunksection = new ChunkSection[16];
boolean flag1 = world.worldProvider.m();
for(int k = 0; k < nbttaglist.size(); ++k) {
NBTTagCompound nbttagcompound1 = nbttaglist.get(k);
byte b0 = nbttagcompound1.getByte("Y");
ChunkSection chunksection = new ChunkSection(b0 << 4, flag1);
byte[] abyte = nbttagcompound1.getByteArray("Blocks");
NibbleArray nibblearray = new NibbleArray(nbttagcompound1.getByteArray("Data"));
NibbleArray nibblearray1 = nbttagcompound1.hasKeyOfType("Add", 7) ? new NibbleArray(nbttagcompound1.getByteArray("Add")):null;
chunksection.getBlocks().a(abyte, nibblearray, nibblearray1);
chunksection.a(new NibbleArray(nbttagcompound1.getByteArray("BlockLight")));
if(flag1) {
chunksection.b(new NibbleArray(nbttagcompound1.getByteArray("SkyLight")));
}
chunksection.recalcBlockCounts();
achunksection[b0] = chunksection;
}
chunk.a(achunksection);
if(nbttagcompound.hasKeyOfType("Biomes", 7)) {
chunk.a(nbttagcompound.getByteArray("Biomes"));
}
return chunk;
}
// saveChunk
@Override
public void a(World world, Chunk chunk) throws IOException, ExceptionWorldConflict {
try {
NBTTagCompound exception = new NBTTagCompound();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
exception.set("Level", nbttagcompound1);
exception.setInt("DataVersion", 819);
this.writeChunkToNBT(chunk, world, nbttagcompound1);
// this.a(chunk.k(), exception);
} catch (Exception var5) {
}
}
private void writeChunkToNBT(Chunk chunk, World world, NBTTagCompound nbttagcompound) {
nbttagcompound.setInt("xPos", chunk.locX);
nbttagcompound.setInt("zPos", chunk.locZ);
nbttagcompound.setLong("LastUpdate", world.getTime());
nbttagcompound.setIntArray("HeightMap", chunk.r());
nbttagcompound.setBoolean("TerrainPopulated", chunk.isDone());
nbttagcompound.setBoolean("LightPopulated", chunk.v());
nbttagcompound.setLong("InhabitedTime", chunk.x());
ChunkSection[] achunksection = chunk.getSections();
NBTTagList nbttaglist = new NBTTagList();
boolean flag = world.worldProvider.m();
ChunkSection[] achunksection1 = achunksection;
int i = achunksection.length;
NBTTagCompound nbttagcompound1;
for(int nbttaglist1 = 0; nbttaglist1 < i; ++nbttaglist1) {
ChunkSection iterator = achunksection1[nbttaglist1];
if(iterator != Chunk.a) {
nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Y", (byte)(iterator.getYPosition() >> 4 & 255));
byte[] nbttaglist2 = new byte[4096];
NibbleArray list = new NibbleArray();
NibbleArray nibblearray1 = iterator.getBlocks().exportData(nbttaglist2, list);
nbttagcompound1.setByteArray("Blocks", nbttaglist2);
nbttagcompound1.setByteArray("Data", list.asBytes());
if(nibblearray1 != null) {
nbttagcompound1.setByteArray("Add", nibblearray1.asBytes());
}
nbttagcompound1.setByteArray("BlockLight", iterator.getEmittedLightArray().asBytes());
if(flag) {
nbttagcompound1.setByteArray("SkyLight", iterator.getSkyLightArray().asBytes());
} else {
nbttagcompound1.setByteArray("SkyLight", new byte[iterator.getEmittedLightArray().asBytes().length]);
}
nbttaglist.add(nbttagcompound1);
}
}
nbttagcompound.set("Sections", nbttaglist);
nbttagcompound.setByteArray("Biomes", chunk.getBiomeIndex());
chunk.g(false);
NBTTagList var22 = new NBTTagList();
Iterator var23;
for(i = 0; i < chunk.getEntitySlices().length; ++i) {
var23 = chunk.getEntitySlices()[i].iterator();
while(var23.hasNext()) {
Entity var24 = (Entity)var23.next();
nbttagcompound1 = new NBTTagCompound();
if(var24.d(nbttagcompound1)) {
chunk.g(true);
var22.add(nbttagcompound1);
}
}
}
nbttagcompound.set("Entities", var22);
NBTTagList var25 = new NBTTagList();
var23 = chunk.getTileEntities().values().iterator();
while(var23.hasNext()) {
TileEntity var26 = (TileEntity)var23.next();
nbttagcompound1 = var26.save(new NBTTagCompound());
var25.add(nbttagcompound1);
}
nbttagcompound.set("TileEntities", var25);
List var27 = world.a(chunk, false);
if(var27 != null) {
long k = world.getTime();
NBTTagList nbttaglist3 = new NBTTagList();
Iterator iterator1 = var27.iterator();
while(iterator1.hasNext()) {
NextTickListEntry nextticklistentry = (NextTickListEntry)iterator1.next();
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
MinecraftKey minecraftkey = (MinecraftKey) Block.REGISTRY.b(nextticklistentry.a());
nbttagcompound2.setString("i", minecraftkey == null?"":minecraftkey.toString());
nbttagcompound2.setInt("x", nextticklistentry.a.getX());
nbttagcompound2.setInt("y", nextticklistentry.a.getY());
nbttagcompound2.setInt("z", nextticklistentry.a.getZ());
nbttagcompound2.setInt("t", (int)(nextticklistentry.b - k));
nbttagcompound2.setInt("p", nextticklistentry.c);
nbttaglist3.add(nbttagcompound2);
}
nbttagcompound.set("TileTicks", nbttaglist3);
}
}
// saveExtraChunkData
@Override
public void b(World world, Chunk chunk) throws IOException {
}
// chunkTick
@Override
public void a() {
}
// saveExtraData
@Override
public void b() {
// try {
// this.savingExtraData = true;
//
// while(true) {
// if(this.writeNextIO()) {
// continue;
// }
// }
// } finally {
// this.savingExtraData = false;
// }
}
// isChunkGeneratedAt
@Override
public boolean a(int x, int z) {
return hashes.containsKey(MathMan.pairInt(x, z));
}
}

View File

@ -12,6 +12,7 @@ import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.StringMan;
import com.boydti.fawe.util.TaskManager;
import com.boydti.fawe.util.TextureUtil;
import com.boydti.fawe.util.Updater;
import com.boydti.fawe.util.WEManager;
import com.sk89q.jnbt.NBTInputStream;
@ -184,6 +185,7 @@ public class Fawe {
private FaweVersion version;
private VisualQueue visualQueue;
private Updater updater;
private TextureUtil textures;
/**
* Get the implementation specific class
@ -272,7 +274,6 @@ public class Fawe {
WEManager.IMP.managers.add(new PlotSquaredFeature());
Fawe.debug("Plugin 'PlotSquared' found. Using it now.");
} catch (Throwable e) {}
Fawe.this.worldedit = WorldEdit.getInstance();
}
}, 0);
@ -319,6 +320,19 @@ public class Fawe {
return updater;
}
public TextureUtil getTextureUtil() {
TextureUtil tmp = textures;
if (tmp == null) {
synchronized (this) {
tmp = textures;
if (tmp == null) {
textures = tmp = new TextureUtil();
}
}
}
return tmp;
}
/**
* The FaweTimer is a useful class for monitoring TPS
* @return FaweTimer
@ -372,13 +386,8 @@ public class Fawe {
BBC.load(new File(this.IMP.getDirectory(), "message.yml"));
}
private WorldEdit worldedit;
public WorldEdit getWorldEdit() {
if (this.worldedit == null) {
return worldedit = WorldEdit.getInstance();
}
return this.worldedit;
return WorldEdit.getInstance();
}
public static void setupInjector() {

View File

@ -68,6 +68,10 @@ public class Settings extends Config {
@Comment("Paths for various directories")
public static final class PATHS {
@Comment({
"Put any minecraft or mod jars for FAWE to be aware of block textures",
})
public String TEXTURES = "textures";
public String HISTORY = "history";
public String CLIPBOARD = "clipboard";
@Comment("Each player has their own sub directory for schematics")

View File

@ -8,6 +8,7 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
@ -45,10 +46,11 @@ public class SchemGen extends Resource {
}
Clipboard clipboard = holder.getClipboard();
Schematic schematic = new Schematic(clipboard);
if (holder.getTransform().isIdentity()) {
Transform transform = holder.getTransform();
if (transform.isIdentity()) {
schematic.paste(extent, mutable, false);
} else {
schematic.paste(extent, worldData, mutable, false, holder.getTransform());
schematic.paste(extent, worldData, mutable, false, transform);
}
mutable.mutY(y);
return true;

View File

@ -0,0 +1,48 @@
package com.boydti.fawe.object.brush.heightmap;
public class AverageHeightMapFilter {
private int[] inData;
private int[] buffer;
private final int width;
private final int height;
private final int minY;
private final int maxY;
public AverageHeightMapFilter(int[] inData, int width, int height, int minY, int maxY) {
this.inData = inData;
this.width = width;
this.height = height;
this.minY = minY;
this.maxY = maxY;
this.buffer = new int[inData.length];
}
public int[] filter(int iterations) {
for (int j = 0; j < iterations; j++) {
int a = -width;
int b = width;
int c = 1;
int d = -1;
for (int i = 0; i < inData.length; i++, a++, b++, c++, d++) {
int height = inData[i];
if (height < minY || height > maxY) {
buffer[i] = height;
continue;
}
int average = (2 + get(a, height) + get(b, height) + get(c, height) + get(d, height)) >> 2;
buffer[i] = average;
}
int[] tmp = inData;
inData = buffer;
buffer = tmp;
}
return inData;
}
private int get(int index, int def) {
int val = inData[Math.max(0, Math.min(inData.length - 1, index))];
if (val < minY || val > maxY) {
return def;
}
return val;
}
}

View File

@ -135,12 +135,16 @@ public class DiskStorageHistory extends FaweStreamChangeSet {
enttFile.delete();
}
public void undo(FawePlayer fp) {
EditSession session = toEditSession(fp);
public void undo(FawePlayer fp, RegionWrapper[] regions) {
EditSession session = toEditSession(fp, regions);
session.undo(session);
deleteFiles();
}
public void undo(FawePlayer fp) {
undo(fp, null);
}
public UUID getUUID() {
return uuid;
}

View File

@ -7,6 +7,7 @@ import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweChunk;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal2;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
@ -138,7 +139,17 @@ public abstract class FaweChangeSet implements ChangeSet {
public void delete() {};
public EditSession toEditSession(FawePlayer player) {
EditSession editSession = new EditSessionBuilder(getWorld()).player(player).autoQueue(false).fastmode(false).checkMemory(false).changeSet(this).limitUnlimited().allowedRegionsEverywhere().build();
return toEditSession(player, null);
}
public EditSession toEditSession(FawePlayer player, RegionWrapper[] regions) {
EditSessionBuilder builder = new EditSessionBuilder(getWorld()).player(player).autoQueue(false).fastmode(false).checkMemory(false).changeSet(this).limitUnlimited();
if (regions != null) {
builder.allowedRegions(regions);
} else {
builder.allowedRegionsEverywhere();
}
EditSession editSession = builder.build();
editSession.setSize(1);
return editSession;
}

View File

@ -1,10 +1,10 @@
package com.boydti.fawe.object.clipboard;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
public class MultiClipboardHolder extends ClipboardHolder{
private final ClipboardHolder[] holders;
@ -41,4 +41,4 @@ public class MultiClipboardHolder extends ClipboardHolder{
if (holder != null) holder.close();
}
}
}
}

View File

@ -10,6 +10,7 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.pattern.AbstractPattern;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
@ -21,9 +22,10 @@ public class RandomFullClipboardPattern extends AbstractPattern {
private final ClipboardHolder[] clipboards;
private final MutableBlockVector mutable = new MutableBlockVector();
private boolean randomRotate;
private boolean randomFlip;
private WorldData worldData;
public RandomFullClipboardPattern(Extent extent, WorldData worldData, ClipboardHolder[] clipboards, boolean randomRotate) {
public RandomFullClipboardPattern(Extent extent, WorldData worldData, ClipboardHolder[] clipboards, boolean randomRotate, boolean randomFlip) {
checkNotNull(clipboards);
this.clipboards = clipboards;
this.extent = extent;
@ -34,15 +36,24 @@ public class RandomFullClipboardPattern extends AbstractPattern {
@Override
public boolean apply(Extent extent, Vector setPosition, Vector getPosition) throws WorldEditException {
ClipboardHolder holder = clipboards[PseudoRandom.random.random(clipboards.length)];
AffineTransform transform = new AffineTransform();
if (randomRotate) {
transform = transform.rotateY(PseudoRandom.random.random(4) * 90);
holder.setTransform(new AffineTransform().rotateY(PseudoRandom.random.random(4) * 90));
}
if (randomFlip) {
transform = transform.scale(new Vector(1, 0, 0).multiply(-2).add(1, 1, 1));
}
if (!transform.isIdentity()) {
holder.setTransform(transform);
}
Clipboard clipboard = holder.getClipboard();
Schematic schematic = new Schematic(clipboard);
if (holder.getTransform().isIdentity()) {
Transform newTransform = holder.getTransform();
if (newTransform.isIdentity()) {
schematic.paste(extent, setPosition, false);
} else {
schematic.paste(extent, worldData, setPosition, false, holder.getTransform());
schematic.paste(extent, worldData, setPosition, false, newTransform);
}
return true;
}

View File

@ -1,28 +1,313 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Settings;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.registry.BundledBlockData;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.imageio.ImageIO;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
public class TextureUtil {
private final File folder;
private Color[] colors;
public TextureUtil(File folder) {
private int[] blockColors = new int[Character.MAX_VALUE + 1];
private int[] validColors;
private int[] validBlockIds;
private String getFileName(String path) {
String[] split = path.toString().split("[/|\\\\]");
String name = split[split.length - 1];
int dot = name.indexOf('.');
if (dot != -1) {
name = name.substring(0, dot);
}
return name;
}
private String alphabetize(String asset) {
String[] split = asset.split("_");
Arrays.sort(split);
return StringMan.join(split, "_");
}
/**
* Unfortunately the names used for the textures don't match the block id <br>
* - This will try to guess possible relevant block ids <br>
* - Match some by reformatting <br>
* - Match by reordering <br>
* - Match by appending / removing <br>
* - Match by hardcoded values <br>
*/
private void addTextureNames(String modelName, JSONObject root, Map<String, String> texturesMap) {
JSONObject textures = (JSONObject) root.get("textures");
if (textures == null) {
return;
}
Set<String> names = new HashSet<>();
String all = (String) textures.get("all");
if (all != null) {
String textureName = getFileName(all);
// Add the model
names.add(modelName);
names.add(textureName);
for (String name : new ArrayList<>(names)) {
if (name.contains("big_oak")) {
name = name.replaceAll("big_oak", "oak");
names.add(name);
}
String[] split = name.split("_");
switch (split[0]) {
case "glass":
names.add(name.replaceAll("glass_", "stained_glass_"));
break;
case "log":
names.add(name.replaceAll("log_", "log2_"));
break;
case "leaves":
names.add(name.replaceAll("leaves_", "leaves2_"));
case "mushroom":
names.add(name.replaceAll("mushroom_block_skin_", "mushroom_block_"));
default:
continue;
}
}
for (String name : names) {
texturesMap.putIfAbsent(name, textureName);
}
} else {
if (textures.containsKey("side") && textures.containsKey("end") && !textures.containsKey("bottom") && !textures.containsKey("top") && !textures.containsKey("platform")) {
String side = (String) textures.get("side");
} else if (textures.containsKey("up")) {
// TODO: Just mushroom, not super important
String up = (String) textures.get("up");
}
}
}
public TextureUtil() throws IOException, ParseException {
this(MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.TEXTURES));
}
public TextureUtil(File folder) throws IOException, ParseException {
this.folder = folder;
BundledBlockData bundled = BundledBlockData.getInstance();
loadModTextures();
}
public void loadModTextures() throws IOException, ParseException {
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".png");
return name.endsWith(".jar");
}
})) {
String name = file.getName().split("\\.")[0];
ZipFile zipFile = new ZipFile(file);
// get mods
BundledBlockData bundled = BundledBlockData.getInstance();
bundled.loadFromResource();
Set<String> mods = new HashSet<String>();
{
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
Path path = Paths.get(name);
if (path.startsWith("assets" + File.separator)) {
String[] split = path.toString().split(Pattern.quote(File.separator));
if (split.length > 1) {
String modId = split[1];
if (mods.add(modId)) {
}
}
}
continue;
}
}
Int2ObjectOpenHashMap<Integer> colorMap = new Int2ObjectOpenHashMap<>();
for (String modId : mods) {
String modelsDir = "assets" + "/" + modId + "/" + "models" + "/" + "block";
String texturesDir = "assets" + "/" + modId + "/" + "textures" + "/" + "blocks";
Map<String, String> texturesMap = new ConcurrentHashMap<>();
// Read models
{
// Read .json
// Find texture file
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}
String name = entry.getName();
if (!name.endsWith(".json")) {
continue;
}
Path path = Paths.get(name);
if (path.startsWith(modelsDir)) {
String[] split = path.toString().split("[/|\\\\|\\.]");
String blockName = getFileName(path.toString());
// Ignore special models
if (blockName.startsWith("#")) {
continue;
}
try (InputStream is = zipFile.getInputStream(entry)) { //Read from a file, or a HttpRequest, or whatever.
JSONParser parser = new JSONParser();
JSONObject root = (JSONObject) parser.parse(new InputStreamReader(is, "UTF-8"));
addTextureNames(blockName, root, texturesMap);
}
}
}
}
for (String key : new ArrayList<>(texturesMap.keySet())) {
String value = texturesMap.get(key);
texturesMap.put(alphabetize(key), value);
String[] split = key.split("_");
if (split.length > 1) {
key = StringMan.join(Arrays.copyOfRange(split, 0, split.length - 1), "_");
texturesMap.putIfAbsent(key, value);
}
}
Int2ObjectOpenHashMap<String> idMap = new Int2ObjectOpenHashMap<>();
for (String id : bundled.stateMap.keySet()) {
if (id.startsWith(modId)) {
BaseBlock block = bundled.findByState(id);
id = id.substring(modId.length() + 1).replaceAll(":", "_");
String texture = texturesMap.remove(id);
if (texture == null) {
texture = texturesMap.remove(alphabetize(id));
}
if (texture != null) {
idMap.put(block.getCombined(), texture);
}
}
}
{
for (Int2ObjectMap.Entry<String> entry : idMap.int2ObjectEntrySet()) {
int combined = entry.getIntKey();
String path = texturesDir + "/" + entry.getValue() + ".png";
ZipEntry textureEntry = zipFile.getEntry(path);
try (InputStream is = zipFile.getInputStream(textureEntry)) {
BufferedImage image = ImageIO.read(is);
int color = getColor(image);
colorMap.put((int) combined, (Integer) color);
}
}
// Load and map the textures
//
}
}
validBlockIds = new int[colorMap.size()];
validColors = new int[colorMap.size()];
Arrays.fill(blockColors, 0);
int index = 0;
for (Int2ObjectMap.Entry<Integer> entry : colorMap.int2ObjectEntrySet()) {
int combinedId = entry.getIntKey();
int color = entry.getValue();
blockColors[combinedId] = color;
validBlockIds[index] = combinedId;
validColors[index] = color;
index++;
}
zipFile.close();
}
}
//
public BaseBlock getNearestBlock(int color) {
long min = Long.MAX_VALUE;
int closest = 0;
int red1 = (color >> 16) & 0xFF;
int green1 = (color >> 8) & 0xFF;
int blue1 = (color >> 0) & 0xFF;
int alpha = (color >> 24) & 0xFF;
for (int i = 0; i < validColors.length; i++) {
int other = validColors[i];
if (((other >> 24) & 0xFF) == alpha) {
long distance = colorDistance(red1, green1, blue1, other);
if (distance < min) {
min = distance;
closest = validBlockIds[i];
}
}
}
return FaweCache.CACHE_BLOCK[closest];
}
public int getColor(BaseBlock block) {
return blockColors[block.getCombined()];
}
private boolean hasAlpha(int color) {
int alpha = (color >> 24) & 0xFF;
return alpha != 255;
}
public long colorDistance(int c1, int c2) {
int red1 = (c1 >> 16) & 0xFF;
int green1 = (c1 >> 8) & 0xFF;
int blue1 = (c1 >> 0) & 0xFF;
return colorDistance(red1, green1, blue1, c2);
}
private long colorDistance(int red1, int green1, int blue1, int c2) {
int red2 = (c2 >> 16) & 0xFF;
int green2 = (c2 >> 8) & 0xFF;
int blue2 = (c2 >> 0) & 0xFF;
int rmean = (red1 + red2) >> 1;
int r = red1 - red2;
int g = green1 - green2;
int b = blue1 - blue2;
return (((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8);
}
public int getColor(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
long totalRed = 0;
long totalGreen = 0;
long totalBlue = 0;
long totalAlpha = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int color = image.getRGB(x, y);
totalRed += (color >> 16) & 0xFF;
totalGreen += (color >> 8) & 0xFF;
totalBlue += (color >> 0) & 0xFF;
totalAlpha += (color >> 24) & 0xFF;
}
}
int a = width * height;
Color color = new Color((int) (totalRed / a), (int) (totalGreen / a), (int) (totalBlue / a), (int) (totalAlpha / a));
return color.getRGB();
}
// public Color getColor(BaseBlock block) {
// long r;
// long b;

View File

@ -28,14 +28,12 @@ import com.boydti.fawe.database.RollbackDatabase;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.MaskedFaweQueue;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.regions.FaweMaskManager;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.SetQueue;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -180,15 +178,21 @@ public class HistoryCommands {
final FaweQueue finalQueue;
RegionWrapper[] allowedRegions = fp.getCurrentRegions(FaweMaskManager.MaskType.OWNER);
if (allowedRegions.length != 1 || !allowedRegions[0].isGlobal()) {
finalQueue = new MaskedFaweQueue(SetQueue.IMP.getNewQueue(fp.getWorld(), true, false), allowedRegions);
} else {
finalQueue = SetQueue.IMP.getNewQueue(fp.getWorld(), true, false);
if (allowedRegions == null) {
BBC.NO_REGION.send(fp);
return;
}
// TODO mask the regions bot / top to the bottom and top coord in the allowedRegions
// TODO: then mask the edit to the bot / top
// if (allowedRegions.length != 1 || !allowedRegions[0].isGlobal()) {
// finalQueue = new MaskedFaweQueue(SetQueue.IMP.getNewQueue(fp.getWorld(), true, false), allowedRegions);
// } else {
// finalQueue = SetQueue.IMP.getNewQueue(fp.getWorld(), true, false);
// }
database.getPotentialEdits(other, System.currentTimeMillis() - timeDiff, bot, top, new RunnableVal<DiskStorageHistory>() {
@Override
public void run(DiskStorageHistory edit) {
edit.undo(fp);
edit.undo(fp, allowedRegions);
BBC.ROLLBACK_ELEMENT.send(player, Fawe.imp().getWorldName(edit.getWorld()) + "/" + user + "-" + edit.getIndex());
count.incrementAndGet();
}

View File

@ -92,10 +92,16 @@ public class SchematicCommands {
this.worldEdit = worldEdit;
}
@Command(aliases = { "loadall" }, usage = "[<format>] <filename|url>", desc = "Load multiple clipboards (paste will randomly choose one)")
@Command(
aliases = { "loadall" },
usage = "[<format>] <filename|url>",
help = "Load multiple clipboards\n" +
"The -r flag will apply random rotation",
desc = "Load multiple clipboards (paste will randomly choose one)"
)
@Deprecated
@CommandPermissions({ "worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.upload" })
public void loadall(final Player player, final LocalSession session, @Optional("schematic") final String formatName, final String filename) throws FilenameException {
public void loadall(final Player player, final LocalSession session, @Optional("schematic") final String formatName, final String filename, @Switch('r') boolean randomRotate) throws FilenameException {
final ClipboardFormat format = ClipboardFormat.findByAlias(formatName);
if (format == null) {
BBC.CLIPBOARD_INVALID_FORMAT.send(player, formatName);
@ -348,6 +354,7 @@ public class SchematicCommands {
}
for (int i = 0; i < len; i++) {
switch (args.getString(i).toLowerCase()) {
case "me":
case "mine":
mine = true;
break;

View File

@ -1,5 +1,6 @@
package com.sk89q.worldedit.extension.factory;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.command.FaweParser;
import com.boydti.fawe.command.SuggestInputParseException;
import com.boydti.fawe.object.pattern.BiomePattern;
@ -30,6 +31,7 @@ import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.input.ParserContext;
@ -53,6 +55,7 @@ import com.sk89q.worldedit.world.registry.BundledBlockData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.paint.Color;
public class HashTagPatternParser extends FaweParser<Pattern> {
@ -141,6 +144,16 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
throw new InputParseException("No session is available, so no clipboard is available");
}
}
case "#color": {
if (split2.length > 1) {
Color color = Color.web(split2[1]);
java.awt.Color awtColor = new java.awt.Color((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue(), (float) color.getOpacity());
BaseBlock block = Fawe.get().getTextureUtil().getNearestBlock(awtColor.getRGB());
return new BlockPattern(block);
} else {
throw new InputParseException("#color:<hex>");
}
}
case "#fullcopy": {
LocalSession session = context.requireSession();
if (session != null) {
@ -148,12 +161,26 @@ public class HashTagPatternParser extends FaweParser<Pattern> {
if (split2.length > 1) {
String location = split2[1];
try {
ClipboardHolder[] clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(context.getActor(), context.requireWorld().getWorldData(), location, true);
ClipboardHolder[] clipboards;
switch (location.toLowerCase()) {
case "#copy":
case "#clipboard":
ClipboardHolder clipboard = session.getExistingClipboard();
if (clipboard == null) {
throw new InputParseException("To use #fullcopy, please first copy something to your clipboard");
}
clipboards = new ClipboardHolder[] {clipboard};
break;
default:
clipboards = ClipboardFormat.SCHEMATIC.loadAllFromInput(context.getActor(), context.requireWorld().getWorldData(), location, true);
break;
}
if (clipboards == null) {
throw new InputParseException("#fullcopy:<source>");
}
boolean random = split2.length == 3 && split2[2].equalsIgnoreCase("true");
return new RandomFullClipboardPattern(Request.request().getExtent(), context.requireWorld().getWorldData(), clipboards, random);
boolean randomRotate = split2.length >= 3 && split2[2].equalsIgnoreCase("true");
boolean randomFlip = split2.length >= 4 && split2[3].equalsIgnoreCase("true");
return new RandomFullClipboardPattern(Request.request().getExtent(), context.requireWorld().getWorldData(), clipboards, randomRotate, randomFlip);
} catch (IOException e) {
throw new RuntimeException(e);

View File

@ -509,7 +509,6 @@ public class PlatformManager {
return;
}
}
break;
}

View File

@ -335,10 +335,16 @@ public enum ClipboardFormat {
}
File working = worldEdit.getWorkingDirectoryFile(config.saveDir);
File dir = new File(working, (Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? (player.getUniqueId().toString() + File.separator) : "") + input);
if (!dir.exists()) {
dir = new File(dir + "." + getExtension());
}
if (!dir.exists()) {
if ((!input.contains("/") && !input.contains("\\")) || player.hasPermission("worldedit.schematic.load.other")) {
dir = new File(worldEdit.getWorkingDirectoryFile(config.saveDir), input);
}
if (!dir.exists()) {
dir = new File(dir + "." + getExtension());
}
}
if (!dir.exists()) {
if (message) BBC.SCHEMATIC_NOT_FOUND.send(player, input);

View File

@ -125,6 +125,16 @@ public class HeightMap {
return layers ? applyLayers(newData) : apply(newData);
}
// TODO
// public int averageFilter(int iterations) throws WorldEditException {
// Vector min = region.getMinimumPoint();
// Vector max = region.getMaximumPoint();
// int shift = layers ? 3 : 0;
// AverageHeightMapFilter filter = new AverageHeightMapFilter(data, width, height, min.getBlockY() << shift, max.getBlockY() << shift);
// int[] newData = filter.filter(iterations);
// return layers ? applyLayers(newData) : apply(newData);
// }
public int applyLayers(int[] data) throws WorldEditException {
checkNotNull(data);

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.world.registry;
import com.boydti.fawe.FaweCache;
import com.google.common.io.Resources;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@ -43,6 +44,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@ -62,9 +64,9 @@ public class BundledBlockData {
private static final Logger log = Logger.getLogger(BundledBlockData.class.getCanonicalName());
private static final BundledBlockData INSTANCE = new BundledBlockData();
private final Map<String, BlockEntry> idMap = new HashMap<String, BlockEntry>();
private final Map<String, BaseBlock> stateMap = new HashMap<String, BaseBlock>();
private final Map<String, BlockEntry> localIdMap = new HashMap<String, BlockEntry>();
private final Map<String, BlockEntry> idMap = new ConcurrentHashMap<>();
public final Map<String, BaseBlock> stateMap = new ConcurrentHashMap<>();
private final Map<String, BlockEntry> localIdMap = new ConcurrentHashMap<>();
private final BlockEntry[] legacyMap = new BlockEntry[4096];
@ -102,6 +104,10 @@ public class BundledBlockData {
return localIdMap.keySet();
}
public Set<String> getStateNames() {
return stateMap.keySet();
}
public List<String> getBlockNames(String partial) {
partial = partial.toLowerCase();
List<String> blocks = new ArrayList<>();
@ -142,19 +148,33 @@ public class BundledBlockData {
return false;
}
idMap.put(entry.id, entry);
String id = (entry.id.contains(":") ? entry.id.split(":")[1] : entry.id).toLowerCase().replace(" ", "_");
localIdMap.putIfAbsent(id, entry);
String modId, id;
if (entry.id.contains(":")) {
String[] split = entry.id.split(":");
id = split[1];
modId = split[0];
} else {
modId = "";
id = entry.id;
}
idMap.putIfAbsent(id, entry);
localIdMap.putIfAbsent(id, entry);
legacyMap[entry.legacyId] = entry;
stateMap.putIfAbsent(id, FaweCache.getBlock(entry.legacyId, 0));
stateMap.putIfAbsent(entry.id, FaweCache.getBlock(entry.legacyId, 0));
if (entry.states == null) {
return true;
}
for (Map.Entry<String, FaweState> stateEntry : entry.states.entrySet()) {
for (Map.Entry<String, FaweStateValue> valueEntry : stateEntry.getValue().valueMap().entrySet()) {
String key = valueEntry.getKey();
if (!stateMap.containsKey(key)) {
stateMap.put(key, new BaseBlock(entry.legacyId, valueEntry.getValue().data));
if (key.equals("true")) {
key = stateEntry.getKey();
}
stateMap.putIfAbsent(id + ":" + key, FaweCache.getBlock(entry.legacyId, valueEntry.getValue().data));
stateMap.putIfAbsent(entry.id + ":" + key, FaweCache.getBlock(entry.legacyId, valueEntry.getValue().data));
stateMap.putIfAbsent(modId + ":" + key, FaweCache.getBlock(entry.legacyId, valueEntry.getValue().data));
stateMap.putIfAbsent(key, FaweCache.getBlock(entry.legacyId, valueEntry.getValue().data));
}
}
FaweState half = entry.states.get("half");
@ -275,11 +295,22 @@ public class BundledBlockData {
@Nullable
public Integer toLegacyId(String id) {
BlockEntry entry = findById(id);
if (entry != null) {
return entry.legacyId;
} else {
return null;
if (entry == null) {
entry = localIdMap.get(id);
if (entry == null) {
int index = id.lastIndexOf('_');
if (index == -1) {
return null;
}
String data = id.substring(index + 1, id.length());
id = id.substring(0, index);
entry = localIdMap.get(id + ":" + data);
if (entry == null) {
return null;
}
}
}
return entry.legacyId;
}
/**

View File

@ -1,6 +1,9 @@
// Add blocks here if you need block names and rotation to work
// See: https://github.com/sk89q/WorldEdit/blob/master/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/blocks.json
// - WorldEdit only uses the id/names + rotation/facing properties.
// Add blocks here if you need block names, rotation and textures to work
// The following blocks are already bundled with FAWE: https://github.com/sk89q/WorldEdit/blob/master/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/blocks.json
// To generating the blocks from forge mods see:
// - https://github.com/wizjany/ForgeUtils
//
// Help with ForgeUtils: wizjany @ http://webchat.esper.net/?nick=&channels=sk89q
[
]