Copy paste/merge FAWE classes to this WorldEdit fork

- so certain people can look at the diff and complain about my sloppy code :(

Signed-off-by: Jesse Boyd <jessepaleg@gmail.com>
This commit is contained in:
Jesse Boyd
2018-08-13 00:03:07 +10:00
parent a920c77cb8
commit a629d15c74
994 changed files with 117583 additions and 10745 deletions

View File

@ -0,0 +1,27 @@
package com.boydti.fawe.util;
import java.util.Arrays;
public class ArrayUtil {
public static final void fill(byte[] a, int fromIndex, int toIndex, byte val) {
for (int i = fromIndex; i < toIndex; i++) a[i] = val;
}
public static final void fill(char[] a, int fromIndex, int toIndex, char val) {
for (int i = fromIndex; i < toIndex; i++) a[i] = val;
}
public static <T> T[] concatAll(T[] first, T[]... rest) {
int totalLength = first.length;
for (T[] array : rest) {
totalLength += array.length;
}
T[] result = Arrays.copyOf(first, totalLength);
int offset = first.length;
for (T[] array : rest) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
}

View File

@ -0,0 +1,124 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.object.brush.BrushSettings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.command.tool.BrushTool;
import com.sk89q.worldedit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public final class BrushCache {
private static final WeakHashMap<Object, BrushTool> brushCache = new WeakHashMap<>();
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static final CompoundTag getNBT(BaseItem item) {
return item.hasNbtData() ? item.getNbtData() : null;
}
private static final Object getKey(BaseItem item) {
return item.getNativeItem();
}
private static final ThreadLocal<Boolean> RECURSION = new ThreadLocal<>();
public static final BrushTool getTool(Player player, LocalSession session, BaseItem item) {
if (!item.hasNbtData()) return null;
Object key = getKey(item);
if (key == null) return null;
BrushTool cached = brushCache.get(key);
if (cached != null) return cached;
StringTag json = (StringTag) item.getNbtData().getValue().get("weBrushJson");
if (json != null) {
try {
if (RECURSION.get() != null) return null;
RECURSION.set(true);
BrushTool tool = BrushTool.fromString(player, session, json.getValue());
tool.setHolder(item);
brushCache.put(key, tool);
return tool;
} catch (Throwable ignore) {
ignore.printStackTrace();
Fawe.debug("Invalid brush for " + player + " holding " + item.getType() + ": " + json.getValue());
if (item != null) {
item.setNbtData(null);
brushCache.remove(key);
}
} finally {
RECURSION.remove();
}
}
return null;
}
public static BrushTool getCachedTool(BaseItem item) {
Object key = getKey(item);
if (key != null) return brushCache.get(key);
return null;
}
public static final BrushTool setTool(BaseItem item, BrushTool tool) {
if (item.getNativeItem() == null) return null;
CompoundTag nbt = item.getNbtData();
Map<String, Tag> map;
if (nbt == null) {
if (tool == null) {
return tool;
}
nbt = new CompoundTag(map = new HashMap<>());
} else {
map = ReflectionUtils.getMap(nbt.getValue());
}
brushCache.remove(getKey(item));
CompoundTag display = (CompoundTag) map.get("display");
Map<String, Tag> displayMap;
if (tool != null) {
String json = tool.toString(gson);
map.put("weBrushJson", new StringTag(json));
if (display == null) {
map.put("display", new CompoundTag(displayMap = new HashMap()));
} else {
displayMap = ReflectionUtils.getMap(display.getValue());
}
displayMap.put("Lore", FaweCache.asTag(json.split("\\r?\\n")));
String primary = (String) tool.getPrimary().getSettings().get(BrushSettings.SettingType.BRUSH);
String secondary = (String) tool.getSecondary().getSettings().get(BrushSettings.SettingType.BRUSH);
if (primary == null) primary = secondary;
if (secondary == null) secondary = primary;
if (primary != null) {
String name = primary == secondary ? primary.split(" ")[0] : primary.split(" ")[0] + " / " + secondary.split(" ")[0];
displayMap.put("Name", new StringTag(name));
}
} else if (map.containsKey("weBrushJson")) {
map.remove("weBrushJson");
if (display != null) {
displayMap = ReflectionUtils.getMap(display.getValue());
displayMap.remove("Lore");
displayMap.remove("Name");
if (displayMap.isEmpty()) {
map.remove("display");
}
}
} else {
return tool;
}
item.setNbtData(nbt);
if (tool != null) {
brushCache.put(getKey(item), tool);
}
return tool;
}
}

View File

@ -0,0 +1,87 @@
package com.boydti.fawe.util;
public class CachedMathMan {
private static final int ATAN2_BITS = 7;
private static final int ATAN2_BITS2 = ATAN2_BITS << 1;
private static final int ATAN2_MASK = ~(-1 << ATAN2_BITS2);
private static final int ATAN2_COUNT = ATAN2_MASK + 1;
private static final int ATAN2_DIM = (int) Math.sqrt(ATAN2_COUNT);
private static final float INV_ATAN2_DIM_MINUS_1 = 1.0f / (ATAN2_DIM - 1);
private static final float[] atan2 = new float[ATAN2_COUNT];
static {
for (int i = 0; i < ATAN2_DIM; i++) {
for (int j = 0; j < ATAN2_DIM; j++) {
float x0 = (float) i / ATAN2_DIM;
float y0 = (float) j / ATAN2_DIM;
atan2[(j * ATAN2_DIM) + i] = (float) Math.atan2(y0, x0);
}
}
}
private static float[] ANGLES = new float[65536];
static {
for (int i = 0; i < 65536; ++i) {
ANGLES[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
}
}
private static char[] SQRT = new char[65536];
static {
for (int i = 0; i < SQRT.length; i++) {
SQRT[i] = (char) Math.round(Math.sqrt(i));
}
}
/**
* Optimized for i elem 0,65536 (characters)
* @param i
* @return square root
*/
protected static int usqrt(int i) {
return SQRT[i];
}
protected static float sinInexact(double paramFloat) {
return ANGLES[(int) (paramFloat * 10430.378F) & 0xFFFF];
}
protected static float cosInexact(double paramFloat) {
return ANGLES[(int) (paramFloat * 10430.378F + 16384.0F) & 0xFFFF];
}
protected static final float atan2(float y, float x) {
float add, mul;
if (x < 0.0f) {
if (y < 0.0f) {
x = -x;
y = -y;
mul = 1.0f;
} else {
x = -x;
mul = -1.0f;
}
add = -3.141592653f;
} else {
if (y < 0.0f) {
y = -y;
mul = -1.0f;
} else {
mul = 1.0f;
}
add = 0.0f;
}
float invDiv = 1.0f / (((x < y) ? y : x) * INV_ATAN2_DIM_MINUS_1);
int xi = (int) (x * invDiv);
int yi = (int) (y * invDiv);
return (atan2[(yi * ATAN2_DIM) + xi] + add) * mul;
}
}

View File

@ -0,0 +1,63 @@
package com.boydti.fawe.util;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileNotFoundException;
public class CachedTextureUtil extends DelegateTextureUtil {
private final TextureUtil parent;
private transient Int2ObjectOpenHashMap<BlockTypes> colorBlockMap;
private transient Int2ObjectOpenHashMap<Integer> colorBiomeMap;
private transient Int2ObjectOpenHashMap<BlockTypes[]> colorLayerMap;
public CachedTextureUtil(TextureUtil parent) throws FileNotFoundException {
super(parent);
this.parent = parent;
this.colorBlockMap = new Int2ObjectOpenHashMap<>();
this.colorLayerMap = new Int2ObjectOpenHashMap<>();
this.colorBiomeMap = new Int2ObjectOpenHashMap<>();
}
@Override
public BlockTypes[] getNearestLayer(int color) {
BlockTypes[] closest = colorLayerMap.get(color);
if (closest != null) {
return closest;
}
closest = parent.getNearestLayer(color);
if (closest != null) {
colorLayerMap.put(color, closest.clone());
}
return closest;
}
@Override
public BiomeColor getNearestBiome(int color) {
Integer value = colorBiomeMap.get(color);
if (value != null) {
return getBiome(value);
}
BiomeColor result = parent.getNearestBiome(color);
if (result != null) {
colorBiomeMap.put((int) color, (Integer) result.id);
}
return result;
}
@Override
public BlockTypes getNearestBlock(int color) {
BlockTypes value = colorBlockMap.get(color);
if (value != null) {
return value;
}
BlockTypes result = parent.getNearestBlock(color);
if (result != null) {
colorBlockMap.put((int) color, result);
}
return result;
}
}

View File

@ -0,0 +1,47 @@
package com.boydti.fawe.util;
import java.io.FileNotFoundException;
import java.util.Arrays;
public class CleanTextureUtil extends TextureUtil {
private final int min, max;
public CleanTextureUtil(TextureUtil parent, int minPercent, int maxPercent) throws FileNotFoundException {
super(parent.getFolder());
this.min = minPercent;
this.max = maxPercent;
int minIndex = ((parent.distances.length - 1) * minPercent) / 100;
int maxIndex = ((parent.distances.length - 1) * maxPercent) / 100;
long min = parent.distances[minIndex];
long max = parent.distances[maxIndex];
for (; minIndex > 0 && parent.distances[minIndex - 1] == min; minIndex--) ;
for (; maxIndex < parent.distances.length - 1 && parent.distances[maxIndex + 1] == max; maxIndex++) ;
int num = maxIndex - minIndex + 1;
this.validMixBiomeColors = parent.validMixBiomeColors;
this.validMixBiomeIds = parent.validMixBiomeIds;
this.validBiomes = parent.validBiomes;
this.blockColors = parent.blockColors;
this.blockDistance = parent.blockDistance;
this.distances = Arrays.copyOfRange(parent.blockDistance, minIndex, maxIndex + 1);
this.validColors = new int[distances.length];
this.validBlockIds = new int[distances.length];
for (int i = 0, j = 0; i < parent.validBlockIds.length; i++) {
int combined = parent.validBlockIds[i];
long distance = parent.blockDistance[combined];
if (distance >= min && distance <= max) {
int color = parent.validColors[i];
this.validColors[j] = color;
this.validBlockIds[j++] = combined;
}
}
this.calculateLayerArrays();
}
public int getMin() {
return min;
}
public int getMax() {
return max;
}
}

View File

@ -0,0 +1,146 @@
package com.boydti.fawe.util;
import java.awt.Color;
import java.lang.reflect.Field;
import java.util.Locale;
public class ColorUtil {
private static final int PARSE_COMPONENT = 0; // percent, or clamped to [0,255] => [0,1]
private static final int PARSE_PERCENT = 1; // clamped to [0,100]% => [0,1]
private static final int PARSE_ANGLE = 2; // clamped to [0,360]
private static final int PARSE_ALPHA = 3; // clamped to [0f,1f]
private static float parseComponent(String color, int off, int end, int type) {
color = color.substring(off, end).trim();
if (color.endsWith("%")) {
if (type > PARSE_PERCENT) {
throw new IllegalArgumentException("Invalid color specification");
}
type = PARSE_PERCENT;
color = color.substring(0, color.length()-1).trim();
} else if (type == PARSE_PERCENT) {
throw new IllegalArgumentException("Invalid color specification");
}
float c = ((type == PARSE_COMPONENT)
? Integer.parseInt(color)
: Float.parseFloat(color));
switch (type) {
case PARSE_ALPHA:
return (c < 0f) ? 0f : ((c > 1f) ? 1f : c);
case PARSE_PERCENT:
return (c <= 0f) ? 0f : ((c >= 100f) ? 1f : (c / 100f));
case PARSE_COMPONENT:
return (c <= 0f) ? 0f : ((c >= 255f) ? 1f : (c / 255f));
case PARSE_ANGLE:
return ((c < 0f)
? ((c % 360f) + 360f)
: ((c > 360f)
? (c % 360f)
: c));
}
throw new IllegalArgumentException("Invalid color specification");
}
private static Color parseRGBColor(String color, int roff)
{
try {
int rend = color.indexOf(',', roff);
int gend = rend < 0 ? -1 : color.indexOf(',', rend+1);
int bend = gend < 0 ? -1 : color.indexOf(gend+1);
float r = parseComponent(color, roff, rend, PARSE_COMPONENT);
float g = parseComponent(color, rend+1, gend, PARSE_COMPONENT);
float b = parseComponent(color, gend+1, bend, PARSE_COMPONENT);
return new Color(r, g, b);
} catch (NumberFormatException nfe) {}
throw new IllegalArgumentException("Invalid color specification");
}
private static Color parseHSLColor(String color, int hoff)
{
try {
int hend = color.indexOf(',', hoff);
int send = hend < 0 ? -1 : color.indexOf(',', hend+1);
int lend = send < 0 ? -1 : color.indexOf(send+1);
float h = parseComponent(color, hoff, hend, PARSE_ANGLE);
float s = parseComponent(color, hend+1, send, PARSE_PERCENT);
float l = parseComponent(color, send+1, lend, PARSE_PERCENT);
return Color.getHSBColor(h, s, l);
} catch (NumberFormatException nfe) {}
throw new IllegalArgumentException("Invalid color specification");
}
public static Color parseColor(String colorString) {
if (colorString == null) {
throw new NullPointerException(
"The color components or name must be specified");
}
if (colorString.isEmpty()) {
throw new IllegalArgumentException("Invalid color specification");
}
String color = colorString.toLowerCase(Locale.ROOT);
if (color.startsWith("#")) {
color = color.substring(1);
} else if (color.startsWith("0x")) {
color = color.substring(2);
} else if (color.startsWith("rgb")) {
if (color.startsWith("(", 3)) {
return parseRGBColor(color, 4);
} else if (color.startsWith("a(", 3)) {
return parseRGBColor(color, 5);
}
} else if (color.startsWith("hsl")) {
if (color.startsWith("(", 3)) {
return parseHSLColor(color, 4);
} else if (color.startsWith("a(", 3)) {
return parseHSLColor(color, 5);
}
} else {
Color col = null;
try {
Field field = java.awt.Color.class.getField(color.toLowerCase());
col = (Color) field.get(null);
} catch (Throwable ignore) {}
if (col != null) {
return col;
}
}
int len = color.length();
try {
int r;
int g;
int b;
int a;
if (len == 3) {
r = Integer.parseInt(color.substring(0, 1), 16);
g = Integer.parseInt(color.substring(1, 2), 16);
b = Integer.parseInt(color.substring(2, 3), 16);
return new Color(r / 15f, g / 15f, b / 15f);
} else if (len == 4) {
r = Integer.parseInt(color.substring(0, 1), 16);
g = Integer.parseInt(color.substring(1, 2), 16);
b = Integer.parseInt(color.substring(2, 3), 16);
return new Color(r / 15f, g / 15f, b / 15f);
} else if (len == 6) {
r = Integer.parseInt(color.substring(0, 2), 16);
g = Integer.parseInt(color.substring(2, 4), 16);
b = Integer.parseInt(color.substring(4, 6), 16);
return new Color(r, g, b);
} else if (len == 8) {
r = Integer.parseInt(color.substring(0, 2), 16);
g = Integer.parseInt(color.substring(2, 4), 16);
b = Integer.parseInt(color.substring(4, 6), 16);
return new Color(r, g, b);
}
} catch (NumberFormatException nfe) {}
throw new IllegalArgumentException("Invalid color specification");
}
}

View File

@ -0,0 +1,134 @@
package com.boydti.fawe.util;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DelegateTextureUtil extends TextureUtil {
private final TextureUtil parent;
public DelegateTextureUtil(TextureUtil parent) throws FileNotFoundException {
super(parent.getFolder());
this.parent = parent;
}
@Override
public BlockTypes getNearestBlock(int color) {
return parent.getNearestBlock(color);
}
@Override
public BlockType getNearestBlock(BlockType block) {
return parent.getNearestBlock(block);
}
@Override
public BlockType getNextNearestBlock(int color) {
return parent.getNextNearestBlock(color);
}
@Override
public BlockTypes[] getNearestLayer(int color) {
return parent.getNearestLayer(color);
}
@Override
public BlockType getLighterBlock(BlockType block) {
return parent.getLighterBlock(block);
}
@Override
public BlockType getDarkerBlock(BlockType block) {
return parent.getDarkerBlock(block);
}
@Override
public int getColor(BlockType block) {
return parent.getColor(block);
}
@Override
public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) {
return parent.getIsBlockCloserThanBiome(blockAndBiomeIdOutput, color, biomePriority);
}
@Override
public int getBiomeMix(int[] biomeIdsOutput, int color) {
return parent.getBiomeMix(biomeIdsOutput, color);
}
@Override
public BiomeColor getBiome(int biome) {
return parent.getBiome(biome);
}
@Override
public BiomeColor getNearestBiome(int color) {
return parent.getNearestBiome(color);
}
@Override
public File getFolder() {
return parent.getFolder();
}
@Override
public int combineTransparency(int top, int bottom) {
return parent.combineTransparency(top, bottom);
}
@Override
public void calculateLayerArrays() {
parent.calculateLayerArrays();
}
@Override
public void loadModTextures() throws IOException {
parent.loadModTextures();
}
@Override
public int multiplyColor(int c1, int c2) {
return parent.multiplyColor(c1, c2);
}
@Override
public BlockType getNearestBlock(BlockType block, boolean darker) {
return parent.getNearestBlock(block, darker);
}
@Override
public BlockType getNearestBlock(int color, boolean darker) {
return parent.getNearestBlock(color, darker);
}
@Override
public boolean hasAlpha(int color) {
return parent.hasAlpha(color);
}
@Override
public long colorDistance(int c1, int c2) {
return parent.colorDistance(c1, c2);
}
@Override
public long colorDistance(int red1, int green1, int blue1, int c2) {
return parent.colorDistance(red1, green1, blue1, c2);
}
public static int hueDistance(int red1, int green1, int blue1, int red2, int green2, int blue2) {
return TextureUtil.hueDistance(red1, green1, blue1, red2, green2, blue2);
}
@Override
public long getDistance(BufferedImage image, int c1) {
return parent.getDistance(image, c1);
}
}

View File

@ -0,0 +1,212 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.boydti.fawe.util;
import com.boydti.fawe.command.AnvilCommands;
import com.boydti.fawe.command.CFICommands;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandPermissions;
import com.sk89q.minecraft.util.commands.NestedCommand;
import com.sk89q.worldedit.command.BiomeCommands;
import com.sk89q.worldedit.command.BrushCommands;
import com.sk89q.worldedit.command.BrushOptionsCommands;
import com.sk89q.worldedit.command.ChunkCommands;
import com.sk89q.worldedit.command.ClipboardCommands;
import com.sk89q.worldedit.command.GenerationCommands;
import com.sk89q.worldedit.command.HistoryCommands;
import com.sk89q.worldedit.command.MaskCommands;
import com.sk89q.worldedit.command.NavigationCommands;
import com.sk89q.worldedit.command.OptionsCommands;
import com.sk89q.worldedit.command.PatternCommands;
import com.sk89q.worldedit.command.RegionCommands;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.ScriptingCommands;
import com.sk89q.worldedit.command.SelectionCommands;
import com.sk89q.worldedit.command.SnapshotCommands;
import com.sk89q.worldedit.command.SnapshotUtilCommands;
import com.sk89q.worldedit.command.SuperPickaxeCommands;
import com.sk89q.worldedit.command.ToolCommands;
import com.sk89q.worldedit.command.TransformCommands;
import com.sk89q.worldedit.command.UtilityCommands;
import com.sk89q.worldedit.command.WorldEditCommands;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public final class DocumentationPrinter {
private DocumentationPrinter() {
}
/**
* Generates documentation.
*
* @param args arguments
* @throws IOException thrown on I/O error
*/
public static void main(String[] args) throws IOException {
writePermissionsWikiTable();
}
private static void writePermissionsWikiTable()
throws IOException {
try (FileOutputStream fos = new FileOutputStream("wiki_permissions.md")) {
PrintStream stream = new PrintStream(fos);
stream.print("## Overview\n");
stream.print("This page is generated from the source. " +
"Click one of the edit buttons below to modify a command class. " +
"You will need to find the parts which correspond to the documentation. " +
"Command documentation will be consistent with what is available ingame");
stream.println();
stream.println();
stream.print("To view this information ingame use `//help [category|command]`\n");
stream.print("## Command Syntax \n");
stream.print(" - `<arg>` - A required parameter \n");
stream.print(" - `[arg]` - An optional parameter \n");
stream.print(" - `<arg1|arg2>` - Multiple parameters options \n");
stream.print(" - `<arg=value>` - Default or suggested value \n");
stream.print(" - `-a` - A command flag e.g. `//<command> -a [flag-value]`");
stream.println();
stream.print("## See also\n");
stream.print(" - [Masks](https://github.com/boy0001/FastAsyncWorldedit/wiki/WorldEdit---FAWE-mask-list)\n");
stream.print(" - [Patterns](https://github.com/boy0001/FastAsyncWorldedit/wiki/WorldEdit-and-FAWE-patterns)\n");
stream.print(" - [Transforms](https://github.com/boy0001/FastAsyncWorldedit/wiki/Transforms)\n");
stream.println();
stream.print("## Content");
stream.println();
stream.print("Click on a category to go to the list of commands, or `More Info` for detailed descriptions ");
stream.println();
StringBuilder builder = new StringBuilder();
writePermissionsWikiTable(stream, builder, "/we ", WorldEditCommands.class);
writePermissionsWikiTable(stream, builder, "/", UtilityCommands.class);
writePermissionsWikiTable(stream, builder, "/", RegionCommands.class);
writePermissionsWikiTable(stream, builder, "/", SelectionCommands.class);
writePermissionsWikiTable(stream, builder, "/", HistoryCommands.class);
writePermissionsWikiTable(stream, builder, "/schematic ", SchematicCommands.class);
writePermissionsWikiTable(stream, builder, "/", ClipboardCommands.class);
writePermissionsWikiTable(stream, builder, "/", GenerationCommands.class);
writePermissionsWikiTable(stream, builder, "/", BiomeCommands.class);
writePermissionsWikiTable(stream, builder, "/anvil ", AnvilCommands.class);
writePermissionsWikiTable(stream, builder, "/sp ", SuperPickaxeCommands.class);
writePermissionsWikiTable(stream, builder, "/", NavigationCommands.class);
writePermissionsWikiTable(stream, builder, "/snapshot", SnapshotCommands.class);
writePermissionsWikiTable(stream, builder, "/", SnapshotUtilCommands.class);
writePermissionsWikiTable(stream, builder, "/", ScriptingCommands.class);
writePermissionsWikiTable(stream, builder, "/", ChunkCommands.class);
writePermissionsWikiTable(stream, builder, "/", OptionsCommands.class);
writePermissionsWikiTable(stream, builder, "/", BrushOptionsCommands.class);
writePermissionsWikiTable(stream, builder, "/tool ", ToolCommands.class);
writePermissionsWikiTable(stream, builder, "/brush ", BrushCommands.class);
writePermissionsWikiTable(stream, builder, "", MaskCommands.class, "/Masks");
writePermissionsWikiTable(stream, builder, "", PatternCommands.class, "/Patterns");
writePermissionsWikiTable(stream, builder, "", TransformCommands.class, "/Transforms");
writePermissionsWikiTable(stream, builder, "/cfi ", CFICommands.class, "Create From Image");
stream.println();
stream.print("#### Uncategorized\n");
stream.append("| Aliases | Permission | flags | Usage |\n");
stream.append("| --- | --- | --- | --- |\n");
stream.append("| //cancel | fawe.cancel | | Cancels your current operations |\n");
stream.append("| /plot replaceall | plots.replaceall | | Replace all blocks in the plot world |\n");
// stream.append("| /plot createfromimage | plots.createfromimage | | Starts world creation from a heightmap image: [More Info](https://github.com/boy0001/FastAsyncWorldedit/wiki/CreateFromImage) |\n");
stream.print("\n---\n");
stream.print(builder);
}
}
private static void writePermissionsWikiTable(PrintStream stream, StringBuilder content, String prefix, Class<?> cls) {
writePermissionsWikiTable(stream, content, prefix, cls, getName(cls));
}
public static String getName(Class cls) {
return cls.getSimpleName().replaceAll("(\\p{Ll})(\\p{Lu})", "$1 $2");
}
private static void writePermissionsWikiTable(PrintStream stream, StringBuilder content, String prefix, Class<?> cls, String name) {
stream.print(" - [`" + name + "`](#" + name.replaceAll(" ", "-").replaceAll("/", "").toLowerCase() + "-edittop) ");
Command cmd = cls.getAnnotation(Command.class);
if (cmd != null) {
stream.print(" (" + cmd.desc() + ")");
}
stream.println();
writePermissionsWikiTable(content, prefix, cls, name, true);
}
private static void writePermissionsWikiTable(StringBuilder stream, String prefix, Class<?> cls, String name, boolean title) {
// //setbiome || worldedit.biome.set || //setbiome || p || Sets the biome of the player's current block or region.
if (title) {
String path = "https://github.com/boy0001/FastAsyncWorldedit/edit/master/core/src/main/java/" + cls.getName().replaceAll("\\.", "/") + ".java";
stream.append("### **" + name + "** `[`[`edit`](" + path + ")`|`[`top`](#overview)`]`");
stream.append("\n");
Command cmd = cls.getAnnotation(Command.class);
if (cmd != null) {
if (!cmd.desc().isEmpty()) {
stream.append("> (" + (cmd.desc()) + ") \n");
}
if (!cmd.help().isEmpty()) {
stream.append("" + (cmd.help()) + " \n");
}
}
stream.append("\n");
stream.append("---");
stream.append("\n");
stream.append("\n");
}
for (Method method : cls.getMethods()) {
if (!method.isAnnotationPresent(Command.class)) {
continue;
}
Command cmd = method.getAnnotation(Command.class);
String[] aliases = cmd.aliases();
String usage = prefix + aliases[0] + " " + cmd.usage();
if (!cmd.flags().isEmpty()) {
for (char c : cmd.flags().toCharArray()) {
usage += " [-" + c + "]";
}
}
// stream.append("#### [`" + usage + "`](" + "https://github.com/boy0001/FastAsyncWorldedit/wiki/" + aliases[0] + ")\n");
stream.append("#### `" + usage + "`\n");
if (method.isAnnotationPresent(CommandPermissions.class)) {
CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
stream.append("**Perm**: `" + StringMan.join(perms.value(), "`, `") + "` \n");
}
String help = cmd.help() == null || cmd.help().isEmpty() ? cmd.desc() : cmd.help();
stream.append("**Desc**: " + help.trim().replaceAll("\n", "<br />") + " \n");
if (method.isAnnotationPresent(NestedCommand.class)) {
NestedCommand nested =
method.getAnnotation(NestedCommand.class);
Class<?>[] nestedClasses = nested.value();
for (Class clazz : nestedClasses) {
writePermissionsWikiTable(stream, prefix + cmd.aliases()[0] + " ", clazz, getName(clazz), false);
}
}
}
stream.append("\n");
if (title) stream.append("---");
stream.append("\n");
stream.append("\n");
}
}

View File

@ -0,0 +1,196 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.*;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.FaweChangeSet;
import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.eventbus.EventBus;
import com.sk89q.worldedit.world.World;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class EditSessionBuilder {
private World world;
private String worldName;
private FaweQueue queue;
private FawePlayer player;
private FaweLimit limit;
private FaweChangeSet changeSet;
private Region[] allowedRegions;
private Boolean autoQueue;
private Boolean fastmode;
private Boolean checkMemory;
private Boolean combineStages;
private EventBus eventBus;
private BlockBag blockBag;
private EditSessionEvent event;
/**
* An EditSession builder<br>
* - Unset values will revert to their default<br>
* <br>
* player: The player doing the edit (defaults to to null)<br>
* limit: Block/Entity/Action limit (defaults to unlimited)<br>
* changeSet: Stores changes (defaults to config.yml value)<br>
* allowedRegions: Allowed editable regions (defaults to player's allowed regions, or everywhere)<br>
* autoQueue: Changes can occur before flushQueue() (defaults true)<br>
* fastmode: bypasses history (defaults to player fastmode or config.yml console history)<br>
* checkMemory: If low memory checks are enabled (defaults to player's fastmode or true)<br>
* combineStages: If history is combined with dispatching
*
* @param world A world must be provided for all EditSession(s)
*/
public EditSessionBuilder(@Nonnull World world) {
checkNotNull(world);
this.world = world;
this.worldName = Fawe.imp().getWorldName(world);
}
public EditSessionBuilder(@Nonnull String worldName) {
checkNotNull(worldName);
this.worldName = worldName;
this.world = FaweAPI.getWorld(worldName);
}
public EditSessionBuilder player(@Nullable FawePlayer player) {
this.player = player;
return this;
}
public EditSessionBuilder limit(@Nullable FaweLimit limit) {
this.limit = limit;
return this;
}
public EditSessionBuilder limitUnlimited() {
return limit(FaweLimit.MAX.copy());
}
public EditSessionBuilder limitUnprocessed(@Nonnull FawePlayer fp) {
checkNotNull(fp);
limitUnlimited();
FaweLimit tmp = fp.getLimit();
limit.INVENTORY_MODE = tmp.INVENTORY_MODE;
return this;
}
public EditSessionBuilder changeSet(@Nullable FaweChangeSet changeSet) {
this.changeSet = changeSet;
return this;
}
public EditSessionBuilder changeSetNull() {
return changeSet(world == null ? new NullChangeSet(worldName) : new NullChangeSet(world));
}
public EditSessionBuilder world(@Nonnull World world) {
checkNotNull(world);
this.world = world;
this.worldName = Fawe.imp().getWorldName(world);
return this;
}
/**
* @param disk If it should be stored on disk
* @param uuid The uuid to store it under (if on disk)
* @param compression Compression level (0-9)
* @return
*/
public EditSessionBuilder changeSet(boolean disk, @Nullable UUID uuid, int compression) {
if (world == null) {
if (disk) {
if (Settings.IMP.HISTORY.USE_DATABASE) {
this.changeSet = new RollbackOptimizedHistory(worldName, uuid);
} else {
this.changeSet = new DiskStorageHistory(worldName, uuid);
}
} else {
this.changeSet = new MemoryOptimizedHistory(worldName);
}
} else if (disk) {
if (Settings.IMP.HISTORY.USE_DATABASE) {
this.changeSet = new RollbackOptimizedHistory(world, uuid);
} else {
this.changeSet = new DiskStorageHistory(world, uuid);
}
} else {
this.changeSet = new MemoryOptimizedHistory(world);
}
return this;
}
public EditSessionBuilder allowedRegions(@Nullable Region[] allowedRegions) {
this.allowedRegions = allowedRegions;
return this;
}
@Deprecated
public EditSessionBuilder allowedRegions(@Nullable RegionWrapper[] allowedRegions) {
this.allowedRegions = allowedRegions;
return this;
}
public EditSessionBuilder allowedRegions(@Nullable RegionWrapper allowedRegion) {
this.allowedRegions = allowedRegion == null ? null : allowedRegion.toArray();
return this;
}
public EditSessionBuilder allowedRegionsEverywhere() {
return allowedRegions(new Region[]{RegionWrapper.GLOBAL()});
}
public EditSessionBuilder autoQueue(@Nullable Boolean autoQueue) {
this.autoQueue = autoQueue;
return this;
}
public EditSessionBuilder fastmode(@Nullable Boolean fastmode) {
this.fastmode = fastmode;
return this;
}
public EditSessionBuilder checkMemory(@Nullable Boolean checkMemory) {
this.checkMemory = checkMemory;
return this;
}
public EditSessionBuilder combineStages(@Nullable Boolean combineStages) {
this.combineStages = combineStages;
return this;
}
public EditSessionBuilder queue(@Nullable FaweQueue queue) {
this.queue = queue;
return this;
}
public EditSessionBuilder blockBag(@Nullable BlockBag blockBag) {
this.blockBag = blockBag;
return this;
}
public EditSessionBuilder eventBus(@Nullable EventBus eventBus) {
this.eventBus = eventBus;
return this;
}
public EditSessionBuilder event(@Nullable EditSessionEvent event) {
this.event = event;
return this;
}
public EditSession build() {
return new EditSession(worldName, world, queue, player, limit, changeSet, allowedRegions, autoQueue, fastmode, checkMemory, combineStages, blockBag, eventBus, event);
}
}

View File

@ -0,0 +1,120 @@
package com.boydti.fawe.util;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import java.lang.reflect.Field;
public class ExtentTraverser<T extends Extent> {
private T root;
private ExtentTraverser<T> parent;
public ExtentTraverser(T root) {
this(root, null);
}
public ExtentTraverser(T root, ExtentTraverser<T> parent) {
this.root = root;
this.parent = parent;
}
public boolean exists() {
return root != null;
}
public T get() {
return root;
}
public boolean setNext(T next) {
try {
Field field = AbstractDelegateExtent.class.getDeclaredField("extent");
ReflectionUtils.setFailsafeFieldValue(field, root, next);
return true;
} catch (Throwable e) {
e.printStackTrace();
return false;
}
}
public ExtentTraverser<T> last() {
ExtentTraverser<T> last = this;
ExtentTraverser<T> traverser = this;
while (traverser != null && traverser.get() instanceof AbstractDelegateExtent) {
last = traverser;
traverser = traverser.next();
}
return last;
}
public boolean insert(T extent) {
try {
Field field = AbstractDelegateExtent.class.getDeclaredField("extent");
field.setAccessible(true);
field.set(extent, field.get(root));
field.set(root, extent);
return true;
} catch (Throwable e) {
e.printStackTrace();
return false;
}
}
public <U> U findAndGet(Class<U> clazz) {
ExtentTraverser<Extent> traverser = find((Class) clazz);
return (traverser != null) ? (U) traverser.get() : null;
}
public <U extends Extent> ExtentTraverser<U> find(Class<U> clazz) {
try {
ExtentTraverser<T> value = this;
while (value != null) {
if (clazz.isAssignableFrom(value.root.getClass())) {
return (ExtentTraverser<U>) value;
}
value = value.next();
}
return null;
} catch (Throwable e) {
MainUtil.handleError(e);
return null;
}
}
public <U extends Extent> ExtentTraverser<U> find(Object object) {
try {
ExtentTraverser<T> value = this;
while (value != null) {
if (value.root == object) {
return (ExtentTraverser<U>) value;
}
value = value.next();
}
return null;
} catch (Throwable e) {
MainUtil.handleError(e);
return null;
}
}
public ExtentTraverser<T> previous() {
return parent;
}
public ExtentTraverser<T> next() {
try {
if (root instanceof AbstractDelegateExtent) {
Field field = AbstractDelegateExtent.class.getDeclaredField("extent");
field.setAccessible(true);
T value = (T) field.get(root);
if (value == null) {
return null;
}
return new ExtentTraverser<>(value, this);
}
return null;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,92 @@
package com.boydti.fawe.util;
public class FaweTimer implements Runnable {
private final double[] history = new double[]{20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d, 20d};
private int historyIndex = 0;
private long lastPoll = System.currentTimeMillis();
private long tickStart = System.currentTimeMillis();
private final long tickInterval = 5;
private final double millisPer20Interval = tickInterval * 50 * 20;
private long tick = 0;
private long tickMod = 0;
@Override
public void run() {
tickStart = System.currentTimeMillis();
tick++;
if (++tickMod == tickInterval) {
tickMod = 0;
} else {
return;
}
long timeSpent = (tickStart - lastPoll);
if (timeSpent == 0) {
timeSpent = 1;
}
double tps = millisPer20Interval / timeSpent;
history[historyIndex++] = tps;
if (historyIndex >= history.length) {
historyIndex = 0;
}
lastPoll = tickStart;
}
private long lastGetTPSTick = 0;
private double lastGetTPSValue = 20d;
public double getTPS() {
if (tick < lastGetTPSTick + tickInterval) {
return lastGetTPSValue;
}
double total = 0;
for (double tps : history) {
total += tps;
}
lastGetTPSValue = total / history.length;
lastGetTPSTick = tick;
return lastGetTPSValue;
}
public long getTick() {
return tick;
}
public long getTickMillis() {
return System.currentTimeMillis() - tickStart;
}
public long getTickStart() {
return tickStart;
}
private long skip = 0;
private long skipTick = 0;
public boolean isAbove(double tps) {
if (tps <= 0) {
return true;
}
if (skip > 0) {
if (skipTick != tick) {
skip--;
skipTick = tick;
return true; // Run once per tick
}
return false;
}
if (getTickMillis() > 100 || getTPS() < tps) {
skip = 10;
skipTick = tick;
}
return true;
}
private boolean runIfAbove(Runnable run, double tps) {
if (isAbove(tps)) {
run.run();
return true;
}
return false;
}
}

View File

@ -0,0 +1,44 @@
package com.boydti.fawe.util;
import com.boydti.fawe.FaweCache;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.io.FileNotFoundException;
import java.util.Set;
public class FilteredTextureUtil extends TextureUtil {
private final Set<BlockType> blocks;
public FilteredTextureUtil(TextureUtil parent, Set<BlockType> blocks) throws FileNotFoundException {
super(parent.getFolder());
this.blocks = blocks;
this.validMixBiomeColors = parent.validMixBiomeColors;
this.validMixBiomeIds = parent.validMixBiomeIds;
this.validBiomes = parent.validBiomes;
this.blockColors = parent.blockColors;
this.blockDistance = parent.blockDistance;
this.distances = parent.distances;
this.validColors = new int[distances.length];
this.validBlockIds = new int[distances.length];
int num = 0;
for (int i = 0; i < parent.validBlockIds.length; i++) {
BlockTypes block = BlockTypes.get(parent.validBlockIds[i]);
if (blocks.contains(block)) num++;
}
this.validBlockIds = new int[num];
this.validColors = new int[num];
num = 0;
for (int i = 0; i < parent.validBlockIds.length; i++) {
BlockTypes block = BlockTypes.get(parent.validBlockIds[i]);
if (blocks.contains(block)) {
validBlockIds[num] = parent.validBlockIds[i];
validColors[num++] = parent.validColors[i];
}
}
this.calculateLayerArrays();
}
}

View File

@ -0,0 +1,107 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HastebinUtility {
public static final String BIN_URL = "https://hastebin.com/documents", USER_AGENT = "Mozilla/5.0";
public static final Pattern PATTERN = Pattern.compile("\\{\"key\":\"([\\S\\s]*)\"\\}");
public static String upload(final String string) throws IOException {
final URL url = new URL(BIN_URL);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", USER_AGENT);
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(string.getBytes());
outputStream.flush();
}
StringBuilder response;
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
response = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}
Matcher matcher = PATTERN.matcher(response.toString());
if (matcher.matches()) {
return "https://hastebin.com/" + matcher.group(1);
} else {
throw new RuntimeException("Couldn't read response!");
}
}
public static String upload(final File file) throws IOException {
final StringBuilder content = new StringBuilder();
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
int i = 0;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
}
for (int i = Math.max(0, lines.size() - 1000); i < lines.size(); i++) {
content.append(lines.get(i)).append("\n");
}
return upload(content.toString());
}
public static String debugPaste() throws IOException {
String settingsYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "config.yml"));
String messagesYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "message.yml"));
String commandsYML = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "commands.yml"));
String latestLOG;
try {
latestLOG = HastebinUtility.upload(new File(Fawe.imp().getDirectory(), "../../logs/latest.log"));
} catch (IOException ignored) {
latestLOG = "too big :(";
}
StringBuilder b = new StringBuilder();
b.append(
"# Welcome to this paste\n# It is meant to provide us at IntellectualSites with better information about your "
+ "problem\n\n# We will start with some informational files\n");
b.append("links.config_yml: ").append(settingsYML).append('\n');
b.append("links.messages_yml: ").append(messagesYML).append('\n');
b.append("links.commands_yml: ").append(commandsYML).append('\n');
b.append("links.latest_log: ").append(latestLOG).append('\n');
b.append("\n# Server Information\n");
b.append("server.platform: ").append(Fawe.imp().getPlatform()).append('\n');
b.append(Fawe.imp().getDebugInfo()).append('\n');
b.append("\n\n# YAY! Now, let's see what we can find in your JVM\n");
Runtime runtime = Runtime.getRuntime();
b.append("memory.free: ").append(runtime.freeMemory()).append('\n');
b.append("memory.max: ").append(runtime.maxMemory()).append('\n');
b.append("java.specification.version: '").append(System.getProperty("java.specification.version")).append("'\n");
b.append("java.vendor: '").append(System.getProperty("java.vendor")).append("'\n");
b.append("java.version: '").append(System.getProperty("java.version")).append("'\n");
b.append("os.arch: '").append(System.getProperty("os.arch")).append("'\n");
b.append("os.name: '").append(System.getProperty("os.name")).append("'\n");
b.append("os.version: '").append(System.getProperty("os.version")).append("'\n\n");
b.append("# Okay :D Great. You are now ready to create your bug report!");
b.append("\n# You can do so at https://github.com/boy0001/FastAsyncWorldedit/issues");
String link = HastebinUtility.upload(b.toString());
return link;
}
}

View File

@ -0,0 +1,56 @@
package com.boydti.fawe.util;
import java.io.*;
import java.net.URI;
public final class IOUtil {
public InputStream toInputStream(URI uri) throws IOException {
String scheme = uri.getScheme();
switch (scheme.toLowerCase()) {
case "file":
return new FileInputStream(uri.getPath());
case "http":
case "https":
return uri.toURL().openStream();
default:
return null;
}
}
public static final int readInt(InputStream in) throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
public static final void writeInt(OutputStream out, int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
}
public static void writeVarInt(OutputStream out, int i) throws IOException {
while((i & -128) != 0) {
out.write(i & 127 | 128);
i >>>= 7;
}
out.write(i);
}
public static int readVarInt(InputStream in) throws IOException {
int i = 0;
int offset = 0;
int b;
while ((b = in.read()) > 127) {
i |= (b - 128) << offset;
offset += 7;
}
i |= b << offset;
return i;
}
}

View File

@ -0,0 +1,64 @@
package com.boydti.fawe.util;
import com.boydti.fawe.object.io.FastByteArrayOutputStream;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Base64;
public class ImgurUtility {
public static final String CLIENT_ID = "50e34b65351eb07";
public static URL uploadImage(File file) throws IOException {
return uploadImage(new FileInputStream(file));
}
public static URL uploadImage(InputStream is) throws IOException {
is = new BufferedInputStream(is);
FastByteArrayOutputStream baos = new FastByteArrayOutputStream(Short.MAX_VALUE);
int d;
while ((d = is.read()) != -1) {
baos.write(d);
}
baos.flush();
return uploadImage(baos.toByteArray());
}
public static URL uploadImage(byte[] image) throws IOException {
String json = getImgurContent(CLIENT_ID, image);
Gson gson = new Gson();
JsonObject obj = gson.fromJson(json, JsonObject.class);
JsonObject data = obj.get("data").getAsJsonObject();
String link = data.get("link").getAsString();
return new URL(link);
}
public static String getImgurContent(String clientID, byte[] image) throws IOException {
String imageString = Base64.getEncoder().encodeToString(image);
URL url = new URL("https://api.imgur.com/3/image");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
String data = URLEncoder.encode("image", "UTF-8") + "=" + URLEncoder.encode(imageString, "UTF-8");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", "Client-ID " + clientID);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.connect();
StringBuilder stb = new StringBuilder();
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(data);
wr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
stb.append(line).append("\n");
}
wr.close();
rd.close();
return stb.toString();
}
}

View File

@ -0,0 +1,73 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public enum Jars {
WE_B_6_1_7_2("https://addons.cursecdn.com/files/2431/372/worldedit-bukkit-6.1.7.2.jar",
"CRVJCWGJJ6UK40CTGHXQVK2/3C9BBTOS25FWI0ZHD4S=", 1726340),
VS_B_5_171_0("https://media.forgecdn.net/files/2488/589/VoxelSniper-5.172.0-SNAPSHOT.jar",
"4CTEDDEKCAN/M6R0DHS925++HBSJ/TUYAAKAR4CUWC4=", 3615020),
MM_v1_4_0("https://github.com/InventivetalentDev/MapManager/releases/download/1.4.0-SNAPSHOT/MapManager_v1.4.0-SNAPSHOT.jar",
"AEO5SKBUGN4YJRS8XGGNLBM2QRZPTI1KF0/1W1URTGA=", 163279),
PL_v3_6_0("https://github.com/InventivetalentDev/PacketListenerAPI/releases/download/3.6.0-SNAPSHOT/PacketListenerAPI_v3.6.0-SNAPSHOT.jar",
"OYBE75VIU+NNWHRVREBLDARWA+/TBDQZ1RC562QULBA=", 166508),
;
public final String url;
public final int filesize;
public final String digest;
/**
* @param url
* Where this jar can be found and downloaded
* @param digest
* The SHA-256 hexadecimal digest
* @param filesize
* Size of this jar in bytes
*/
Jars(String url, String digest, int filesize) {
this.url = url;
this.digest = digest.toUpperCase();
this.filesize = filesize;
}
/** download a jar, verify hash, return byte[] containing the jar */
public byte[] download() throws IOException {
byte[] jarBytes = new byte[this.filesize];
URL url = new URL(this.url);
try (DataInputStream dis = new DataInputStream(url.openConnection().getInputStream());) {
dis.readFully(jarBytes);
if (dis.read() != -1) { // assert that we've read everything
throw new IllegalStateException("downloaded jar is longer than expected");
}
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] jarDigestBytes = md.digest(jarBytes);
String jarDigest = Base64.getEncoder().encodeToString(jarDigestBytes).toUpperCase();
if (this.digest.equals(jarDigest)) {
Fawe.debug("++++ HASH CHECK ++++");
Fawe.debug(this.url);
Fawe.debug(this.digest);
return jarBytes;
} else {
Fawe.debug(jarDigest + " | " + url);
throw new IllegalStateException("downloaded jar does not match the hash");
}
} catch (NoSuchAlgorithmException e) {
// Shouldn't ever happen, Minecraft won't even run on such a JRE
throw new IllegalStateException("Your JRE does not support SHA-256");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
package com.boydti.fawe.util;
import com.boydti.fawe.object.mask.ResettableMask;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import java.lang.reflect.Field;
import java.util.Collection;
public class MaskTraverser {
private final Mask mask;
public MaskTraverser(Mask start) {
this.mask = start;
}
public void reset(Extent newExtent) {
reset(mask, newExtent);
}
private void reset(Mask mask, Extent newExtent) {
if (mask == null) {
return;
}
if (mask instanceof ResettableMask) {
((ResettableMask) mask).reset();
}
Class<?> current = mask.getClass();
while (current.getSuperclass() != null) {
try {
Field field = current.getDeclaredField("extent");
field.setAccessible(true);
field.set(mask, newExtent);
} catch (NoSuchFieldException | IllegalAccessException ignore) {
}
try {
Field field = current.getDeclaredField("mask");
field.setAccessible(true);
Mask next = (Mask) field.get(mask);
reset(next, newExtent);
} catch (NoSuchFieldException | IllegalAccessException ignore) {
}
try {
Field field = current.getDeclaredField("masks");
field.setAccessible(true);
Collection<Mask> masks = (Collection<Mask>) field.get(mask);
for (Mask next : masks) {
reset(next, newExtent);
}
} catch (NoSuchFieldException | IllegalAccessException ignore) {
}
current = current.getSuperclass();
}
}
}

View File

@ -0,0 +1,417 @@
package com.boydti.fawe.util;
public class MathMan {
/**
* Optimized for i elem 0,65536 (characters)
* @param i
* @return square root
*/
public static int usqrt(int i) {
if (i < 65536) {
return CachedMathMan.usqrt(i);
}
return (int) Math.round(Math.sqrt(i));
}
public static float sinInexact(double paramFloat) {
return CachedMathMan.sinInexact(paramFloat);
}
public static float cosInexact(double paramFloat) {
return CachedMathMan.cosInexact(paramFloat);
}
public static int log2nlz( int bits ) {
return Integer.SIZE - Integer.numberOfLeadingZeros(bits);
}
public static int floorZero(double d0) {
int i = (int) d0;
return d0 < (double) i ? i - 1 : i;
}
public static double max(double... values) {
double max = Double.MIN_VALUE;
for (double d : values) {
if (d > max) {
max = d;
}
}
return max;
}
public static int max(int... values) {
int max = Integer.MIN_VALUE;
for (int d : values) {
if (d > max) {
max = d;
}
}
return max;
}
public static int min(int... values) {
int min = Integer.MAX_VALUE;
for (int d : values) {
if (d < min) {
min = d;
}
}
return min;
}
public static double min(double... values) {
double min = Double.MAX_VALUE;
for (double d : values) {
if (d < min) {
min = d;
}
}
return min;
}
public static int ceilZero(float floatNumber) {
int floor = (int) floatNumber;
return floatNumber > (float) floor ? floor + 1 : floor;
}
public static int sqr(int val) {
return val * val;
}
public static int clamp(int check, int min, int max) {
return check > max ? max : (check < min ? min : check);
}
public static float clamp(float check, float min, float max) {
return check > max ? max : (check < min ? min : check);
}
public static double hypot(final double... pars) {
double sum = 0;
for (final double d : pars) {
sum += Math.pow(d, 2);
}
return Math.sqrt(sum);
}
public static double hypot2(final double... pars) {
double sum = 0;
for (final double d : pars) {
sum += Math.pow(d, 2);
}
return sum;
}
public static final int wrap(int value, int min, int max) {
if (max < min) {
return value;
}
if (min == max) {
return min;
}
int diff = max - min + 1;
if (value < min) {
return max - ((min - value) % diff);
} else if (value > max) {
return min + ((value - min) % diff);
} else {
return value;
}
}
public static final long inverseRound(double val) {
long round = Math.round(val);
return (long) (round + Math.signum(val - round));
}
public static final int pair(short x, short y) {
return (x << 16) | (y & 0xFFFF);
}
public static final short unpairX(int hash) {
return (short) (hash >> 16);
}
public static final short unpairY(int hash) {
return (short) (hash & 0xFFFF);
}
public static final short pairByte(int x, int y) {
return (short) ((x << 8) | (y & 0xFF));
}
public static final byte unpairShortX(short pair) {
return (byte) (pair >> 8);
}
public static final byte unpairShortY(short pair) {
return (byte) pair;
}
public static final long pairInt(int x, int y) {
return (((long) x) << 32) | (y & 0xffffffffL);
}
public static final long tripleWorldCoord(int x, int y, int z) {
return y + (((long) x & 0x3FFFFFF) << 8) + (((long) z & 0x3FFFFFF) << 34);
}
public static final long untripleWorldCoordX(long triple) {
return (((triple >> 8) & 0x3FFFFFF) << 38) >> 38;
}
public static final long untripleWorldCoordY(long triple) {
return triple & 0xFF;
}
public static final long untripleWorldCoordZ(long triple) {
return (((triple >> 34) & 0x3FFFFFF) << 38) >> 38;
}
public static final short tripleBlockCoord(int x, int y, int z) {
return (short) ((x & 15) << 12 | (z & 15) << 8 | y);
}
public static final char tripleBlockCoordChar(int x, int y, int z) {
return (char) ((x & 15) << 12 | (z & 15) << 8 | y);
}
public static final int untripleBlockCoordX(int triple) {
return (triple >> 12) & 0xF;
}
public static final int untripleBlockCoordY(int triple) {
return (triple & 0xFF);
}
public static final int untripleBlockCoordZ(int triple) {
return (triple >> 8) & 0xF;
}
public static int tripleSearchCoords(int x, int y, int z) {
byte b1 = (byte) y;
byte b3 = (byte) (x);
byte b4 = (byte) (z);
int x16 = (x >> 8) & 0x7;
int z16 = (z >> 8) & 0x7;
byte b2 = MathMan.pair8(x16, z16);
return ((b1 & 0xFF)
+ ((b2 & 0x7F) << 8)
+ ((b3 & 0xFF) << 15)
+ ((b4 & 0xFF) << 23))
;
}
public static int pairSearchCoords(int x, int y) {
byte b1 = (byte) ((x & 0xF) + ((y & 0xF) << 4));
byte b2 = (byte) ((x >> 4) & 0xFF);
byte b3 = (byte) ((y >> 4) & 0xFF);
int x16 = (x >> 12) & 0xF;
int y16 = (y >> 12) & 0xF;
byte b4 = (byte) ((x16 & 0xF) + ((y16 & 0xF) << 4));
return ((b1 & 0xFF)
+ ((b2 & 0xFF) << 8)
+ ((b3 & 0xFF) << 16)
+ ((b4 & 0xFF) << 24));
}
public static int unpairSearchCoordsX(int pair) {
int x1 = (pair >> 24) & 0x7;
int x2 = (pair >> 8) & 0xFF;
int x3 = (pair & 0xF);
return x3 + (x2 << 4) + (x1 << 12);
}
public static int unpairSearchCoordsY(int pair) {
int y1 = ((pair >> 24) & 0x7F) >> 3;
int y2 = (pair >> 16) & 0xFF;
int y3 = (pair & 0xFF) >> 4;
return y3 + (y2 << 4) + (y1 << 12);
}
public static final long chunkXZ2Int(int x, int z) {
return (long) x & 4294967295L | ((long) z & 4294967295L) << 32;
}
public static final int unpairIntX(long pair) {
return (int) (pair >> 32);
}
public static final int unpairIntY(long pair) {
return (int) pair;
}
public static final byte pair16(int x, int y) {
return (byte) (x + (y << 4));
}
public static final byte unpair16x(byte value) {
return (byte) (value & 0xF);
}
public static final byte unpair16y(byte value) {
return (byte) ((value >> 4) & 0xF);
}
public static final byte pair8(int x, int y) {
return (byte) (x + (y << 3));
}
public static byte unpair8x(int value) {
return (byte) (value & 0x7);
}
public static byte unpair8y(int value) {
return (byte) ((value >> 3) & 0x7F);
}
public static final int lossyFastDivide(int a, int b) {
return (a * ((1 << 16) / b)) >> 16;
}
public static final int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
public static final int gcd(int[] a) {
int result = a[0];
for (int i = 1; i < a.length; i++) {
result = gcd(result, a[i]);
}
return result;
}
public static final double getMean(int[] array) {
double count = 0;
for (int i : array) {
count += i;
}
return count / array.length;
}
public static final double getMean(double[] array) {
double count = 0;
for (double i : array) {
count += i;
}
return count / array.length;
}
/**
* Returns [x, y, z]
*
* @param yaw
* @param pitch
* @return
*/
public static final float[] getDirection(float yaw, float pitch) {
double pitch_sin = Math.sin(pitch);
return new float[]{(float) (pitch_sin * Math.cos(yaw)), (float) (pitch_sin * Math.sin(yaw)), (float) Math.cos(pitch)};
}
public static final int roundInt(double value) {
return (int) (value < 0 ? (value == (int) value) ? value : value - 1 : value);
}
/**
* Returns [ pitch, yaw ]
*
* @param x
* @param y
* @param z
* @return
*/
public static final float[] getPitchAndYaw(float x, float y, float z) {
float distance = sqrtApprox((z * z) + (x * x));
return new float[]{atan2(y, distance), atan2(x, z)};
}
public static final float atan2(float y, float x) {
return CachedMathMan.atan2(y, x);
}
public static final float sqrtApprox(float f) {
return f * Float.intBitsToFloat(0x5f375a86 - (Float.floatToIntBits(f) >> 1));
}
public static final double sqrtApprox(double d) {
return Double.longBitsToDouble(((Double.doubleToLongBits(d) - (1l << 52)) >> 1) + (1l << 61));
}
public static final float invSqrt(float x) {
float xhalf = 0.5f * x;
int i = Float.floatToIntBits(x);
i = 0x5f3759df - (i >> 1);
x = Float.intBitsToFloat(i);
x = x * (1.5f - (xhalf * x * x));
return x;
}
public static final boolean isInteger(String str) {
if (str == null) {
return false;
}
int length = str.length();
if (length == 0) {
return false;
}
int i = 0;
if (str.charAt(0) == '-') {
if (length == 1) {
return false;
}
i = 1;
}
for (; i < length; i++) {
char c = str.charAt(i);
if ((c <= '/') || (c >= ':')) {
return false;
}
}
return true;
}
public static final double getSD(double[] array, double av) {
double sd = 0;
for (double element : array) {
sd += Math.pow(Math.abs(element - av), 2);
}
return Math.sqrt(sd / array.length);
}
public static final double getSD(int[] array, double av) {
double sd = 0;
for (int element : array) {
sd += Math.pow(Math.abs(element - av), 2);
}
return Math.sqrt(sd / array.length);
}
public static final int absByte(int value) {
return (value ^ (value >> 8)) - (value >> 8);
}
public static final int mod(int x, int y) {
if (isPowerOfTwo(y)) {
return x & (y - 1);
}
return x % y;
}
public static final int unsignedmod(int x, int y) {
if (isPowerOfTwo(y)) {
return x & (y - 1);
}
return x % y;
}
public static final boolean isPowerOfTwo(int x) {
return (x & (x - 1)) == 0;
}
}

View File

@ -0,0 +1,82 @@
package com.boydti.fawe.util;
import com.boydti.fawe.config.Settings;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class MemUtil {
private static AtomicBoolean memory = new AtomicBoolean(false);
public static boolean isMemoryFree() {
return !memory.get();
}
public static boolean isMemoryLimited() {
return memory.get();
}
public static boolean isMemoryLimitedSlow() {
if (memory.get()) {
System.gc();
System.gc();
calculateMemory();
return memory.get();
}
return false;
}
public static long getUsedBytes() {
long used = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
return used;
}
public static long getFreeBytes() {
return Runtime.getRuntime().maxMemory() - getUsedBytes();
}
public static int calculateMemory() {
final long heapSize = Runtime.getRuntime().totalMemory();
final long heapMaxSize = Runtime.getRuntime().maxMemory();
if (heapSize < heapMaxSize) {
return Integer.MAX_VALUE;
}
final long heapFreeSize = Runtime.getRuntime().freeMemory();
final int size = (int) ((heapFreeSize * 100) / heapMaxSize);
if (size > (100 - Settings.IMP.MAX_MEMORY_PERCENT)) {
memoryPlentifulTask();
return Integer.MAX_VALUE;
}
return size;
}
private static Queue<Runnable> memoryLimitedTasks = new ConcurrentLinkedQueue<>();
private static Queue<Runnable> memoryPlentifulTasks = new ConcurrentLinkedQueue<>();
public static void addMemoryLimitedTask(Runnable run) {
if (run != null)
memoryLimitedTasks.add(run);
}
public static void addMemoryPlentifulTask(Runnable run) {
if (run != null)
memoryPlentifulTasks.add(run);
}
public static void memoryLimitedTask() {
System.gc();
System.gc();
for (Runnable task : memoryLimitedTasks) {
task.run();
}
memory.set(true);
}
public static void memoryPlentifulTask() {
for (Runnable task : memoryPlentifulTasks) {
task.run();
}
memory.set(false);
}
}

View File

@ -0,0 +1,44 @@
package com.boydti.fawe.util;
import com.boydti.fawe.object.FawePlayer;
public enum Perm {
/*
* Permission related functions
*/
ADMIN("fawe.admin", "admin");
public String s;
public String cat;
Perm(final String perm, final String cat) {
this.s = perm;
this.cat = cat;
}
public boolean has(final FawePlayer<?> player) {
return this.hasPermission(player, this);
}
public boolean hasPermission(final FawePlayer<?> player, final Perm perm) {
return hasPermission(player, perm.s);
}
public static boolean hasPermission(final FawePlayer<?> player, final String perm) {
if ((player == null) || player.hasPermission(ADMIN.s)) {
return true;
}
if (player.hasPermission(perm)) {
return true;
}
final String[] nodes = perm.split("\\.");
final StringBuilder n = new StringBuilder();
for (int i = 0; i < (nodes.length - 1); i++) {
n.append(nodes[i] + ("."));
if (player.hasPermission(n + "*")) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,100 @@
package com.boydti.fawe.util;
import com.boydti.fawe.object.PseudoRandom;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileNotFoundException;
public class RandomTextureUtil extends CachedTextureUtil {
public RandomTextureUtil(TextureUtil parent) throws FileNotFoundException {
super(parent);
}
private int index;
private int[] biomeMixBuffer = new int[3];
private Int2ObjectOpenHashMap<Integer> offsets = new Int2ObjectOpenHashMap<>();
private Int2ObjectOpenHashMap<int[]> biomeMixes = new Int2ObjectOpenHashMap<>();
protected int addRandomColor(int c1, int c2) {
int red1 = (c1 >> 16) & 0xFF;
int green1 = (c1 >> 8) & 0xFF;
int blue1 = (c1 >> 0) & 0xFF;
byte red2 = (byte) ((c2 >> 16));
byte green2 = (byte) ((c2 >> 8));
byte blue2 = (byte) ((c2 >> 0));
int red = MathMan.clamp(red1 + random(red2), 0, 255);
int green = MathMan.clamp(green1 + random(green2), 0, 255);
int blue = MathMan.clamp(blue1 + random(blue2), 0, 255);
return (red << 16) + (green << 8) + (blue << 0) + (255 << 24);
}
private int random(int i) {
if (i < 0) {
return -PseudoRandom.random.nextInt((-i));
} else {
return PseudoRandom.random.nextInt(i);
}
}
@Override
public boolean getIsBlockCloserThanBiome(int[] blockAndBiomeIdOutput, int color, int biomePriority) {
BlockType block = getNearestBlock(color);
int[] mix = biomeMixes.getOrDefault(color, null);
if (mix == null) {
int average = getBiomeMix(biomeMixBuffer, color);
mix = new int[4];
System.arraycopy(biomeMixBuffer, 0, mix, 0, 3);
mix[3] = average;
biomeMixes.put(color, mix);
}
if (++index > 2) index = 0;
int biomeId = mix[index];
int biomeAvColor = mix[3];
int blockColor = getColor(block);
blockAndBiomeIdOutput[0] = block.getInternalId();
blockAndBiomeIdOutput[1] = biomeId;
if (colorDistance(biomeAvColor, color) - biomePriority > colorDistance(blockColor, color)) {
return true;
}
return false;
}
@Override
public BiomeColor getNearestBiome(int color) {
int[] mix = biomeMixes.getOrDefault(color, null);
if (mix == null) {
int average = getBiomeMix(biomeMixBuffer, color);
mix = new int[4];
System.arraycopy(biomeMixBuffer, 0, mix, 0, 3);
mix[3] = average;
biomeMixes.put(color, mix);
}
if (++index > 2) index = 0;
int biomeId = mix[index];
return getBiome(biomeId);
}
@Override
public BlockTypes getNearestBlock(int color) {
int offsetColor = offsets.getOrDefault(color, 0);
if (offsetColor != 0) {
offsetColor = addRandomColor(color, offsetColor);
} else {
offsetColor = color;
}
BlockTypes res = super.getNearestBlock(offsetColor);
if (res == null) return null;
int newColor = getColor(res);
{
byte dr = (byte) (((color >> 16) & 0xFF) - ((newColor >> 16) & 0xFF));
byte dg = (byte) (((color >> 8) & 0xFF) - ((newColor >> 8) & 0xFF));
byte db = (byte) (((color >> 0) & 0xFF) - ((newColor >> 0) & 0xFF));
offsets.put(color, (Integer) ((dr << 16) + (dg << 8) + (db << 0)));
}
return res;
}
}

View File

@ -0,0 +1,855 @@
package com.boydti.fawe.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
/**
* @author DPOH-VAR
* @version 1.0
*/
@SuppressWarnings({"UnusedDeclaration", "rawtypes"})
public class ReflectionUtils {
public static <T> T as(Class<T> t, Object o) {
return t.isInstance(o) ? t.cast(o) : null;
}
@SuppressWarnings("unchecked")
public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName) {
return addEnum(enumType, enumName, new Class<?>[]{} , new Object[]{});
}
public static <T extends Enum<?>> T addEnum(Class<T> enumType, String enumName, Class<?>[] additionalTypes, Object[] additionalValues) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType)) {
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
}
// 1. Lookup "$VALUES" holder in enum class and get previous enum instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[]{valuesField}, true);
try {
// 2. Copy it
T[] previousValues = (T[]) valuesField.get(enumType);
List values = new ArrayList(Arrays.asList(previousValues));
// 3. build new enum
T newValue = (T) makeEnum(enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(),
additionalTypes, // can be used to pass values to the enum constuctor
additionalValues); // can be used to pass values to the enum constuctor
// 4. add new value
values.add(newValue);
// 5. Set new values field
setFailsafeFieldValue(valuesField, null,
values.toArray((T[]) Array.newInstance(enumType, 0)));
// 6. Clean enum cache
cleanEnumCache(enumType);
return newValue;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
public static <T extends Enum<?>> void clearEnum(Class<T> enumType) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType)) {
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
}
// 1. Lookup "$VALUES" holder in enum class and get previous enum instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[]{valuesField}, true);
try {
setFailsafeFieldValue(valuesField, null, Array.newInstance(enumType, 0));
// 6. Clean enum cache
cleanEnumCache(enumType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
public static <T extends Enum<?>> void copyEnum(T dest, String value, Class<?>[] additionalTypes, Object[] additionalValues) {
try {
Class<? extends Enum> clazz = dest.getClass();
Object newEnum = makeEnum(clazz, value, dest.ordinal(), additionalTypes, additionalValues);
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) continue;
field.setAccessible(true);
Object newValue = field.get(newEnum);
setField(field, dest, newValue);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public static Object makeEnum(Class<?> enumClass, String value, int ordinal,
Class<?>[] additionalTypes, Object[] additionalValues) throws Exception {
Object[] parms = new Object[additionalValues.length + 2];
parms[0] = value;
parms[1] = Integer.valueOf(ordinal);
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}
private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass,
Class<?>[] additionalParameterTypes) throws NoSuchMethodException {
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
parameterTypes[0] = String.class;
parameterTypes[1] = int.class;
System.arraycopy(additionalParameterTypes, 0,
parameterTypes, 2, additionalParameterTypes.length);
return ReflectionFactory.getReflectionFactory().newConstructorAccessor(enumClass.getDeclaredConstructor(parameterTypes));
}
public static void setFailsafeFieldValue(Field field, Object target, Object value)
throws NoSuchFieldException, IllegalAccessException {
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
// blank out the final bit in the modifiers int
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
try {
FieldAccessor fa = ReflectionFactory.getReflectionFactory().newFieldAccessor(field, false);
fa.set(target, value);
} catch (NoSuchMethodError error) {
field.set(target, value);
}
}
private static void blankField(Class<?> enumClass, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[]{field}, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
private static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException {
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static Class<?> UNMODIFIABLE_MAP = Collections.unmodifiableMap(Collections.EMPTY_MAP).getClass();
public static <T, V> Map<T, V> getMap(Map<T, V> map) {
try {
Class<? extends Map> clazz = map.getClass();
if (clazz != UNMODIFIABLE_MAP) return map;
Field m = clazz.getDeclaredField("m");
m.setAccessible(true);
return (Map<T, V>) m.get(map);
} catch (Throwable e) {
MainUtil.handleError(e);
return map;
}
}
public static <T> List<T> getList(List<T> list) {
try {
Class<? extends List> clazz = (Class<? extends List>) Class.forName("java.util.Collections$UnmodifiableList");
if (!clazz.isInstance(list)) return list;
Field m = clazz.getDeclaredField("list");
m.setAccessible(true);
return (List<T>) m.get(list);
} catch (Throwable e) {
MainUtil.handleError(e);
return list;
}
}
public static Object getHandle(final Object wrapper) {
final Method getHandle = makeMethod(wrapper.getClass(), "getHandle");
return callMethod(getHandle, wrapper);
}
//Utils
public static Method makeMethod(final Class<?> clazz, final String methodName, final Class<?>... paramaters) {
try {
return clazz.getDeclaredMethod(methodName, paramaters);
} catch (final NoSuchMethodException ex) {
return null;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> T callMethod(final Method method, final Object instance, final Object... paramaters) {
if (method == null) {
throw new RuntimeException("No such method");
}
method.setAccessible(true);
try {
return (T) method.invoke(instance, paramaters);
} catch (final InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> Constructor<T> makeConstructor(final Class<?> clazz, final Class<?>... paramaterTypes) {
try {
return (Constructor<T>) clazz.getConstructor(paramaterTypes);
} catch (final NoSuchMethodException ex) {
return null;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
public static <T> T callConstructor(final Constructor<T> constructor, final Object... paramaters) {
if (constructor == null) {
throw new RuntimeException("No such constructor");
}
constructor.setAccessible(true);
try {
return constructor.newInstance(paramaters);
} catch (final InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
public static Field makeField(final Class<?> clazz, final String name) {
try {
return clazz.getDeclaredField(name);
} catch (final NoSuchFieldException ex) {
return null;
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
public static Field findField(final Class<?> clazz, final Class<?> type, int hasMods, int noMods) {
for (Field field : clazz.getDeclaredFields()) {
if (type == null || type.isAssignableFrom(field.getType())) {
int mods = field.getModifiers();
if ((mods & hasMods) == hasMods && (mods & noMods) == 0) {
return setAccessible(field);
}
}
}
return null;
}
public static Field findField(final Class<?> clazz, final Class<?> type) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type) {
return setAccessible(field);
}
}
return null;
}
public static Method findMethod(final Class<?> clazz, final Class<?> returnType, Class... params) {
return findMethod(clazz, 0, returnType, params);
}
public static Method findMethod(final Class<?> clazz, int index, int hasMods, int noMods, final Class<?> returnType, Class... params) {
outer:
for (Method method : sortMethods(clazz.getDeclaredMethods())) {
if (returnType == null || method.getReturnType() == returnType) {
Class<?>[] mp = method.getParameterTypes();
int mods = method.getModifiers();
if ((mods & hasMods) != hasMods || (mods & noMods) != 0) continue;
if (params == null) {
if (index-- == 0) return setAccessible(method);
else {
continue;
}
}
if (mp.length == params.length) {
for (int i = 0; i < mp.length; i++) {
if (mp[i] != params[i]) continue outer;
}
if (index-- == 0) return setAccessible(method);
else {
continue;
}
}
}
}
return null;
}
public static Method[] sortMethods(Method[] methods) {
Arrays.sort(methods, (o1, o2) -> o1.getName().compareTo(o2.getName()));
return methods;
}
public static Field[] sortFields(Field[] fields) {
Arrays.sort(fields, (o1, o2) -> o1.getName().compareTo(o2.getName()));
return fields;
}
public static Method findMethod(final Class<?> clazz, int index, final Class<?> returnType, Class... params) {
return findMethod(clazz, index, 0, 0, returnType, params);
}
public static <T extends AccessibleObject> T setAccessible(final T ao) {
ao.setAccessible(true);
return ao;
}
@SuppressWarnings("unchecked")
public static <T> T getField(final Field field, final Object instance) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
return (T) field.get(instance);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
public static void setField(String fieldName, Object instance, Object value) {
try {
Field field = instance.getClass().getDeclaredField(fieldName);
setField(field, instance, value);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
public static void setField(final Field field, final Object instance, final Object value) {
if (field == null) {
throw new RuntimeException("No such field");
}
field.setAccessible(true);
try {
field.set(instance, value);
} catch (final Exception ex) {
throw new RuntimeException(ex);
}
}
public static Class<?> getClass(final String name) {
try {
return Class.forName(name);
} catch (final ClassNotFoundException ex) {
return null;
}
}
public static <T> Class<? extends T> getClass(final String name, final Class<T> superClass) {
try {
return Class.forName(name).asSubclass(superClass);
} catch (ClassCastException | ClassNotFoundException ex) {
return null;
}
}
/**
* get RefClass object by real class
*
* @param clazz class
* @return RefClass based on passed class
*/
public static RefClass getRefClass(final Class clazz) {
return new RefClass(clazz);
}
/**
* RefClass - utility to simplify work with reflections.
*/
public static class RefClass {
private final Class<?> clazz;
private RefClass(final Class<?> clazz) {
this.clazz = clazz;
}
/**
* get passed class
*
* @return class
*/
public Class<?> getRealClass() {
return this.clazz;
}
/**
* see {@link Class#isInstance(Object)}
*
* @param object the object to check
* @return true if object is an instance of this class
*/
public boolean isInstance(final Object object) {
return this.clazz.isInstance(object);
}
/**
* get existing method by name and types
*
* @param name name
* @param types method parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod getMethod(final String name, final Object... types) throws NoSuchMethodException {
try {
final Class[] classes = new Class[types.length];
int i = 0;
for (final Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
classes[i++] = ((RefClass) e).getRealClass();
} else {
classes[i++] = e.getClass();
}
}
try {
return new RefMethod(this.clazz.getMethod(name, classes));
} catch (final NoSuchMethodException ignored) {
return new RefMethod(this.clazz.getDeclaredMethod(name, classes));
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
/**
* get existing constructor by types
*
* @param types parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if constructor not found
*/
public RefConstructor getConstructor(final Object... types) {
try {
final Class[] classes = new Class[types.length];
int i = 0;
for (final Object e : types) {
if (e instanceof Class) {
classes[i++] = (Class) e;
} else if (e instanceof RefClass) {
classes[i++] = ((RefClass) e).getRealClass();
} else {
classes[i++] = e.getClass();
}
}
try {
return new RefConstructor(this.clazz.getConstructor(classes));
} catch (final NoSuchMethodException ignored) {
return new RefConstructor(this.clazz.getDeclaredConstructor(classes));
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
/**
* find method by type parameters
*
* @param types parameters. can be Class or RefClass
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod findMethod(final Object... types) {
final Class[] classes = new Class[types.length];
int t = 0;
for (final Object e : types) {
if (e instanceof Class) {
classes[t++] = (Class) e;
} else if (e instanceof RefClass) {
classes[t++] = ((RefClass) e).getRealClass();
} else {
classes[t++] = e.getClass();
}
}
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
findMethod:
for (final Method m : methods) {
final Class<?>[] methodTypes = m.getParameterTypes();
if (methodTypes.length != classes.length) {
continue;
}
for (final Class aClass : classes) {
if (!Arrays.equals(classes, methodTypes)) {
continue findMethod;
}
return new RefMethod(m);
}
}
throw new RuntimeException("no such method");
}
/**
* find method by name
*
* @param names possible names of method
* @return RefMethod object
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByName(final String... names) {
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
for (final Method m : methods) {
for (final String name : names) {
if (m.getName().equals(name)) {
return new RefMethod(m);
}
}
}
throw new RuntimeException("no such method");
}
/**
* find method by return value
*
* @param type type of returned value
* @return RefMethod
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByReturnType(final RefClass type) {
return this.findMethodByReturnType(type.clazz);
}
/**
* find method by return value
*
* @param type type of returned value
* @return RefMethod
* @throws RuntimeException if method not found
*/
public RefMethod findMethodByReturnType(Class type) {
if (type == null) {
type = void.class;
}
final List<Method> methods = new ArrayList<>();
Collections.addAll(methods, this.clazz.getMethods());
Collections.addAll(methods, this.clazz.getDeclaredMethods());
for (final Method m : methods) {
if (type.equals(m.getReturnType())) {
return new RefMethod(m);
}
}
throw new RuntimeException("no such method");
}
/**
* find constructor by number of arguments
*
* @param number number of arguments
* @return RefConstructor
* @throws RuntimeException if constructor not found
*/
public RefConstructor findConstructor(final int number) {
final List<Constructor> constructors = new ArrayList<>();
Collections.addAll(constructors, this.clazz.getConstructors());
Collections.addAll(constructors, this.clazz.getDeclaredConstructors());
for (final Constructor m : constructors) {
if (m.getParameterTypes().length == number) {
return new RefConstructor(m);
}
}
throw new RuntimeException("no such constructor");
}
/**
* get field by name
*
* @param name field name
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField getField(final String name) {
try {
try {
return new RefField(this.clazz.getField(name));
} catch (final NoSuchFieldException ignored) {
return new RefField(this.clazz.getDeclaredField(name));
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
/**
* find field by type
*
* @param type field type
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField findField(final RefClass type) {
return this.findField(type.clazz);
}
/**
* find field by type
*
* @param type field type
* @return RefField
* @throws RuntimeException if field not found
*/
public RefField findField(Class type) {
if (type == null) {
type = void.class;
}
final List<Field> fields = new ArrayList<>();
Collections.addAll(fields, this.clazz.getFields());
Collections.addAll(fields, this.clazz.getDeclaredFields());
for (final Field f : fields) {
if (type.equals(f.getType())) {
return new RefField(f);
}
}
throw new RuntimeException("no such field");
}
}
/**
* Method wrapper
*/
public static class RefMethod {
private final Method method;
private RefMethod(final Method method) {
this.method = method;
method.setAccessible(true);
}
/**
* @return passed method
*/
public Method getRealMethod() {
return this.method;
}
/**
* @return owner class of method
*/
public RefClass getRefClass() {
return new RefClass(this.method.getDeclaringClass());
}
/**
* @return class of method return type
*/
public RefClass getReturnRefClass() {
return new RefClass(this.method.getReturnType());
}
/**
* apply method to object
*
* @param e object to which the method is applied
* @return RefExecutor with method call(...)
*/
public RefExecutor of(final Object e) {
return new RefExecutor(e);
}
/**
* call static method
*
* @param params sent parameters
* @return return value
*/
public Object call(final Object... params) {
try {
return this.method.invoke(null, params);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public class RefExecutor {
final Object e;
public RefExecutor(final Object e) {
this.e = e;
}
/**
* apply method for selected object
*
* @param params sent parameters
* @return return value
* @throws RuntimeException if something went wrong
*/
public Object call(final Object... params) {
try {
return RefMethod.this.method.invoke(this.e, params);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
}
/**
* Constructor wrapper
*/
public static class RefConstructor {
private final Constructor constructor;
private RefConstructor(final Constructor constructor) {
this.constructor = constructor;
constructor.setAccessible(true);
}
/**
* @return passed constructor
*/
public Constructor getRealConstructor() {
return this.constructor;
}
/**
* @return owner class of method
*/
public RefClass getRefClass() {
return new RefClass(this.constructor.getDeclaringClass());
}
/**
* create new instance with constructor
*
* @param params parameters for constructor
* @return new object
* @throws RuntimeException if something went wrong
*/
public Object create(final Object... params) {
try {
return this.constructor.newInstance(params);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
public static class RefField {
private final Field field;
private RefField(final Field field) {
this.field = field;
field.setAccessible(true);
}
/**
* @return passed field
*/
public Field getRealField() {
return this.field;
}
/**
* @return owner class of field
*/
public RefClass getRefClass() {
return new RefClass(this.field.getDeclaringClass());
}
/**
* @return type of field
*/
public RefClass getFieldRefClass() {
return new RefClass(this.field.getType());
}
/**
* apply fiend for object
*
* @param e applied object
* @return RefExecutor with getter and setter
*/
public RefExecutor of(final Object e) {
return new RefExecutor(e);
}
public class RefExecutor {
final Object e;
public RefExecutor(final Object e) {
this.e = e;
}
/**
* set field value for applied object
*
* @param param value
*/
public void set(final Object param) {
try {
RefField.this.field.set(this.e, param);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
/**
* get field value for applied object
*
* @return value of field
*/
public Object get() {
try {
return RefField.this.field.get(this.e);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
}
}

View File

@ -0,0 +1,450 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.wrappers.WorldWrapper;
import com.sk89q.worldedit.world.World;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
public class SetQueue {
/**
* The implementation specific queue
*/
public static final SetQueue IMP = new SetQueue();
private double targetTPS = 18;
public enum QueueStage {
INACTIVE, ACTIVE, NONE;
}
private final ConcurrentLinkedDeque<FaweQueue> activeQueues;
private final ConcurrentLinkedDeque<FaweQueue> inactiveQueues;
private final ConcurrentLinkedDeque<Runnable> tasks;
/**
* Used to calculate elapsed time in milliseconds and ensure block placement doesn't lag the server
*/
private long last;
private long allocate = 50;
private long lastSuccess;
/**
* A queue of tasks that will run when the queue is empty
*/
private final ConcurrentLinkedDeque<Runnable> emptyTasks = new ConcurrentLinkedDeque<>();
private ForkJoinPool pool = new ForkJoinPool();
private ExecutorCompletionService completer = new ExecutorCompletionService(pool);
/**
* @return ForkJoinPool
* @see TaskManager#getPublicForkJoinPool()
*/
@Deprecated
public ExecutorCompletionService getCompleterService() {
return completer;
}
@Deprecated
public ForkJoinPool getForkJoinPool() {
return pool;
}
public void runMiscTasks() {
while (Fawe.get().getTimer().isAbove(targetTPS)) {
Runnable task = tasks.poll();
if (task != null) {
task.run();
} else {
break;
}
}
}
public SetQueue() {
tasks = new ConcurrentLinkedDeque<>();
activeQueues = new ConcurrentLinkedDeque();
inactiveQueues = new ConcurrentLinkedDeque<>();
if (TaskManager.IMP == null) return;
TaskManager.IMP.repeat(new Runnable() {
@Override
public void run() {
try {
long now = System.currentTimeMillis();
boolean empty = (inactiveQueues.isEmpty() && activeQueues.isEmpty());
boolean emptyTasks = tasks.isEmpty();
if (emptyTasks && empty) {
last = now;
runEmptyTasks();
return;
}
targetTPS = 18 - Math.max(Settings.IMP.QUEUE.EXTRA_TIME_MS * 0.05, 0);
long diff = (50 + SetQueue.this.last) - (SetQueue.this.last = now);
long absDiff = Math.abs(diff);
if (diff == 0) {
allocate = Math.min(50, allocate + 1);
} else if (diff < 0) {
allocate = Math.max(5, allocate + diff);
} else if (!Fawe.get().getTimer().isAbove(targetTPS)) {
allocate = Math.max(5, allocate - 1);
}
long currentAllocate = allocate - absDiff;
if (!emptyTasks) {
long taskAllocate = activeQueues.isEmpty() ? currentAllocate : 1 + (currentAllocate >> 1);
long used = 0;
boolean wait = false;
do {
Runnable task = tasks.poll();
if (task == null) {
if (wait) {
synchronized (tasks) {
tasks.wait(1);
}
task = tasks.poll();
wait = false;
} else {
break;
}
}
if (task != null) {
task.run();
wait = true;
}
} while ((used = System.currentTimeMillis() - now) < taskAllocate);
currentAllocate -= used;
}
if (empty) {
runEmptyTasks();
return;
}
if (!MemUtil.isMemoryFree()) {
final int mem = MemUtil.calculateMemory();
if (mem != Integer.MAX_VALUE) {
allocate = Math.max(5, allocate - 1);
if ((mem <= 1) && Settings.IMP.PREVENT_CRASHES) {
for (FaweQueue queue : getAllQueues()) {
queue.saveMemory();
}
return;
}
if (SetQueue.this.forceChunkSet()) {
System.gc();
} else {
SetQueue.this.runEmptyTasks();
}
return;
}
}
FaweQueue queue = getNextQueue();
if (queue == null) {
return;
}
long time = Settings.IMP.QUEUE.EXTRA_TIME_MS + currentAllocate - System.currentTimeMillis() + now;
// Disable the async catcher as it can't discern async vs parallel
boolean parallel = Settings.IMP.QUEUE.PARALLEL_THREADS > 1;
queue.startSet(parallel);
try {
if (!queue.next(Settings.IMP.QUEUE.PARALLEL_THREADS, time) && queue.getStage() == QueueStage.ACTIVE) {
queue.setStage(QueueStage.NONE);
queue.runTasks();
}
} catch (Throwable e) {
pool.awaitQuiescence(Settings.IMP.QUEUE.DISCARD_AFTER_MS, TimeUnit.MILLISECONDS);
completer = new ExecutorCompletionService(pool);
e.printStackTrace();
}
if (pool.getQueuedSubmissionCount() != 0 || pool.getRunningThreadCount() != 0 || pool.getQueuedTaskCount() != 0) {
// if (Fawe.get().isJava8())
{
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
// else {
// pool.shutdown();
// pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
// pool = new ForkJoinPool();
// completer = new ExecutorCompletionService(pool);
// }
}
queue.endSet(parallel);
} catch (Throwable e) {
e.printStackTrace();
}
}
}, 1);
}
public QueueStage getStage(FaweQueue queue) {
return queue.getStage();
}
public boolean isStage(FaweQueue queue, QueueStage stage) {
switch (stage) {
case ACTIVE:
return activeQueues.contains(queue);
case INACTIVE:
return inactiveQueues.contains(queue);
case NONE:
return !activeQueues.contains(queue) && !inactiveQueues.contains(queue);
}
return false;
}
public boolean enqueue(FaweQueue queue) {
queue.setStage(QueueStage.ACTIVE);
inactiveQueues.remove(queue);
if (queue.size() > 0) {
if (!activeQueues.contains(queue)) {
queue.optimize();
activeQueues.add(queue);
}
return true;
}
return false;
}
public void dequeue(FaweQueue queue) {
queue.setStage(QueueStage.NONE);
inactiveQueues.remove(queue);
activeQueues.remove(queue);
queue.runTasks();
}
public Collection<FaweQueue> getAllQueues() {
ArrayList<FaweQueue> list = new ArrayList<FaweQueue>(activeQueues.size() + inactiveQueues.size());
list.addAll(inactiveQueues);
list.addAll(activeQueues);
return list;
}
public Collection<FaweQueue> getActiveQueues() {
return Collections.unmodifiableCollection(activeQueues);
}
public Collection<FaweQueue> getInactiveQueues() {
return Collections.unmodifiableCollection(inactiveQueues);
}
public FaweQueue getNewQueue(World world, boolean fast, boolean autoqueue) {
world = WorldWrapper.unwrap(world);
if (world instanceof FaweQueue) return (FaweQueue) world;
FaweQueue queue = Fawe.imp().getNewQueue(world, fast);
if (autoqueue) {
queue.setStage(QueueStage.INACTIVE);
inactiveQueues.add(queue);
}
return queue;
}
public FaweQueue getNewQueue(String world, boolean fast, boolean autoqueue) {
FaweQueue queue = Fawe.imp().getNewQueue(world, fast);
if (autoqueue) {
queue.setStage(QueueStage.INACTIVE);
inactiveQueues.add(queue);
}
return queue;
}
public void flush(FaweQueue queue) {
int parallelThreads;
if (Fawe.get().isMainThread()) {
parallelThreads = Settings.IMP.QUEUE.PARALLEL_THREADS;
Settings.IMP.QUEUE.PARALLEL_THREADS = 1;
} else {
parallelThreads = 0;
}
try {
queue.startSet(Settings.IMP.QUEUE.PARALLEL_THREADS > 1);
queue.next(Settings.IMP.QUEUE.PARALLEL_THREADS, Long.MAX_VALUE);
} catch (Throwable e) {
pool.awaitQuiescence(Settings.IMP.QUEUE.DISCARD_AFTER_MS, TimeUnit.MILLISECONDS);
completer = new ExecutorCompletionService(pool);
MainUtil.handleError(e);
} finally {
queue.endSet(Settings.IMP.QUEUE.PARALLEL_THREADS > 1);
queue.setStage(QueueStage.NONE);
queue.runTasks();
if (parallelThreads != 0) {
Settings.IMP.QUEUE.PARALLEL_THREADS = parallelThreads;
}
}
}
public FaweQueue getNextQueue() {
long now = System.currentTimeMillis();
while (!activeQueues.isEmpty()) {
FaweQueue queue = activeQueues.peek();
if (queue != null && queue.size() > 0) {
queue.setModified(now);
return queue;
} else {
queue.setStage(QueueStage.NONE);
queue.runTasks();
activeQueues.poll();
}
}
int size = inactiveQueues.size();
if (size > 0) {
Iterator<FaweQueue> iter = inactiveQueues.iterator();
try {
int total = 0;
FaweQueue firstNonEmpty = null;
while (iter.hasNext()) {
FaweQueue queue = iter.next();
long age = now - queue.getModified();
total += queue.size();
if (queue.size() == 0) {
if (age > Settings.IMP.QUEUE.DISCARD_AFTER_MS) {
queue.setStage(QueueStage.NONE);
queue.runTasks();
iter.remove();
}
continue;
}
if (firstNonEmpty == null) {
firstNonEmpty = queue;
}
if (total > Settings.IMP.QUEUE.TARGET_SIZE) {
firstNonEmpty.setModified(now);
return firstNonEmpty;
}
if (age > Settings.IMP.QUEUE.MAX_WAIT_MS) {
queue.setModified(now);
return queue;
}
}
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
return null;
}
public boolean next() {
while (activeQueues.size() > 0) {
FaweQueue queue = activeQueues.poll();
if (queue != null) {
final boolean set = queue.next();
if (set) {
activeQueues.add(queue);
return set;
} else {
queue.setStage(QueueStage.NONE);
queue.runTasks();
}
}
}
if (inactiveQueues.size() > 0) {
ArrayList<FaweQueue> tmp = new ArrayList<>(inactiveQueues);
if (Settings.IMP.QUEUE.MAX_WAIT_MS != -1) {
long now = System.currentTimeMillis();
if (lastSuccess == 0) {
lastSuccess = now;
}
long diff = now - lastSuccess;
if (diff > Settings.IMP.QUEUE.MAX_WAIT_MS) {
for (FaweQueue queue : tmp) {
boolean result = queue.next();
if (result) {
return result;
}
}
if (diff > Settings.IMP.QUEUE.DISCARD_AFTER_MS) {
// These edits never finished
for (FaweQueue queue : tmp) {
queue.setStage(QueueStage.NONE);
queue.runTasks();
}
inactiveQueues.clear();
}
return false;
}
}
if (Settings.IMP.QUEUE.TARGET_SIZE != -1) {
int total = 0;
for (FaweQueue queue : tmp) {
total += queue.size();
}
if (total > Settings.IMP.QUEUE.TARGET_SIZE) {
for (FaweQueue queue : tmp) {
boolean result = queue.next();
if (result) {
return result;
}
}
}
}
}
return false;
}
public boolean forceChunkSet() {
return next();
}
/**
* Is the this empty
*
* @return
*/
public boolean isEmpty() {
return activeQueues.size() == 0 && inactiveQueues.size() == 0;
}
public void addTask(Runnable whenFree) {
tasks.add(whenFree);
synchronized (tasks) {
tasks.notifyAll();
}
}
/**
* Add a task to run when it is empty
*
* @param whenDone
* @return
*/
public boolean addEmptyTask(final Runnable whenDone) {
if (this.isEmpty()) {
// Run
this.runEmptyTasks();
if (whenDone != null) {
whenDone.run();
}
return true;
}
if (whenDone != null) {
this.emptyTasks.add(whenDone);
}
return false;
}
private synchronized boolean runEmptyTasks() {
if (this.emptyTasks.isEmpty()) {
return false;
}
final ConcurrentLinkedDeque<Runnable> tmp = new ConcurrentLinkedDeque<>(this.emptyTasks);
this.emptyTasks.clear();
for (final Runnable runnable : tmp) {
runnable.run();
}
return true;
}
}

View File

@ -0,0 +1,738 @@
package com.boydti.fawe.util;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.IllegalPathStateException;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Vector;
/**
* <a href="https://github.com/Sciss/ShapeInterpolator/blob/master/src/main/java/de/sciss/shapeint/ShapeInterpolator.java">Original source</a><br>
* An interpolator for {@link Shape} objects.
* This class can be used to morph between the geometries
* of two relatively arbitrary shapes with the only restrictions being
* that the two different numbers of sub-paths or two shapes with
* disparate winding rules may not blend together in a pleasing
* manner.
* The ShapeEvaluator will do the best job it can if the shapes do
* not match in winding rule or number of sub-paths, but the geometry
* of the shapes may need to be adjusted by other means to make the
* shapes more like each other for best aesthetic effect.
* <p>
* Note that the process of comparing two geometries and finding similar
* structures between them to blend for the morphing operation can be
* expensive.
* Instances of this class will properly perform the necessary
* geometric analysis of their arguments on every method call and attempt
* to cache the information so that they can operate more quickly if called
* multiple times in a row on the same pair of {@code Shape} objects.
* As a result attempting to mutate a {@code Shape} object that is stored
* in one of their keyframes may not have any effect if the associated
* interpolator has already cached the geometry.
* Also, it is advisable to use different instances of {@code ShapeEvaluator}
* for every pair of keyframes being morphed so that the cached information
* can be reused as much as possible.
*/
public class ShapeInterpolator {
private Shape savedV0;
private Shape savedV1;
private Geometry geom0;
private Geometry geom1;
public static Shape apply(Shape v0, Shape v1, float fraction) {
return apply(v0, v1, fraction, false);
}
public static Shape apply(Shape v0, Shape v1, float fraction, boolean unionBounds) {
final ShapeInterpolator instance = new ShapeInterpolator();
return instance.evaluate(v0, v1, fraction, unionBounds);
}
/**
* Creates an interpolated shape from tight bounds.
*/
public Shape evaluate(Shape v0, Shape v1, float fraction) {
return evaluate(v0, v1, fraction, false);
}
/**
* Creates an interpolated shape.
*
* @param v0 the first shape
* @param v1 the second shape
* @param fraction the fraction from zero (just first shape) to one (just second shape)
* @param unionBounds if `true`, the shape reports bounds which are the union of
* the bounds of both shapes, if `false` it reports "tight" bounds
* using the actual interpolated path.
*/
public Shape evaluate(Shape v0, Shape v1, float fraction, boolean unionBounds) {
if (savedV0 != v0 || savedV1 != v1) {
if (savedV0 == v1 && savedV1 == v0) {
// Just swap the geometries
final Geometry tmp = geom0;
geom0 = geom1;
geom1 = tmp;
} else {
recalculate(v0, v1);
}
savedV0 = v0;
savedV1 = v1;
}
return getShape(fraction, unionBounds);
}
private void recalculate(Shape v0, Shape v1) {
geom0 = new Geometry(v0);
geom1 = new Geometry(v1);
final float[] tVals0 = geom0.getTVals();
final float[] tVals1 = geom1.getTVals();
final float[] masterTVals = mergeTVals(tVals0, tVals1);
geom0.setTVals(masterTVals);
geom1.setTVals(masterTVals);
}
private Shape getShape(float fraction, boolean unionBounds) {
return new MorphedShape(geom0, geom1, fraction, unionBounds);
}
private static float[] mergeTVals(float[] tVals0, float[] tVals1) {
final int count = sortTVals(tVals0, tVals1, null);
final float[] newTVals = new float[count];
sortTVals(tVals0, tVals1, newTVals);
return newTVals;
}
private static int sortTVals(float[] tVals0,
float[] tVals1,
float[] newTVals) {
int i0 = 0;
int i1 = 0;
int numTVals = 0;
while (i0 < tVals0.length && i1 < tVals1.length) {
final float t0 = tVals0[i0];
final float t1 = tVals1[i1];
if (t0 <= t1) {
if (newTVals != null) {
newTVals[numTVals] = t0;
}
i0++;
}
if (t1 <= t0) {
if (newTVals != null) {
newTVals[numTVals] = t1;
}
i1++;
}
numTVals++;
}
return numTVals;
}
private static float interp(float v0, float v1, float t) {
return (v0 + ((v1 - v0) * t));
}
private static class Geometry {
static final float THIRD = (1f / 3f);
static final float MIN_LEN = 0.001f;
final int windingRule;
float[] bezierCoordinates;
int numCoordinates;
float[] myTVals;
public Geometry(Shape s) {
// Multiple of 6 plus 2 more for initial move-to
bezierCoordinates = new float[20];
final PathIterator pi = s.getPathIterator(null);
windingRule = pi.getWindingRule();
if (pi.isDone()) {
// We will have 1 segment and it will be all zeros
// It will have 8 coordinates (2 for move-to, 6 for cubic)
numCoordinates = 8;
}
final float[] coordinates = new float[6];
int type = pi.currentSegment(coordinates);
pi.next();
if (type != PathIterator.SEG_MOVETO) {
throw new IllegalPathStateException("missing initial move-to");
}
float curX, curY, movX, movY;
bezierCoordinates[0] = curX = movX = coordinates[0];
bezierCoordinates[1] = curY = movY = coordinates[1];
float newX, newY;
final Vector<Point2D.Float> savedPathEndPoints = new Vector<Point2D.Float>();
numCoordinates = 2;
while (!pi.isDone()) {
switch (pi.currentSegment(coordinates)) {
case PathIterator.SEG_MOVETO:
if (curX != movX || curY != movY) {
appendLineTo(curX, curY, movX, movY);
curX = movX;
curY = movY;
}
newX = coordinates[0];
newY = coordinates[1];
if (curX != newX || curY != newY) {
savedPathEndPoints.add(new Point2D.Float(movX, movY));
appendLineTo(curX, curY, newX, newY);
curX = movX = newX;
curY = movY = newY;
}
break;
case PathIterator.SEG_CLOSE:
if (curX != movX || curY != movY) {
appendLineTo(curX, curY, movX, movY);
curX = movX;
curY = movY;
}
break;
case PathIterator.SEG_LINETO:
newX = coordinates[0];
newY = coordinates[1];
appendLineTo(curX, curY, newX, newY);
curX = newX;
curY = newY;
break;
case PathIterator.SEG_QUADTO:
final float ctrlX = coordinates[0];
final float ctrlY = coordinates[1];
newX = coordinates[2];
newY = coordinates[3];
appendQuadTo(curX, curY, ctrlX, ctrlY, newX, newY);
curX = newX;
curY = newY;
break;
case PathIterator.SEG_CUBICTO:
newX = coordinates[4];
newY = coordinates[5];
appendCubicTo(
coordinates[0], coordinates[1],
coordinates[2], coordinates[3],
newX, newY);
curX = newX;
curY = newY;
break;
}
pi.next();
}
// Add closing segment if either:
// - we only have initial move-to - expand it to an empty cubic
// - or we are not back to the starting point
if ((numCoordinates < 8) || curX != movX || curY != movY) {
appendLineTo(curX, curY, movX, movY);
curX = movX;
curY = movY;
}
// Now retrace our way back through all of the connecting
// inter-sub-path segments
for (int i = savedPathEndPoints.size() - 1; i >= 0; i--) {
final Point2D.Float p = savedPathEndPoints.get(i);
newX = p.x;
newY = p.y;
if (curX != newX || curY != newY) {
appendLineTo(curX, curY, newX, newY);
curX = newX;
curY = newY;
}
}
// Now find the segment endpoint with the smallest Y coordinate
int minPt = 0;
float minX = bezierCoordinates[0];
float minY = bezierCoordinates[1];
for (int ci = 6; ci < numCoordinates; ci += 6) {
float x = bezierCoordinates[ci];
float y = bezierCoordinates[ci + 1];
if (y < minY || (y == minY && x < minX)) {
minPt = ci;
minX = x;
minY = y;
}
}
// If the smallest Y coordinate is not the first coordinate,
// rotate the points so that it is...
if (minPt > 0) {
// Keep in mind that first 2 coordinates == last 2 coordinates
final float[] newCoordinates = new float[numCoordinates];
// Copy all coordinates from minPt to the end of the
// array to the beginning of the new array
System.arraycopy(bezierCoordinates, minPt,
newCoordinates, 0,
numCoordinates - minPt);
// Now we do not want to copy 0,1 as they are duplicates
// of the last 2 coordinates which we just copied. So
// we start the source copy at index 2, but we still
// copy a full minPt coordinates which copies the two
// coordinates that were at minPt to the last two elements
// of the array, thus ensuring that thew new array starts
// and ends with the same pair of coordinates...
System.arraycopy(bezierCoordinates, 2,
newCoordinates, numCoordinates - minPt,
minPt);
bezierCoordinates = newCoordinates;
}
/* Clockwise enforcement:
* - This technique is based on the formula for calculating
* the area of a Polygon. The standard formula is:
* Area(Poly) = 1/2 * sum(x[i]*y[i+1] - x[i+1]y[i])
* - The returned area is negative if the polygon is
* "mostly clockwise" and positive if the polygon is
* "mostly counter-clockwise".
* - One failure mode of the Area calculation is if the
* Polygon is self-intersecting. This is due to the
* fact that the areas on each side of the self-intersection
* are bounded by segments which have opposite winding
* direction. Thus, those areas will have opposite signs
* on the accumulation of their area summations and end
* up canceling each other out partially.
* - This failure mode of the algorithm in determining the
* exact magnitude of the area is not actually a big problem
* for our needs here since we are only using the sign of
* the resulting area to figure out the overall winding
* direction of the path. If self-intersections cause
* different parts of the path to disagree as to the
* local winding direction, that is no matter as we just
* wait for the final answer to tell us which winding
* direction had greater representation. If the final
* result is zero then the path was equal parts clockwise
* and counter-clockwise and we do not care about which
* way we order it as either way will require half of the
* path to unwind and re-wind itself.
*/
float area = 0;
// Note that first and last points are the same so we
// do not need to process coordinates[0,1] against coordinates[n-2,n-1]
curX = bezierCoordinates[0];
curY = bezierCoordinates[1];
for (int i = 2; i < numCoordinates; i += 2) {
newX = bezierCoordinates[i];
newY = bezierCoordinates[i + 1];
area += curX * newY - newX * curY;
curX = newX;
curY = newY;
}
if (area < 0) {
/* The area is negative so the shape was clockwise
* in a Euclidean sense. But, our screen coordinate
* systems have the origin in the upper left so they
* are flipped. Thus, this path "looks" ccw on the
* screen so we are flipping it to "look" clockwise.
* Note that the first and last points are the same
* so we do not need to swap them.
* (Not that it matters whether the paths end up cw
* or ccw in the end as long as all of them are the
* same, but above we called this section "Clockwise
* Enforcement", so we do not want to be liars. ;-)
*/
// Note that [0,1] do not need to be swapped with [n-2,n-1]
// So first pair to swap is [2,3] and [n-4,n-3]
int i = 2;
int j = numCoordinates - 4;
while (i < j) {
curX = bezierCoordinates[i];
curY = bezierCoordinates[i + 1];
bezierCoordinates[i] = bezierCoordinates[j];
bezierCoordinates[i + 1] = bezierCoordinates[j + 1];
bezierCoordinates[j] = curX;
bezierCoordinates[j + 1] = curY;
i += 2;
j -= 2;
}
}
}
private void appendLineTo(float x0, float y0,
float x1, float y1) {
appendCubicTo(// A third of the way from xy0 to xy1:
interp(x0, x1, THIRD),
interp(y0, y1, THIRD),
// A third of the way from xy1 back to xy0:
interp(x1, x0, THIRD),
interp(y1, y0, THIRD),
x1, y1);
}
private void appendQuadTo(float x0, float y0,
float ctrlX, float ctrlY,
float x1, float y1) {
appendCubicTo(// A third of the way from ctrl X/Y back to xy0:
interp(ctrlX, x0, THIRD),
interp(ctrlY, y0, THIRD),
// A third of the way from ctrl X/Y to xy1:
interp(ctrlX, x1, THIRD),
interp(ctrlY, y1, THIRD),
x1, y1);
}
private void appendCubicTo(float ctrlX1, float ctrlY1,
float ctrlX2, float ctrlY2,
float x1, float y1) {
if (numCoordinates + 6 > bezierCoordinates.length) {
// Keep array size to a multiple of 6 plus 2
int newsize = (numCoordinates - 2) * 2 + 2;
final float[] newCoordinates = new float[newsize];
System.arraycopy(bezierCoordinates, 0, newCoordinates, 0, numCoordinates);
bezierCoordinates = newCoordinates;
}
bezierCoordinates[numCoordinates++] = ctrlX1;
bezierCoordinates[numCoordinates++] = ctrlY1;
bezierCoordinates[numCoordinates++] = ctrlX2;
bezierCoordinates[numCoordinates++] = ctrlY2;
bezierCoordinates[numCoordinates++] = x1;
bezierCoordinates[numCoordinates++] = y1;
}
public int getWindingRule() {
return windingRule;
}
public int getNumCoordinates() {
return numCoordinates;
}
public float getCoordinate(int i) {
return bezierCoordinates[i];
}
public float[] getTVals() {
if (myTVals != null) {
return myTVals;
}
// assert(numCoordinates >= 8);
// assert(((numCoordinates - 2) % 6) == 0);
final float[] tVals = new float[(numCoordinates - 2) / 6 + 1];
// First calculate total "length" of path
// Length of each segment is averaged between
// the length between the endpoints (a lower bound for a cubic)
// and the length of the control polygon (an upper bound)
float segX = bezierCoordinates[0];
float segY = bezierCoordinates[1];
float tLen = 0;
int ci = 2;
int ti = 0;
while (ci < numCoordinates) {
float prevX, prevY, newX, newY;
prevX = segX;
prevY = segY;
newX = bezierCoordinates[ci++];
newY = bezierCoordinates[ci++];
prevX -= newX;
prevY -= newY;
float len = (float) Math.sqrt(prevX * prevX + prevY * prevY);
prevX = newX;
prevY = newY;
newX = bezierCoordinates[ci++];
newY = bezierCoordinates[ci++];
prevX -= newX;
prevY -= newY;
len += (float) Math.sqrt(prevX * prevX + prevY * prevY);
prevX = newX;
prevY = newY;
newX = bezierCoordinates[ci++];
newY = bezierCoordinates[ci++];
prevX -= newX;
prevY -= newY;
len += (float) Math.sqrt(prevX * prevX + prevY * prevY);
// len is now the total length of the control polygon
segX -= newX;
segY -= newY;
len += (float) Math.sqrt(segX * segX + segY * segY);
// len is now sum of linear length and control polygon length
len /= 2;
// len is now average of the two lengths
/* If the result is zero length then we will have problems
* below trying to do the math and bookkeeping to split
* the segment or pair it against the segments in the
* other shape. Since these lengths are just estimates
* to map the segments of the two shapes onto corresponding
* segments of "approximately the same length", we will
* simply modify the length of this segment to be at least
* a minimum value and it will simply grow from zero or
* near zero length to a non-trivial size as it morphs.
*/
if (len < MIN_LEN) {
len = MIN_LEN;
}
tLen += len;
tVals[ti++] = tLen;
segX = newX;
segY = newY;
}
// Now set tVals for each segment to its proportional
// part of the length
float prevT = tVals[0];
tVals[0] = 0;
for (ti = 1; ti < tVals.length - 1; ti++) {
final float nextT = tVals[ti];
tVals[ti] = prevT / tLen;
prevT = nextT;
}
tVals[ti] = 1;
return (myTVals = tVals);
}
public void setTVals(float[] newTVals) {
final float[] oldCoordinates = bezierCoordinates;
final float[] newCoordinates = new float[2 + (newTVals.length - 1) * 6];
final float[] oldTVals = getTVals();
int oldCi = 0;
float x0, xc0, xc1, x1;
float y0, yc0, yc1, y1;
x0 = xc0 = xc1 = x1 = oldCoordinates[oldCi++];
y0 = yc0 = yc1 = y1 = oldCoordinates[oldCi++];
int newCi = 0;
newCoordinates[newCi++] = x0;
newCoordinates[newCi++] = y0;
float t0 = 0;
float t1 = 0;
int oldTi = 1;
int newTi = 1;
while (newTi < newTVals.length) {
if (t0 >= t1) {
x0 = x1;
y0 = y1;
xc0 = oldCoordinates[oldCi++];
yc0 = oldCoordinates[oldCi++];
xc1 = oldCoordinates[oldCi++];
yc1 = oldCoordinates[oldCi++];
x1 = oldCoordinates[oldCi++];
y1 = oldCoordinates[oldCi++];
t1 = oldTVals[oldTi++];
}
float nt = newTVals[newTi++];
// assert(nt > t0);
if (nt < t1) {
// Make nt proportional to [t0 => t1] range
float relT = (nt - t0) / (t1 - t0);
newCoordinates[newCi++] = x0 = interp(x0, xc0, relT);
newCoordinates[newCi++] = y0 = interp(y0, yc0, relT);
xc0 = interp(xc0, xc1, relT);
yc0 = interp(yc0, yc1, relT);
xc1 = interp(xc1, x1, relT);
yc1 = interp(yc1, y1, relT);
newCoordinates[newCi++] = x0 = interp(x0, xc0, relT);
newCoordinates[newCi++] = y0 = interp(y0, yc0, relT);
xc0 = interp(xc0, xc1, relT);
yc0 = interp(yc0, yc1, relT);
newCoordinates[newCi++] = x0 = interp(x0, xc0, relT);
newCoordinates[newCi++] = y0 = interp(y0, yc0, relT);
} else {
newCoordinates[newCi++] = xc0;
newCoordinates[newCi++] = yc0;
newCoordinates[newCi++] = xc1;
newCoordinates[newCi++] = yc1;
newCoordinates[newCi++] = x1;
newCoordinates[newCi++] = y1;
}
t0 = nt;
}
bezierCoordinates = newCoordinates;
numCoordinates = newCoordinates.length;
myTVals = newTVals;
}
}
private static class MorphedShape implements Shape {
final Geometry geom0;
final Geometry geom1;
final float t;
final boolean unionBounds;
MorphedShape(Geometry geom0, Geometry geom1, float t, boolean unionBounds) {
this.geom0 = geom0;
this.geom1 = geom1;
this.t = t;
this.unionBounds = unionBounds;
}
public Rectangle getBounds() {
return getBounds2D().getBounds();
}
public Rectangle2D getBounds2D() {
final int n = geom0.getNumCoordinates();
float xMin, yMin, xMax, yMax;
if (unionBounds) {
xMin = xMax = geom0.getCoordinate(0);
yMin = yMax = geom0.getCoordinate(1);
for (int i = 2; i < n; i += 2) {
final float x = geom0.getCoordinate(i);
final float y = geom0.getCoordinate(i + 1);
if (xMin > x) {
xMin = x;
}
if (yMin > y) {
yMin = y;
}
if (xMax < x) {
xMax = x;
}
if (yMax < y) {
yMax = y;
}
}
final int m = geom1.getNumCoordinates();
for (int i = 0; i < m; i += 2) {
final float x = geom1.getCoordinate(i);
final float y = geom1.getCoordinate(i + 1);
if (xMin > x) {
xMin = x;
}
if (yMin > y) {
yMin = y;
}
if (xMax < x) {
xMax = x;
}
if (yMax < y) {
yMax = y;
}
}
} else {
xMin = xMax = interp(geom0.getCoordinate(0), geom1.getCoordinate(0), t);
yMin = yMax = interp(geom0.getCoordinate(1), geom1.getCoordinate(1), t);
for (int i = 2; i < n; i += 2) {
final float x = interp(geom0.getCoordinate(i), geom1.getCoordinate(i), t);
final float y = interp(geom0.getCoordinate(i + 1), geom1.getCoordinate(i + 1), t);
if (xMin > x) {
xMin = x;
}
if (yMin > y) {
yMin = y;
}
if (xMax < x) {
xMax = x;
}
if (yMax < y) {
yMax = y;
}
}
}
return new Rectangle2D.Float(xMin, yMin, xMax - xMin, yMax - yMin);
}
public boolean contains(double x, double y) {
return Path2D.contains(getPathIterator(null), x, y);
}
public boolean contains(Point2D p) {
return Path2D.contains(getPathIterator(null), p);
}
public boolean intersects(double x, double y, double w, double h) {
return Path2D.intersects(getPathIterator(null), x, y, w, h);
}
public boolean intersects(Rectangle2D r) {
return Path2D.intersects(getPathIterator(null), r);
}
public boolean contains(double x, double y, double width, double height) {
return Path2D.contains(getPathIterator(null), x, y, width, height);
}
public boolean contains(Rectangle2D r) {
return Path2D.contains(getPathIterator(null), r);
}
public PathIterator getPathIterator(AffineTransform at) {
return new Iterator(at, geom0, geom1, t);
}
public PathIterator getPathIterator(AffineTransform at, double flatness) {
return new FlatteningPathIterator(getPathIterator(at), flatness);
}
}
private static class Iterator implements PathIterator {
AffineTransform at;
Geometry g0;
Geometry g1;
float t;
int cIndex;
public Iterator(AffineTransform at,
Geometry g0, Geometry g1,
float t) {
this.at = at;
this.g0 = g0;
this.g1 = g1;
this.t = t;
}
/**
* @{inheritDoc}
*/
public int getWindingRule() {
return (t < 0.5 ? g0.getWindingRule() : g1.getWindingRule());
}
/**
* @{inheritDoc}
*/
public boolean isDone() {
return (cIndex > g0.getNumCoordinates());
}
/**
* @{inheritDoc}
*/
public void next() {
if (cIndex == 0) {
cIndex = 2;
} else {
cIndex += 6;
}
}
/**
* @{inheritDoc}
*/
public int currentSegment(float[] coordinates) {
int type;
int n;
if (cIndex == 0) {
type = SEG_MOVETO;
n = 2;
} else if (cIndex >= g0.getNumCoordinates()) {
type = SEG_CLOSE;
n = 0;
} else {
type = SEG_CUBICTO;
n = 6;
}
if (n > 0) {
for (int i = 0; i < n; i++) {
coordinates[i] = interp(
g0.getCoordinate(cIndex + i),
g1.getCoordinate(cIndex + i),
t);
}
if (at != null) {
at.transform(coordinates, 0, coordinates, 0, n / 2);
}
}
return type;
}
public int currentSegment(double[] coordinates) {
final float[] temp = new float[6];
final int res = currentSegment(temp);
for (int i = 0; i < 6; i++) {
coordinates[i] = temp[i];
}
return res;
}
}
}

View File

@ -0,0 +1,453 @@
package com.boydti.fawe.util;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
public class StringMan {
public static String replaceFromMap(final String string, final Map<String, String> replacements) {
final StringBuilder sb = new StringBuilder(string);
int size = string.length();
for (final Entry<String, String> entry : replacements.entrySet()) {
if (size == 0) {
break;
}
final String key = entry.getKey();
final String value = entry.getValue();
int start = sb.indexOf(key, 0);
while (start > -1) {
final int end = start + key.length();
final int nextSearchStart = start + value.length();
sb.replace(start, end, value);
size -= end - start;
start = sb.indexOf(key, nextSearchStart);
}
}
return sb.toString();
}
public static int findMatchingBracket(CharSequence sequence, int index) {
char startC = sequence.charAt(index);
char lookC = getMatchingBracket(startC);
if (lookC == startC) return -1;
boolean forward = isBracketForwards(startC);
int increment = forward ? 1 : -1;
int end = forward ? sequence.length() : -1;
int count = 0;
for (int i = index + increment; i != end; i += increment) {
char c = sequence.charAt(i);
if (c == startC) {
count++;
} else if (c == lookC && count-- == 0) {
return i;
}
}
return -1;
}
public static boolean isBracketForwards(char c) {
switch (c) {
case '[':
case '(':
case '{':
case '<':
return true;
default: return false;
}
}
public static char getMatchingBracket(char c) {
switch (c) {
case '[': return ']';
case '(': return ')';
case '{': return '}';
case '<': return '>';
case ']': return '[';
case ')': return '(';
case '}': return '{';
case '>': return '<';
default: return c;
}
}
public static int parseInt(CharSequence string) {
int val = 0;
boolean neg = false;
int numIndex = 1;
int len = string.length();
outer:
for (int i = len - 1; i >= 0; i--) {
char c = string.charAt(i);
switch (c) {
case '-':
val = -val;
break;
default:
val = val + (c - 48) * numIndex;
numIndex *= 10;
break;
}
}
return val;
}
public static String removeFromSet(final String string, final Collection<String> replacements) {
final StringBuilder sb = new StringBuilder(string);
int size = string.length();
for (final String key : replacements) {
if (size == 0) {
break;
}
int start = sb.indexOf(key, 0);
while (start > -1) {
final int end = start + key.length();
final int nextSearchStart = start + 0;
sb.delete(start, end);
size -= end - start;
start = sb.indexOf(key, nextSearchStart);
}
}
return sb.toString();
}
public static int indexOf(String input, int start, char... values) {
for (int i = start; i < input.length(); i++) {
for (char c : values) {
if (c == input.charAt(i)) return i;
}
}
return -1;
}
public static String toProperCase(String s) {
return s.substring(0, 1).toUpperCase() +
s.substring(1);
}
public static List<String> split(String input, char delim) {
List<String> result = new ArrayList<String>();
int start = 0;
int bracket = 0;
boolean inQuotes = false;
for (int current = 0; current < input.length(); current++) {
char currentChar = input.charAt(current);
boolean atLastChar = (current == input.length() - 1);
if (!atLastChar && (bracket > 0 || (currentChar == '{' && ++bracket > 0) || (current == '}' && --bracket <= 0)))
continue;
if (currentChar == '\"') inQuotes = !inQuotes; // toggle state
if (atLastChar) result.add(input.substring(start));
else if (currentChar == delim && !inQuotes) {
String toAdd = input.substring(start, current);
if (toAdd.startsWith("\"")) {
toAdd = toAdd.substring(1, toAdd.length() - 1);
}
result.add(toAdd);
start = current + 1;
}
}
return result;
}
public static int intersection(final Set<String> options, final String[] toCheck) {
int count = 0;
for (final String check : toCheck) {
if (options.contains(check)) {
count++;
}
}
return count;
}
public static String padRight(String s, int n) {
return String.format("%1$-" + n + "s", s);
}
public static String padLeft(String s, int n) {
return String.format("%1$" + n + "s", s);
}
public static String getString(final Object obj) {
if (obj == null) {
return "null";
}
if (obj.getClass() == String.class) {
return (String) obj;
}
if (obj.getClass().isArray()) {
String result = "";
String prefix = "";
for (int i = 0; i < Array.getLength(obj); i++) {
result += prefix + getString(Array.get(obj, i));
prefix = ",";
}
return "{ " + result + " }";
} else if (obj instanceof Collection<?>) {
String result = "";
String prefix = "";
for (final Object element : (Collection<?>) obj) {
result += prefix + getString(element);
prefix = ",";
}
return "( " + result + " )";
} else {
return obj.toString();
}
}
public static String replaceFirst(final char c, final String s) {
if (s == null) {
return "";
}
if (s.isEmpty()) {
return s;
}
char[] chars = s.toCharArray();
final char[] newChars = new char[chars.length];
int used = 0;
boolean found = false;
for (final char cc : chars) {
if (!found && (c == cc)) {
found = true;
} else {
newChars[used++] = cc;
}
}
if (found) {
chars = new char[newChars.length - 1];
System.arraycopy(newChars, 0, chars, 0, chars.length);
return String.valueOf(chars);
}
return s;
}
public static String replaceAll(final String string, final Object... pairs) {
final StringBuilder sb = new StringBuilder(string);
for (int i = 0; i < pairs.length; i += 2) {
final String key = pairs[i] + "";
final String value = pairs[i + 1] + "";
int start = sb.indexOf(key, 0);
while (start > -1) {
final int end = start + key.length();
final int nextSearchStart = start + value.length();
sb.replace(start, end, value);
start = sb.indexOf(key, nextSearchStart);
}
}
return sb.toString();
}
public static boolean isAlphanumeric(final String str) {
for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i);
if ((c < 0x30) || ((c >= 0x3a) && (c <= 0x40)) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a)) {
return false;
}
}
return true;
}
public static boolean isAlphanumericUnd(final CharSequence str) {
for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i);
if ((c < 0x30) || ((c >= 0x3a) && (c <= 0x40)) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a) || (c == '_')) {
return false;
}
}
return true;
}
public static boolean isAlpha(final String str) {
for (int i = 0; i < str.length(); i++) {
final char c = str.charAt(i);
if ((c <= 0x40) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a)) {
return false;
}
}
return true;
}
public static String join(final Collection<?> collection, final String delimiter) {
return join(collection.toArray(), delimiter);
}
public static String joinOrdered(final Collection<?> collection, final String delimiter) {
final Object[] array = collection.toArray();
Arrays.sort(array, new Comparator<Object>() {
@Override
public int compare(final Object a, final Object b) {
return a.hashCode() - b.hashCode();
}
});
return join(array, delimiter);
}
public static String join(final Collection<?> collection, final char delimiter) {
return join(collection.toArray(), delimiter + "");
}
public static boolean isAsciiPrintable(final char c) {
return (c >= ' ') && (c < '');
}
public static boolean isAsciiPrintable(final String s) {
for (final char c : s.toCharArray()) {
if (!isAsciiPrintable(c)) {
return false;
}
}
return true;
}
public static int getLevenshteinDistance(String s, String t) {
int n = s.length();
int m = t.length();
if (n == 0) {
return m;
} else if (m == 0) {
return n;
}
if (n > m) {
final String tmp = s;
s = t;
t = tmp;
n = m;
m = t.length();
}
int p[] = new int[n + 1];
int d[] = new int[n + 1];
int _d[];
int i;
int j;
char t_j;
int cost;
for (i = 0; i <= n; i++) {
p[i] = i;
}
for (j = 1; j <= m; j++) {
t_j = t.charAt(j - 1);
d[0] = j;
for (i = 1; i <= n; i++) {
cost = s.charAt(i - 1) == t_j ? 0 : 1;
d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
}
_d = p;
p = d;
d = _d;
}
return p[n];
}
public static <T> String join(Collection<T> arr, final String delimiter, Function<T, String> funx) {
final StringBuilder result = new StringBuilder();
int i = 0;
for (T obj : arr) {
if (i > 0) {
result.append(delimiter);
}
result.append(funx.apply(obj));
i++;
}
return result.toString();
}
public static String join(final Object[] array, final String delimiter) {
switch (array.length) {
case 0:
return "";
case 1:
return array[0].toString();
default:
final StringBuilder result = new StringBuilder();
for (int i = 0, j = array.length; i < j; i++) {
if (i > 0) {
result.append(delimiter);
}
result.append(array[i]);
}
return result.toString();
}
}
public static Integer toInteger(String string, int start, int end) {
int value = 0;
char char0 = string.charAt(0);
boolean negative;
if (char0 == '-') {
negative = true;
start++;
}
else negative = false;
for (int i = start; i < end; i++) {
char c = string.charAt(i);
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = value * 10 + c - '0';
break;
default:
return null;
}
}
return negative ? -value : value;
}
public static String join(final int[] array, final String delimiter) {
final Integer[] wrapped = new Integer[array.length];
for (int i = 0; i < array.length; i++) {
wrapped[i] = array[i];
}
return join(wrapped, delimiter);
}
public static boolean isEqualToAny(final String a, final String... args) {
for (final String arg : args) {
if (StringMan.isEqual(a, arg)) {
return true;
}
}
return false;
}
public static boolean isEqualIgnoreCaseToAny(final String a, final String... args) {
for (final String arg : args) {
if (StringMan.isEqualIgnoreCase(a, arg)) {
return true;
}
}
return false;
}
public static boolean isEqual(final String a, final String b) {
return ((a == b) || ((a != null) && (b != null) && (a.length() == b.length()) && (a.hashCode() == b.hashCode()) && a.equals(b)));
}
public static boolean isEqualIgnoreCase(final String a, final String b) {
return ((a == b) || ((a != null) && (b != null) && (a.length() == b.length()) && a.equalsIgnoreCase(b)));
}
public static String repeat(final String s, final int n) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append(s);
}
return sb.toString();
}
}

View File

@ -0,0 +1,428 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.RunnableVal;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import javax.annotation.Nullable;
public abstract class TaskManager {
public static TaskManager IMP;
private ForkJoinPool pool = new ForkJoinPool();
/**
* Run a repeating task on the main thread
*
* @param r
* @param interval in ticks
* @return
*/
public abstract int repeat(final Runnable r, final int interval);
/**
* Run a repeating task asynchronously
*
* @param r
* @param interval in ticks
* @return
*/
public abstract int repeatAsync(final Runnable r, final int interval);
/**
* Run a task asynchronously
*
* @param r
*/
public abstract void async(final Runnable r);
/**
* Run a task on the main thread
*
* @param r
*/
public abstract void task(final Runnable r);
/**
* Get the public ForkJoinPool<br>
* - ONLY SUBMIT SHORT LIVED TASKS<br>
* - DO NOT USE SLEEP/WAIT/LOCKS IN ANY SUBMITTED TASKS<br>
*
* @return
*/
public ForkJoinPool getPublicForkJoinPool() {
return pool;
}
/**
* Run a buch of tasks in parallel using the shared thread pool
*
* @param runnables
*/
public void parallel(Collection<Runnable> runnables) {
// if (!Fawe.get().isJava8()) {
// ExecutorCompletionService c = new ExecutorCompletionService(pool);
// for (Runnable run : runnables) {
// c.submit(run, null);
// }
// try {
// for (int i = 0; i < runnables.size(); i++) {
// c.take();
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return;
// }
for (Runnable run : runnables) {
pool.submit(run);
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
/**
* Run a bunch of tasks in parallel
*
* @param runnables The tasks to run
* @param numThreads Number of threads (null = config.yml parallel threads)
*/
@Deprecated
public void parallel(Collection<Runnable> runnables, @Nullable Integer numThreads) {
if (runnables == null) {
return;
}
if (numThreads == null) {
numThreads = Settings.IMP.QUEUE.PARALLEL_THREADS;
}
if (numThreads <= 1) {
for (Runnable run : runnables) {
if (run != null) {
run.run();
}
}
return;
}
int numRuns = runnables.size();
int amountPerThread = 1 + numRuns / numThreads;
final Runnable[][] split = new Runnable[numThreads][amountPerThread];
Thread[] threads = new Thread[numThreads];
int i = 0;
int j = 0;
for (Runnable run : runnables) {
split[i][j] = run;
if (++i >= numThreads) {
i = 0;
j++;
}
}
for (i = 0; i < threads.length; i++) {
final Runnable[] toRun = split[i];
Thread thread = threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < toRun.length; j++) {
Runnable run = toRun[j];
if (run != null) {
run.run();
}
}
}
});
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Disable async catching for a specific task
*
* @param queue
* @param run
*/
public void runUnsafe(FaweQueue queue, Runnable run) {
queue.startSet(true);
try {
run.run();
} catch (Throwable e) {
e.printStackTrace();
}
queue.endSet(true);
}
/**
* Run a task on the current thread or asynchronously
* - If it's already the main thread, it will jst call run()
*
* @param r
* @param async
*/
public void taskNow(final Runnable r, boolean async) {
if (async) {
async(r);
} else if (r != null) {
r.run();
}
}
/**
* Run a task as soon as possible on the main thread
* - Non blocking if not calling from the main thread
*
* @param r
*/
public void taskNowMain(final Runnable r) {
if (r == null) {
return;
}
if (Thread.currentThread() == Fawe.get().getMainThread()) {
r.run();
} else {
task(r);
}
}
/**
* Run a task as soon as possible not on the main thread
*
* @param r
* @see com.boydti.fawe.Fawe#isMainThread()
*/
public void taskNowAsync(final Runnable r) {
taskNow(r, Fawe.isMainThread());
}
/**
* Run a task on the main thread at the next tick or now async
*
* @param r
* @param async
*/
public void taskSoonMain(final Runnable r, boolean async) {
if (async) {
async(r);
} else {
task(r);
}
}
/**
* Run a task later on the main thread
*
* @param r
* @param delay in ticks
*/
public abstract void later(final Runnable r, final int delay);
/**
* Run a task later asynchronously
*
* @param r
* @param delay in ticks
*/
public abstract void laterAsync(final Runnable r, final int delay);
/**
* Cancel a task
*
* @param task
*/
public abstract void cancel(final int task);
/**
* Break up a task and run it in fragments of 5ms.<br>
* - Each task will run on the main thread.<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
* @param <T>
*/
public <T> void objectTask(Collection<T> objects, final RunnableVal<T> task, final Runnable whenDone) {
final Iterator<T> iterator = objects.iterator();
task(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
boolean hasNext;
while ((hasNext = iterator.hasNext()) && System.currentTimeMillis() - start < 5) {
task.value = iterator.next();
task.run();
}
if (!hasNext) {
later(whenDone, 1);
} else {
later(this, 1);
}
}
});
}
/**
* 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
*/
public <T> T sync(final RunnableVal<T> function) {
return sync(function, Integer.MAX_VALUE);
}
public <T> T sync(final Supplier<T> function) {
return sync(function, Integer.MAX_VALUE);
}
public void wait(AtomicBoolean running, int timout) {
try {
long start = System.currentTimeMillis();
synchronized (running) {
while (running.get()) {
running.wait(timout);
if (running.get() && System.currentTimeMillis() - start > Settings.IMP.QUEUE.DISCARD_AFTER_MS) {
new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ").printStackTrace();
Fawe.debug("For full debug information use: /fawe threads");
}
}
}
} catch (InterruptedException e) {
MainUtil.handleError(e);
}
}
public void notify(AtomicBoolean running) {
running.set(false);
synchronized (running) {
running.notifyAll();
}
}
public <T> T syncWhenFree(final RunnableVal<T> function) {
return syncWhenFree(function, Integer.MAX_VALUE);
}
public void taskWhenFree(Runnable run) {
if (Fawe.isMainThread()) {
run.run();
} else {
SetQueue.IMP.addTask(run);
}
}
/**
* Run a task on the main thread when the TPS is high enough, 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>
* @return
*/
public <T> T syncWhenFree(final RunnableVal<T> function, int timeout) {
if (Fawe.get().getMainThread() == Thread.currentThread()) {
function.run();
return function.value;
}
final AtomicBoolean running = new AtomicBoolean(true);
RunnableVal<RuntimeException> run = new RunnableVal<RuntimeException>() {
@Override
public void run(RuntimeException value) {
try {
function.run();
} catch (RuntimeException e) {
this.value = e;
} catch (Throwable neverHappens) {
MainUtil.handleError(neverHappens);
} finally {
running.set(false);
}
synchronized (function) {
function.notifyAll();
}
}
};
SetQueue.IMP.addTask(run);
try {
synchronized (function) {
while (running.get()) {
function.wait(timeout);
}
}
} catch (InterruptedException e) {
MainUtil.handleError(e);
}
if (run.value != null) {
throw run.value;
}
return function.value;
}
/**
* 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>
* @return
*/
public <T> T sync(final RunnableVal<T> function, int timeout) {
return sync((Supplier<T>) function, timeout);
}
public <T> T sync(final Supplier<T> function, int timeout) {
if (Fawe.get().getMainThread() == Thread.currentThread()) {
return function.get();
}
final AtomicBoolean running = new AtomicBoolean(true);
RunnableVal<Object> run = new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
this.value = function.get();
} catch (RuntimeException e) {
this.value = e;
} catch (Throwable neverHappens) {
MainUtil.handleError(neverHappens);
} finally {
running.set(false);
}
synchronized (function) {
function.notifyAll();
}
}
};
SetQueue.IMP.addTask(run);
try {
synchronized (function) {
while (running.get()) {
function.wait(timeout);
}
}
} catch (InterruptedException e) {
MainUtil.handleError(e);
}
if (run.value != null && run.value instanceof RuntimeException) {
throw (RuntimeException) run.value;
}
return (T) run.value;
}
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.util;
public interface TextureHolder {
TextureUtil getTextureUtil();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,128 @@
package com.boydti.fawe.util;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweVersion;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FawePlayer;
import com.boydti.fawe.util.chat.Message;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Scanner;
public class Updater {
private FaweVersion newVersion;
private String changes;
private volatile boolean pending;
private File pendingFile, destFile;
private String versionString;
public synchronized String getChanges() {
if (changes == null) {
try (Scanner scanner = new Scanner(new URL("https://empcraft.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash)).openStream(), "UTF-8")) {
changes = scanner.useDelimiter("\\A").next();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
return changes;
}
public synchronized boolean isOutdated() {
return newVersion != null;
}
public boolean hasPending(FawePlayer fp) {
return (pending && fp.hasPermission("fawe.admin"));
}
public synchronized void confirmUpdate(FawePlayer fp) {
if (pending && fp.hasPermission("fawe.admin")) {
Fawe.debug("Updated FAWE to " + versionString + " @ " + pendingFile);
String url = "https://empcraft.com/fawe/cl?" + Integer.toHexString(Fawe.get().getVersion().hash);
new Message().prefix().text("A FAWE update is available:")
.text("\n&8 - &a/fawe update &8 - &7Update the plugin")
.cmdTip("fawe update")
.text("\n&8 - &a/fawe changelog")
.cmdTip("fawe changelog")
.text("&8 - &7( &9&o" + url + " &7)")
.link(url)
.send(fp);
}
}
public synchronized boolean installUpdate(FawePlayer fp) {
if (pending && (fp == null || fp.hasPermission("fawe.admin")) && pendingFile.exists()) {
pending = false;
File outFileParent = destFile.getParentFile();
if (!outFileParent.exists()) {
outFileParent.mkdirs();
}
pendingFile.renameTo(destFile);
return true;
}
return false;
}
public synchronized void getUpdate(String platform, FaweVersion currentVersion) {
if (currentVersion == null || platform == null) {
return;
}
try {
String downloadUrl = "https://ci.athion.net/job/FastAsyncWorldEdit/lastSuccessfulBuild/artifact/target/FastAsyncWorldEdit-%platform%-%version%.jar";
String versionUrl = "https://empcraft.com/fawe/version.php?%platform%";
URL url = new URL(versionUrl.replace("%platform%", platform));
try (Scanner reader = new Scanner(url.openStream())) {
this.versionString = reader.next();
FaweVersion version = new FaweVersion(versionString);
if (version.isNewer(newVersion != null ? newVersion : currentVersion)) {
newVersion = version;
URL download = new URL(downloadUrl.replaceAll("%platform%", platform).replaceAll("%version%", versionString));
try (ReadableByteChannel rbc = Channels.newChannel(download.openStream())) {
File jarFile = MainUtil.getJarFile();
File finalFile = new File(jarFile.getParent(), "update-confirm" + File.separator + jarFile.getName());
File outFile = new File(jarFile.getParent(), "update-confirm" + File.separator + jarFile.getName().replace(".jar", ".part"));
boolean exists = outFile.exists();
if (exists) {
outFile.delete();
} else {
File outFileParent = outFile.getParentFile();
if (!outFileParent.exists()) {
outFileParent.mkdirs();
}
}
try (FileOutputStream fos = new FileOutputStream(outFile)) {
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
}
outFile.renameTo(finalFile);
if (Settings.IMP.UPDATE.equalsIgnoreCase("true")) {
pending = true;
pendingFile = finalFile;
destFile = new File(jarFile.getParent(), "update" + File.separator + jarFile.getName());
installUpdate(null);
Fawe.debug("Updated FAWE to " + versionString + " @ " + pendingFile);
MainUtil.sendAdmin("&a/restart&7 to update FAWE with these changes: &c/fawe changelog &7or&c " + "https://empcraft.com/fawe/cl?" + Integer.toHexString(currentVersion.hash));
} else if (!Settings.IMP.UPDATE.equalsIgnoreCase("false")) {
pendingFile = finalFile;
destFile = new File(jarFile.getParent(), "update" + File.separator + jarFile.getName());
pending = true;
for (final FawePlayer<?> player : Fawe.get().getCachedPlayers()) {
confirmUpdate(player);
}
}
}
}
}
} catch (Throwable ignore) {
}
}
}

View File

@ -0,0 +1,235 @@
package com.boydti.fawe.util;
import com.boydti.fawe.config.BBC;
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.exception.FaweException;
import com.boydti.fawe.object.extent.NullExtent;
import com.boydti.fawe.regions.FaweMask;
import com.boydti.fawe.regions.FaweMaskManager;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class WEManager {
public final static WEManager IMP = new WEManager();
public final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>();
public void cancelEditSafe(Extent parent, BBC reason) throws FaweException {
try {
final Field field = AbstractDelegateExtent.class.getDeclaredField("extent");
field.setAccessible(true);
Object currentExtent = field.get(parent);
if (!(currentExtent instanceof NullExtent)) {
field.set(parent, new NullExtent((Extent) field.get(parent), reason));
}
} catch (final Exception e) {
MainUtil.handleError(e);
}
throw new FaweException(reason);
}
public void cancelEdit(Extent parent, BBC reason) throws WorldEditException {
cancelEditSafe(parent, reason);
}
public boolean maskContains(final HashSet<RegionWrapper> mask, final int x, final int z) {
for (final RegionWrapper region : mask) {
if ((x >= region.minX) && (x <= region.maxX) && (z >= region.minZ) && (z <= region.maxZ)) {
return true;
}
}
return false;
}
public boolean maskContains(RegionWrapper[] mask, final int x, final int z) {
switch (mask.length) {
case 0:
return false;
case 1:
return mask[0].isIn(x, z);
default:
for (final RegionWrapper region : mask) {
if (region.isIn(x, z)) {
return true;
}
}
return false;
}
}
@Deprecated
public Region[] getMask(final FawePlayer<?> player) {
return getMask(player, FaweMaskManager.MaskType.getDefaultMaskType());
}
public boolean isIn(int x, int y, int z, Region region) {
if (region.contains(x, y, z)) {
return true;
}
return false;
}
/**
* Get a player's mask
*
* @param player
* @return
*/
public Region[] getMask(final FawePlayer<?> player, FaweMaskManager.MaskType type) {
if (!Settings.IMP.REGION_RESTRICTIONS || player.hasPermission("fawe.bypass") || player.hasPermission("fawe.bypass.regions")) {
return new Region[]{RegionWrapper.GLOBAL()};
}
FaweLocation loc = player.getLocation();
String world = loc.world;
if (!world.equals(player.getMeta("lastMaskWorld"))) {
player.deleteMeta("lastMaskWorld");
player.deleteMeta("lastMask");
}
player.setMeta("lastMaskWorld", world);
Set<FaweMask> masks = player.getMeta("lastMask");
Set<Region> backupRegions = new HashSet<>();
Set<Region> regions = new HashSet<>();
if (masks == null) {
masks = new HashSet<>();
} else {
synchronized (masks) {
boolean removed = false;
if (!masks.isEmpty()) {
Iterator<FaweMask> iter = masks.iterator();
while (iter.hasNext()) {
FaweMask mask = iter.next();
if (mask.isValid(player, type)) {
Region region = mask.getRegion();
if (region.contains(loc.x, loc.y, loc.z)) {
regions.add(region);
} else {
removed = true;
backupRegions.add(region);
}
} else {
removed = true;
iter.remove();
}
}
}
if (!removed) return regions.toArray(new Region[regions.size()]);
}
}
Set<FaweMask> tmpMasks = new HashSet<>();
for (final FaweMaskManager manager : managers) {
if (player.hasPermission("fawe." + manager.getKey())) {
try {
final FaweMask mask = manager.getMask(player, FaweMaskManager.MaskType.getDefaultMaskType());
if (mask != null) {
regions.add(mask.getRegion());
masks.add(mask);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
if (!tmpMasks.isEmpty()) {
masks = tmpMasks;
regions = masks.stream().map(mask -> mask.getRegion()).collect(Collectors.toSet());
} else {
regions.addAll(backupRegions);
}
if (!masks.isEmpty()) {
player.setMeta("lastMask", masks);
} else {
player.deleteMeta("lastMask");
}
return regions.toArray(new Region[regions.size()]);
}
public boolean intersects(final Region region1, final Region region2) {
Vector rg1P1 = region1.getMinimumPoint();
Vector rg1P2 = region1.getMaximumPoint();
Vector rg2P1 = region2.getMinimumPoint();
Vector rg2P2 = region2.getMaximumPoint();
return (rg1P1.getBlockX() <= rg2P2.getBlockX()) && (rg1P2.getBlockX() >= rg2P1.getBlockX()) && (rg1P1.getBlockZ() <= rg2P2.getBlockZ()) && (rg1P2.getBlockZ() >= rg2P1.getBlockZ());
}
public boolean regionContains(final Region selection, final HashSet<Region> mask) {
for (final Region region : mask) {
if (this.intersects(region, selection)) {
return true;
}
}
return false;
}
public boolean delay(final FawePlayer<?> player, final String command) {
final long start = System.currentTimeMillis();
return this.delay(player, new Runnable() {
@Override
public void run() {
try {
if ((System.currentTimeMillis() - start) > 1000) {
BBC.WORLDEDIT_RUN.send(FawePlayer.wrap(player));
}
TaskManager.IMP.task(new Runnable() {
@Override
public void run() {
final long start = System.currentTimeMillis();
player.executeCommand(command.substring(1));
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
SetQueue.IMP.addEmptyTask(new Runnable() {
@Override
public void run() {
if ((System.currentTimeMillis() - start) > 1000) {
BBC.WORLDEDIT_COMPLETE.send(FawePlayer.wrap(player));
}
}
});
}
}, 2);
}
});
} catch (final Exception e) {
MainUtil.handleError(e);
}
}
}, false, false);
}
public boolean delay(final FawePlayer<?> player, final Runnable whenDone, final boolean delayed, final boolean onlyDelayedExecution) {
final boolean free = SetQueue.IMP.addEmptyTask(null);
if (free) {
if (delayed) {
if (whenDone != null) {
whenDone.run();
}
} else {
if ((whenDone != null) && !onlyDelayedExecution) {
whenDone.run();
} else {
return false;
}
}
} else {
if (!delayed && (player != null)) {
BBC.WORLDEDIT_DELAYED.send(player);
}
SetQueue.IMP.addEmptyTask(whenDone);
}
return true;
}
}

View File

@ -0,0 +1,21 @@
package com.boydti.fawe.util.chat;
import com.boydti.fawe.object.FawePlayer;
public interface ChatManager<T> {
T builder();
void color(Message message, String color);
void tooltip(Message message, Message... tooltip);
void command(Message message, String command);
void text(Message message, String text);
void send(Message message, FawePlayer player);
void suggest(Message message, String command);
void link(Message message, String url);
}

View File

@ -0,0 +1,147 @@
package com.boydti.fawe.util.chat;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Actor;
import java.util.Objects;
public class Message {
private Object builder;
private boolean active;
public Message() {
try {
reset(Fawe.get().getChatManager());
} catch (Throwable e) {
Fawe.debug("Doesn't support fancy chat for " + Fawe.imp().getPlatform());
Fawe.get().setChatManager(new PlainChatManager());
reset(Fawe.get().getChatManager());
}
active = !(Fawe.get().getChatManager() instanceof PlainChatManager);
}
public Message(BBC caption, Object... args) {
this(BBC.getPrefix() + caption.format(args));
}
public Message(String text) {
this();
text(text);
}
public <T> T $(ChatManager<T> manager) {
return (T) this.builder;
}
public <T> T reset(ChatManager<T> manager) {
return (T) (this.builder = manager.builder());
}
public Message activeText(String text) {
if (active) {
text(text);
}
return this;
}
public boolean supportsInteraction() {
return active;
}
public Message text(BBC caption, Object... args) {
return text(caption.format(args));
}
public Message text(Object text) {
Fawe.get().getChatManager().text(this, BBC.color(Objects.toString(text)));
return this;
}
public Message link(String text) {
Fawe.get().getChatManager().link(this, text);
return this;
}
public Message tooltip(Message... tooltip) {
Fawe.get().getChatManager().tooltip(this, tooltip);
return this;
}
public Message tooltip(String tooltip) {
return tooltip(new Message(tooltip));
}
public Message command(String command) {
Fawe.get().getChatManager().command(this, (WorldEdit.getInstance().getConfiguration().noDoubleSlash ? "" : "/") + command);
return this;
}
public Message prefix() {
return text(BBC.getPrefix());
}
public Message newline() {
return text("\n");
}
public Message cmdTip(String commandAndTooltip) {
return tooltip(commandAndTooltip).command(commandAndTooltip);
}
public Message linkTip(String linkAndTooltip) {
return tooltip(linkAndTooltip).link(linkAndTooltip);
}
public Message cmdOptions(String prefix, String suffix, String... options) {
for (int i = 0; i < options.length; i++) {
if (i != 0) text(" &8|&7 ");
text("&7[&a" + options[i] + "&7]")
.cmdTip(prefix + options[i] + suffix);
}
return this;
}
public Message suggestTip(String commandAndTooltip) {
return tooltip(commandAndTooltip).suggest(commandAndTooltip);
}
public Message suggest(String command) {
Fawe.get().getChatManager().suggest(this, command);
return this;
}
public Message color(String color) {
Fawe.get().getChatManager().color(this, BBC.color(color));
return this;
}
public void send(Actor player) {
send(FawePlayer.wrap(player));
}
public void send(FawePlayer player) {
Fawe.get().getChatManager().send(this, player);
}
public Message paginate(String baseCommand, int page, int totalPages) {
if (!active) {
return text(BBC.PAGE_FOOTER.f(baseCommand, page + 1));
}
if (page < totalPages && page > 1) { // Back | Next
this.text("&f<<").command(baseCommand + " " + (page - 1)).text("&8 | ").text("&f>>")
.command(baseCommand + " " + (page + 1));
} else if (page <= 1 && totalPages > page) { // Next
this.text("&8 -").text(" | ").text("&f>>")
.command(baseCommand + " " + (page + 1));
} else if (page == totalPages && totalPages > 1) { // Back
this.text("&f<<").command(baseCommand + " " + (page - 1)).text("&8 | ").text("- ");
} else {
this.text("&8 - | - ");
}
return this;
}
}

View File

@ -0,0 +1,47 @@
package com.boydti.fawe.util.chat;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.object.FawePlayer;
import java.util.ArrayList;
import java.util.List;
public class PlainChatManager implements ChatManager<List<StringBuilder>> {
@Override
public List<StringBuilder> builder() {
return new ArrayList<>();
}
@Override
public void color(Message message, String color) {
List<StringBuilder> parts = message.$(this);
parts.get(parts.size() - 1).insert(0, color);
}
@Override
public void tooltip(Message message, Message... tooltips) {}
@Override
public void command(Message message, String command) {}
@Override
public void text(Message message, String text) {
message.$(this).add(new StringBuilder(BBC.color(text)));
}
@Override
public void send(Message plotMessage, FawePlayer player) {
StringBuilder built = new StringBuilder();
for (StringBuilder sb : plotMessage.$(this)) {
built.append(sb);
}
player.sendMessage(built.toString());
}
@Override
public void suggest(Message plotMessage, String command) {}
@Override
public void link(Message message, String url) {}
}

View File

@ -0,0 +1,160 @@
package com.boydti.fawe.util.chat;
import com.boydti.fawe.config.BBC;
import com.boydti.fawe.config.Commands;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.StringMan;
import com.sk89q.minecraft.util.commands.CommandLocals;
import com.sk89q.minecraft.util.commands.Link;
import com.sk89q.worldedit.extension.platform.CommandManager;
import com.sk89q.worldedit.util.command.CommandCallable;
import com.sk89q.worldedit.util.command.CommandMapping;
import com.sk89q.worldedit.util.command.Description;
import com.sk89q.worldedit.util.command.Dispatcher;
import com.sk89q.worldedit.util.command.Parameter;
import com.sk89q.worldedit.util.command.PrimaryAliasComparator;
import com.sk89q.worldedit.util.command.binding.Range;
import com.sk89q.worldedit.util.command.parametric.ParameterData;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class UsageMessage extends Message {
/**
* Create a new usage box.
*
* @param command the command to describe
* @param commandString the command that was used, such as "/we" or "/brush sphere"
*/
public UsageMessage(CommandCallable command, String commandString) {
this(command, commandString, null);
}
/**
* Create a new usage box.
*
* @param command the command to describe
* @param commandString the command that was used, such as "/we" or "/brush sphere"
* @param locals list of locals to use
*/
public UsageMessage(CommandCallable command, String commandString, @Nullable CommandLocals locals) {
checkNotNull(command);
checkNotNull(commandString);
if (command instanceof Dispatcher) {
attachDispatcherUsage((Dispatcher) command, commandString, locals);
} else {
attachCommandUsage(command.getDescription(), commandString);
}
}
private void attachDispatcherUsage(Dispatcher dispatcher, String commandString, @Nullable CommandLocals locals) {
prefix();
text(BBC.HELP_HEADER_SUBCOMMANDS.f());
String prefix = !commandString.isEmpty() ? commandString + " " : "";
List<CommandMapping> list = new ArrayList<CommandMapping>(dispatcher.getCommands());
Collections.sort(list, new PrimaryAliasComparator(CommandManager.COMMAND_CLEAN_PATTERN));
for (CommandMapping mapping : list) {
boolean perm = locals == null || mapping.getCallable().testPermission(locals);
newline();
String cmd = prefix + mapping.getPrimaryAlias();
text((perm ? BBC.HELP_ITEM_ALLOWED : BBC.HELP_ITEM_DENIED).format(cmd, mapping.getDescription().getDescription()));
command(cmd);
}
}
protected String separateArg(String arg) {
return " " + arg;
}
private void attachCommandUsage(Description description, String commandString) {
List<Parameter> params = description.getParameters();
String[] usage;
if (description.getUsage() != null) {
usage = description.getUsage().split(" ", params.size());
} else {
usage = new String[params.size()];
for (int i = 0; i < usage.length; i++) {
Parameter param = params.get(i);
boolean optional = param.isValueFlag() || param.isOptional();
String arg;
if (param.getFlag() != null) {
arg = "-" + param.getFlag();
if (param.isValueFlag())
arg += param.getName();
} else {
arg = param.getName();
if (param.getDefaultValue() != null && param.getDefaultValue().length > 0)
arg += "=" + StringMan.join(param.getDefaultValue(), ",");
}
usage[i] = optional ? ("[" + arg + "]") : ("<" + arg + ">");
}
}
prefix();
text("&cUsage: ");
text("&7" + commandString);
suggestTip(commandString + " ");
for (int i = 0; i < usage.length; i++) {
String argStr = usage[i];
text(separateArg(argStr.replaceAll("[\\[|\\]|<|>]", "&0$0&7")));
if (params.isEmpty()) continue;
Parameter param = params.get(i);
StringBuilder tooltip = new StringBuilder();
String command = null;
String webpage = null;
tooltip.append("Name: " + param.getName());
if (param instanceof ParameterData) {
ParameterData pd = (ParameterData) param;
Type type = pd.getType();
if (type instanceof Class) {
tooltip.append("\nType: " + ((Class) type).getSimpleName());
}
Range range = MainUtil.getOf(pd.getModifiers(), Range.class);
if (range != null) {
String min = range.min() == Double.MIN_VALUE ? "(-∞" : ("[" + range.min());
String max = range.max() == Double.MAX_VALUE ? "∞)" : (range.max() + "]");
tooltip.append("\nRange: " + min + "," + max);
}
if (type instanceof Class) {
Link link = (Link) ((Class) type).getAnnotation(Link.class);
if (link != null) {
if (link.value().startsWith("http")) webpage = link.value();
else command = Commands.getAlias(link.clazz(), link.value());
}
}
}
tooltip.append("\nOptional: " + (param.isOptional() || param.isValueFlag()));
if (param.getDefaultValue() != null && param.getDefaultValue().length >= 0) {
tooltip.append("\nDefault: " + param.getDefaultValue()[0]);
} else if (argStr.contains("=")) {
tooltip.append("\nDefault: " + argStr.split("[=|\\]|>]")[1]);
}
if (command != null || webpage != null) {
tooltip.append("\nClick for more info");
}
tooltip(tooltip.toString());
if (command != null) command(command);
if (webpage != null) link(webpage);
}
newline();
if (description.getHelp() != null) {
text("&cHelp: &7" + description.getHelp());
} else if (description.getDescription() != null) {
text("&cDescription: &7" + description.getDescription());
} else {
text("No further help is available.");
}
}
}

View File

@ -0,0 +1,18 @@
package com.boydti.fawe.util.cui;
import com.boydti.fawe.object.FawePlayer;
import com.sk89q.worldedit.internal.cui.CUIEvent;
public abstract class CUI {
private final FawePlayer player;
public CUI(FawePlayer player) {
this.player = player;
}
public <T> FawePlayer<T> getPlayer() {
return player;
}
public abstract void dispatchCUIEvent(CUIEvent event);
}

View File

@ -0,0 +1,31 @@
package com.boydti.fawe.util.gui;
import com.boydti.fawe.object.FawePlayer;
import java.net.URL;
import java.util.Map;
import java.util.function.Consumer;
import javax.annotation.Nullable;
public interface FormBuilder<T> {
FormBuilder setTitle(String text);
FormBuilder setIcon(URL icon);
FormBuilder addButton(String text, @Nullable URL image);
FormBuilder addDropdown(String text, int def, String... options);
FormBuilder addInput(String text, String placeholder, String def);
FormBuilder addLabel(String text);
FormBuilder addSlider(String text, double min, double max, int step, double def);
FormBuilder addStepSlider(String text, int def, String... options);
FormBuilder addToggle(String text, boolean def);
FormBuilder setResponder(Consumer<Map<Integer, Object>> handler);
void display(FawePlayer<T> fp);
}

View File

@ -0,0 +1,7 @@
package com.boydti.fawe.util.image;
import java.awt.image.BufferedImage;
public interface Drawable {
public BufferedImage draw();
}

View File

@ -0,0 +1,180 @@
package com.boydti.fawe.util.image;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.util.command.parametric.ParameterException;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ImageUtil {
public static BufferedImage getScaledInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality)
{
if (img.getHeight() == targetHeight && img.getWidth() == targetWidth) {
return img;
}
int type = (img.getTransparency() == Transparency.OPAQUE) ?
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
return ret;
}
public static void fadeAlpha(BufferedImage image) {
int[] raw = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
int width = image.getWidth();
int height = image.getHeight();
int centerX = width / 2;
int centerZ = height / 2;
float invRadiusX = 1f / centerX;
float invRadiusZ = 1f / centerZ;
float[] sqrX = new float[width];
float[] sqrZ = new float[height];
for (int x = 0; x < width; x++) {
float distance = Math.abs(x - centerX) * invRadiusX;
sqrX[x] = distance * distance;
}
for (int z = 0; z < height; z++) {
float distance = Math.abs(z - centerZ) * invRadiusZ;
sqrZ[z] = distance * distance;
}
for (int z = 0, index = 0; z < height; z++) {
float dz2 = sqrZ[z];
for (int x = 0; x < width; x++, index++) {
int color = raw[index];
int alpha = (color >> 24) & 0xFF;
if (alpha != 0) {
float dx2 = sqrX[x];
float distSqr = dz2 + dx2;
if (distSqr > 1) raw[index] = 0;
else {
alpha = (int) (alpha * (1 - distSqr));
raw[index] = (color & 0x00FFFFFF) + (alpha << 24);
}
}
}
}
}
public static void scaleAlpha(BufferedImage image, double alphaScale) {
int[] raw = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
int defined = (MathMan.clamp((int) (255 * alphaScale), 0, 255)) << 24;
for (int i = 0; i < raw.length; i++) {
int color = raw[i];
int alpha = ((color >> 24) & 0xFF);
switch (alpha) {
case 0:
continue;
case 255:
raw[i] = (color & 0x00FFFFFF) + defined;
continue;
default:
alpha = MathMan.clamp((int) (alpha * alphaScale), 0, 255);
raw[i] = (color & 0x00FFFFFF) + (alpha << 24);
continue;
}
}
}
public static 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;
int red = (int) (totalRed / a);
int green = (int) (totalGreen / a);
int blue = (int) (totalBlue / a);
int alpha = (int) (totalAlpha / a);
return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0);
}
public static BufferedImage getImage(String arg) throws ParameterException {
try {
if (arg.startsWith("http")) {
if (arg.contains("imgur.com") && !arg.contains("i.imgur.com")) {
arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png";
}
URL url = new URL(arg);
BufferedImage img = MainUtil.readImage(url);
if (img == null) {
throw new IOException("Failed to read " + url + ", please try again later");
}
return img;
} else if (arg.startsWith("file:/")) {
arg = arg.replaceFirst("file:/+", "");
File file = MainUtil.getFile(MainUtil.getFile(Fawe.imp().getDirectory(), com.boydti.fawe.config.Settings.IMP.PATHS.HEIGHTMAP), arg);
return MainUtil.readImage(file);
} else {
throw new ParameterException("Invalid image " + arg);
}
} catch (IOException e) {
throw new ParameterException(e);
}
}
}

View File

@ -0,0 +1,7 @@
package com.boydti.fawe.util.image;
import java.io.Closeable;
public interface ImageViewer extends Closeable{
public void view(Drawable drawable);
}

View File

@ -0,0 +1,419 @@
package com.boydti.fawe.util.metrics;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweVersion;
import com.boydti.fawe.configuration.file.YamlConfiguration;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.io.PGZIPOutputStream;
import com.boydti.fawe.util.TaskManager;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.net.ssl.HttpsURLConnection;
/**
* bStats collects some data for plugin authors.
*
* Check out https://bStats.org/ to learn more about bStats!
*/
public class BStats implements Closeable {
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private final String url;
// The plugin
private final String plugin;
private final String platform;
private final boolean online;
private final String serverVersion;
private final String pluginVersion;
private Timer timer;
private Gson gson = new Gson();
// Is bStats enabled on this server?
private volatile boolean enabled;
// The uuid of the server
private UUID serverUUID;
// Should failed requests be logged?
private boolean logFailedRequests = false;
// A list with all known metrics class objects including this one
private static Class<?> usedMetricsClass;
private static final ConcurrentLinkedQueue<Object> knownMetricsInstances = new ConcurrentLinkedQueue<>();
public BStats() {
this("FastAsyncWorldEdit", Fawe.get().getVersion(), Fawe.imp().getPlatformVersion(), Fawe.imp().getPlatform(), Fawe.imp().isOnlineMode());
}
public int getPlayerCount() {
return Fawe.imp() == null ? 1 : Fawe.imp().getPlayerCount();
}
private BStats(String plugin, FaweVersion faweVersion, String serverVersion, String platform, boolean online) {
this.url = "https://bStats.org/submitData/" + platform;
this.plugin = plugin;
this.pluginVersion = "" + faweVersion;
this.serverVersion = serverVersion;
this.platform = platform;
this.online = online;
File configFile = new File(getJarFile().getParentFile(), "bStats" + File.separator + "config.yml");
if (!configFile.exists()) {
configFile.getParentFile().mkdirs();
try {
configFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
if (config.isSet("serverUuid")) {
try {
serverUUID = UUID.fromString(config.getString("serverUuid"));
} catch (IllegalArgumentException ignore) {}
}
// Check if the config file exists
if (serverUUID == null) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", (serverUUID = UUID.randomUUID()).toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
"To honor their work, you should not disable it.\n" +
"This has nearly no effect on the server performance!\n" +
"Check out https://bStats.org/ to learn more :)"
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) { }
}
if (usedMetricsClass != null) {
// Already an instance of this class
linkMetrics(this);
return;
}
this.usedMetricsClass = getFirstBStatsClass();
if (usedMetricsClass == null) {
// Failed to get first metrics class
return;
}
if (usedMetricsClass == getClass()) {
// We are the first! :)
linkMetrics(this);
enabled = true;
} else {
// We aren't the first so we link to the first metrics class
try {
usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null,this);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
if (logFailedRequests) {
System.out.println("Failed to link to first metrics class " + usedMetricsClass.getName() + "!");
}
}
}
}
public void start() {
if (enabled) {
startSubmitting();
}
}
/**
* Links an other metrics class with this class.
* This method is called using Reflection.
*
* @param metrics An object of the metrics class to link.
*/
public static void linkMetrics(Object metrics) {
if (!knownMetricsInstances.contains(metrics)) knownMetricsInstances.add(metrics);
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
data.addProperty("pluginName", plugin);
data.addProperty("pluginVersion", pluginVersion);
JsonArray customCharts = new JsonArray();
data.add("customCharts", customCharts);
return data;
}
private void startSubmitting() {
this.timer = new Timer(true);
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!enabled) {
timer.cancel();
return;
}
submitData();
}
// No 2m delay, as this is only started after the server is loaded
}, 0, 1000*60*30);
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
@Override
public void close() {
enabled = false;
if (timer != null) {
timer.cancel();
}
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
int playerAmount = getPlayerCount();
int onlineMode = online ? 1 : 0;
int managedServers = 1;
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID.toString());
data.addProperty("playerAmount", playerAmount);
data.addProperty("managedServers", managedServers);
data.addProperty("onlineMode", onlineMode);
data.addProperty(platform + "Version", serverVersion);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
final JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Object metrics : knownMetricsInstances) {
Object plugin = TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override
public void run(Object value) {
try {
this.value = metrics.getClass().getMethod("getPluginData").invoke(metrics);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NullPointerException | JsonSyntaxException ignored) {}
}
});
if (plugin != null) {
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else {
pluginData.add(gson.fromJson(plugin.toString(), JsonObject.class));
}
}
}
data.add("plugins", pluginData);
try {
// Send the data
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
System.err.println("Could not submit plugin stats!");
}
}
}
/**
* Gets the first bStat Metrics class.
*
* @return The first bStats metrics class.
*/
private Class<?> getFirstBStatsClass() {
Path configPath = getJarFile().toPath().getParent().resolve("bStats");
configPath.toFile().mkdirs();
File tempFile = new File(configPath.toFile(), "temp.txt");
try {
String className = readFile(tempFile);
if (className != null) {
try {
// Let's check if a class with the given name exists.
return Class.forName(className);
} catch (ClassNotFoundException ignored) { }
}
writeFile(tempFile, getClass().getName());
return getClass();
} catch (IOException e) {
if (logFailedRequests) {
System.err.println("Failed to get first bStats class!");
}
return null;
}
}
private File getJarFile() {
try {
URL url = BStats.class.getProtectionDomain().getCodeSource().getLocation();
return new File(new URL(url.toURI().toString().split("\\!")[0].replaceAll("jar:file", "file")).toURI().getPath());
} catch (MalformedURLException | URISyntaxException | SecurityException e) {
return new File(".", "plugins");
}
}
/**
* Reads the first line of the file.
*
* @param file The file to read. Cannot be null.
* @return The first line of the file or <code>null</code> if the file does not exist or is empty.
* @throws IOException If something did not work :(
*/
private String readFile(File file) throws IOException {
if (!file.exists()) {
return null;
}
try (
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
) {
return bufferedReader.readLine();
}
}
/**
* Writes a String to a file. It also adds a note for the user,
*
* @param file The file to write to. Cannot be null.
* @param lines The lines to write.
* @throws IOException If something did not work :(
*/
private void writeFile(File file, String... lines) throws IOException {
if (!file.exists()) {
file.createNewFile();
}
try (
FileWriter fileWriter = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)
) {
for (String line : lines) {
bufferedWriter.write(line);
bufferedWriter.newLine();
}
}
}
/**
* Sends the data to the bStats server.
*
* @param data The data to send.
* @throws Exception If the request failed.
*/
private void sendData(JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PGZIPOutputStream gzip = new PGZIPOutputStream(outputStream);
gzip.write(str.getBytes("UTF-8"));
gzip.close();
return outputStream.toByteArray();
}
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.util.task;
public interface DelayedTask<T> {
int getDelay(T previousResult);
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.util.task;
public interface ReceiveTask<T> {
void run(T previous);
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.util.task;
public interface ReturnTask<T> {
T run();
}

View File

@ -0,0 +1,5 @@
package com.boydti.fawe.util.task;
public interface Task<T, V> {
T run(V previousResult);
}

View File

@ -0,0 +1,574 @@
package com.boydti.fawe.util.task;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.object.FaweQueue;
import com.boydti.fawe.object.Metadatable;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.util.SetQueue;
import com.boydti.fawe.util.TaskManager;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
public class TaskBuilder extends Metadatable {
private final ForkJoinPool pool = new ForkJoinPool();
private final ArrayDeque<RunnableTask> tasks;
private Object result = null;
private Thread.UncaughtExceptionHandler handler;
public TaskBuilder() {
this(null);
}
public TaskBuilder(Thread.UncaughtExceptionHandler handler) {
tasks = new ArrayDeque<>();
this.handler = handler;
}
public TaskBuilder async(Task task) {
tasks.add(RunnableTask.adapt(task, TaskType.ASYNC));
return this;
}
public TaskBuilder async(ReceiveTask task) {
tasks.add(RunnableTask.adapt(task, TaskType.ASYNC));
return this;
}
public TaskBuilder async(ReturnTask task) {
tasks.add(RunnableTask.adapt(task, TaskType.ASYNC));
return this;
}
public TaskBuilder async(Runnable task) {
tasks.add(RunnableTask.adapt(task, TaskType.ASYNC));
return this;
}
public TaskBuilder sync(Task task) {
tasks.add(RunnableTask.adapt(task, TaskType.SYNC));
return this;
}
public TaskBuilder sync(ReceiveTask task) {
tasks.add(RunnableTask.adapt(task, TaskType.SYNC));
return this;
}
public TaskBuilder sync(ReturnTask task) {
tasks.add(RunnableTask.adapt(task, TaskType.SYNC));
return this;
}
public TaskBuilder sync(Runnable task) {
tasks.add(RunnableTask.adapt(task, TaskType.SYNC));
return this;
}
public TaskBuilder delay(int ticks) {
tasks.add(RunnableDelayedTask.adapt(ticks));
return this;
}
public TaskBuilder delay(DelayedTask task) {
tasks.add(RunnableDelayedTask.adapt(task));
return this;
}
/**
* Run some sync tasks in parallel<br>
* - All sync parallel tasks which occur directly after each other will be run at the same time
*
* @param run
* @return this
*/
public TaskBuilder syncParallel(Runnable run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_PARALLEL));
return this;
}
public TaskBuilder syncParallel(Task run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_PARALLEL));
return this;
}
public TaskBuilder syncParallel(ReceiveTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_PARALLEL));
return this;
}
public TaskBuilder syncParallel(ReturnTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_PARALLEL));
return this;
}
/**
* Run some async tasks in parallel<br>
* - All async parallel tasks which occur directly after each other will be run at the same time
*
* @param run
* @return this
*/
public TaskBuilder asyncParallel(Runnable run) {
tasks.add(RunnableTask.adapt(run, TaskType.ASYNC_PARALLEL));
return this;
}
public TaskBuilder asyncParallel(Task run) {
tasks.add(RunnableTask.adapt(run, TaskType.ASYNC_PARALLEL));
return this;
}
public TaskBuilder asyncParallel(ReceiveTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.ASYNC_PARALLEL));
return this;
}
public TaskBuilder asyncParallel(ReturnTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.ASYNC_PARALLEL));
return this;
}
/**
* Run a split task when the server has free time<br>
* - i.e. To maintain high tps
* - Use the split() method within task execution
* - FAWE will be able to pause execution at these points
*
* @param run
* @return this
*/
public TaskBuilder syncWhenFree(SplitTask run) {
tasks.add(run);
return this;
}
public TaskBuilder syncWhenFree(Task run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_WHEN_FREE));
return this;
}
public TaskBuilder syncWhenFree(ReceiveTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_WHEN_FREE));
return this;
}
public TaskBuilder syncWhenFree(ReturnTask run) {
tasks.add(RunnableTask.adapt(run, TaskType.SYNC_WHEN_FREE));
return this;
}
public TaskBuilder abortIfTrue(Task<Boolean, Object> run) {
tasks.add(RunnableTask.adapt(run, TaskType.ABORT));
return this;
}
public TaskBuilder abortIfTrue(final Runnable run) {
tasks.add(RunnableTask.adapt(new Task<Boolean, Boolean>() {
@Override
public Boolean run(Boolean previous) {
if (previous == Boolean.TRUE) run.run();
return previous == Boolean.TRUE;
}
}, TaskType.ABORT));
return this;
}
public TaskBuilder abortIfNull(final Runnable run) {
tasks.add(RunnableTask.adapt(new Task<Boolean, Object>() {
@Override
public Boolean run(Object previous) {
if (previous == null) run.run();
return previous == null;
}
}, TaskType.ABORT));
return this;
}
public TaskBuilder abortIfEqual(final Runnable run, final Object other) {
tasks.add(RunnableTask.adapt(new Task<Boolean, Object>() {
@Override
public Boolean run(Object previous) {
if (Objects.equals(previous, other)) run.run();
return Objects.equals(previous, other);
}
}, TaskType.ABORT));
return this;
}
public TaskBuilder abortIfNotEqual(final Runnable run, final Object other) {
tasks.add(RunnableTask.adapt(new Task<Boolean, Object>() {
@Override
public Boolean run(Object previous) {
if (!Objects.equals(previous, other)) run.run();
return !Objects.equals(previous, other);
}
}, TaskType.ABORT));
return this;
}
/**
* Have all async tasks run on a new thread<br>
* - As opposed to trying to using the current thread
*/
public void buildAsync() {
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
build();
}
});
}
/**
* Begins execution of the tasks<br>
* - The builder will attempt to run on the current thread if possible
*/
public void build() {
RunnableTask peek;
while ((peek = tasks.peek()) != null) {
try {
switch (peek.type) {
case DELAY:
DelayedTask task = (DelayedTask) tasks.poll();
RunnableTask next = tasks.peek();
if (next != null) {
switch (next.type) {
case SYNC:
case ABORT:
case SYNC_PARALLEL:
TaskManager.IMP.later(new Runnable() {
@Override
public void run() {
build();
}
}, task.getDelay(result));
return;
default:
TaskManager.IMP.laterAsync(new Runnable() {
@Override
public void run() {
build();
}
}, task.getDelay(result));
return;
}
}
return;
case SYNC:
case SYNC_PARALLEL:
if (!Fawe.isMainThread()) {
TaskManager.IMP.sync(new RunnableVal() {
@Override
public void run(Object value) {
build();
}
});
return;
}
break;
case SYNC_WHEN_FREE:
case ASYNC:
case ASYNC_PARALLEL:
if (Fawe.isMainThread()) {
TaskManager.IMP.async(new Runnable() {
@Override
public void run() {
build();
}
});
return;
}
break;
}
RunnableTask task = tasks.poll();
task.value = result;
switch (task.type) {
case ABORT:
if (((Task<Boolean, Object>) task).run(result)) {
return;
}
break;
case SYNC:
result = task.exec(result);
break;
case SYNC_WHEN_FREE:
if (task instanceof SplitTask) {
SplitTask splitTask = (SplitTask) task;
result = splitTask.execSplit(result);
} else {
result = TaskManager.IMP.syncWhenFree(task);
}
break;
case ASYNC:
result = task.exec(result);
continue;
case SYNC_PARALLEL:
case ASYNC_PARALLEL:
final ArrayList<RunnableTask> parallel = new ArrayList<RunnableTask>();
parallel.add(task);
RunnableTask next = tasks.peek();
while (next != null && next.type == task.type) {
parallel.add(next);
tasks.poll();
next = tasks.peek();
}
for (RunnableTask current : parallel) {
pool.submit(current);
}
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
result = null;
for (RunnableTask current : parallel) {
if (current.value != null) {
result = current.value;
}
}
break;
}
if (task.isAborted()) {
return;
}
} catch (TaskAbortException abort) {
return;
} catch (Throwable e1) {
if (handler != null) {
try {
handler.uncaughtException(Thread.currentThread(), e1);
} catch (Throwable e2) {
e1.printStackTrace();
e2.printStackTrace();
}
}
return;
}
}
}
public static final class TaskAbortException extends RuntimeException {
@Override
public Throwable fillInStackTrace() {
return this;
}
}
private FaweQueue queue;
private long last;
private long start;
private Object asyncWaitLock = new Object();
private Object syncWaitLock = new Object();
private boolean finished;
private static abstract class RunnableTask<T> extends RunnableVal<T> {
public final TaskType type;
private boolean aborted;
public RunnableTask(TaskType type) {
this.type = type;
}
public void abortNextTasks() {
this.aborted = true;
}
public boolean isAborted() {
return aborted;
}
public static RunnableTask adapt(final Task task, TaskType type) {
return new RunnableTask(type) {
@Override
public Object exec(Object previous) {
return task.run(previous);
}
};
}
public static RunnableTask adapt(final ReturnTask task, TaskType type) {
return new RunnableTask(type) {
@Override
public Object exec(Object previous) {
return task.run();
}
};
}
public static RunnableTask adapt(final ReceiveTask task, TaskType type) {
return new RunnableTask(type) {
@Override
public Object exec(Object previous) {
task.run(previous);
return null;
}
};
}
public static RunnableTask adapt(final Runnable run, TaskType type) {
return new RunnableTask(type) {
@Override
public Object exec(Object previous) {
if (run instanceof RunnableVal) {
((RunnableVal) run).value = this.value;
return this.value = ((RunnableVal) run).runAndGet();
}
run.run();
return null;
}
};
}
public abstract T exec(Object previous);
@Override
public final void run(T value) {
this.value = exec(value);
}
}
private static abstract class RunnableDelayedTask extends RunnableTask {
public RunnableDelayedTask(TaskType type) {
super(type);
}
@Override
public Object exec(Object previous) {
return previous;
}
public abstract int delay(Object previous);
public static RunnableDelayedTask adapt(final DelayedTask task) {
return new RunnableDelayedTask(TaskType.DELAY) {
@Override
public int delay(Object previous) {
return task.getDelay(previous);
}
};
}
public static RunnableDelayedTask adapt(final int time) {
return new RunnableDelayedTask(TaskType.DELAY) {
@Override
public int delay(Object previous) {
return time;
}
};
}
}
public static abstract class SplitTask extends RunnableTask {
private final long allocation;
private final FaweQueue queue;
private long last;
private long start;
private Object asyncWaitLock = new Object();
private Object syncWaitLock = new Object();
private boolean finished;
private boolean waitingAsync = true;
private boolean waitingSync = false;
public SplitTask() {
this(20);
}
public SplitTask(long allocation) {
super(TaskType.SYNC_WHEN_FREE);
this.allocation = allocation;
this.queue = SetQueue.IMP.getNewQueue((String) null, true, false);
}
public Object execSplit(final Object previous) {
this.value = previous;
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (asyncWaitLock) {
asyncWaitLock.notifyAll();
asyncWaitLock.wait(Long.MAX_VALUE);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
exec(previous);
finished = true;
waitingAsync = true;
waitingSync = false;
synchronized (syncWaitLock) {
syncWaitLock.notifyAll();
}
}
});
try {
synchronized (asyncWaitLock) {
thread.start();
asyncWaitLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
while (thread.isAlive()) {
TaskManager.IMP.syncWhenFree(new RunnableVal() {
@Override
public void run(Object ignore) {
queue.startSet(true);
start = System.currentTimeMillis();
try {
if (!finished) {
synchronized (asyncWaitLock) {
while (!waitingAsync) asyncWaitLock.wait(1);
asyncWaitLock.notifyAll();
}
waitingSync = true;
synchronized (syncWaitLock) {
syncWaitLock.notifyAll();
syncWaitLock.wait();
}
waitingSync = false;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.endSet(true);
}
});
}
return this.value;
}
public void split() {
long now = System.currentTimeMillis();
if (now - start > allocation) {
try {
synchronized (syncWaitLock) {
while (!waitingSync) syncWaitLock.wait(1);
syncWaitLock.notifyAll();
}
waitingAsync = true;
synchronized (asyncWaitLock) {
asyncWaitLock.notifyAll();
asyncWaitLock.wait();
}
waitingAsync = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private enum TaskType {
SYNC,
ASYNC,
SYNC_PARALLEL,
ASYNC_PARALLEL,
SYNC_WHEN_FREE,
DELAY,
ABORT
}
}

View File

@ -0,0 +1,35 @@
package com.boydti.fawe.util.terrain;
import java.util.Arrays;
import static com.boydti.fawe.util.MathMan.pairInt;
public final class Erosion {
private final int area;
private float[][] terrainHeight;
private float[][] waterHeight;
private long[] queue_2;
private long[] queue;
private int queueIndex;
public Erosion(int width, int length) {
this.area = width * length;
queue = new long[area];
Arrays.fill(queue, -1);
}
public void addWater(int x, int z, float amt) {
waterHeight[x][z] += amt;
queue[queueIndex++] = pairInt(x, z);
}
public void propogateWater() {
}
}