cn.nukkit.utils.Utils Maven / Gradle / Ivy
package cn.nukkit.utils;
import cn.nukkit.api.PowerNukkitOnly;
import cn.nukkit.api.PowerNukkitXOnly;
import cn.nukkit.api.Since;
import cn.nukkit.block.Block;
import cn.nukkit.level.Level;
import cn.nukkit.math.AxisAlignedBB;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.math.Vector3;
import lombok.extern.log4j.Log4j2;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author MagicDroidX (Nukkit Project)
*/
@Log4j2
public class Utils {
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static final Integer[] EMPTY_INTEGERS = new Integer[0];
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static final SplittableRandom random = new SplittableRandom();
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static void safeWrite(File currentFile, Consumer operation) throws IOException {
File parent = currentFile.getParentFile();
File newFile = new File(parent, currentFile.getName() + "_new");
File oldFile = new File(parent, currentFile.getName() + "_old");
File olderFile = new File(parent, currentFile.getName() + "_older");
if (olderFile.isFile() && !olderFile.delete()) {
log.fatal("Could not delete the file {}", olderFile.getAbsolutePath());
}
if (newFile.isFile() && !newFile.delete()) {
log.fatal("Could not delete the file {}", newFile.getAbsolutePath());
}
try {
operation.accept(newFile);
} catch (Exception e) {
throw new IOException(e);
}
if (oldFile.isFile()) {
if (olderFile.isFile()) {
Utils.copyFile(oldFile, olderFile);
} else if (!oldFile.renameTo(olderFile)) {
throw new IOException("Could not rename the " + oldFile + " to " + olderFile);
}
}
if (currentFile.isFile() && !currentFile.renameTo(oldFile)) {
throw new IOException("Could not rename the " + currentFile + " to " + oldFile);
}
if (!newFile.renameTo(currentFile)) {
throw new IOException("Could not rename the " + newFile + " to " + currentFile);
}
}
public static void writeFile(String fileName, String content) throws IOException {
writeFile(fileName, new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
}
public static void writeFile(String fileName, InputStream content) throws IOException {
writeFile(new File(fileName), content);
}
public static void writeFile(File file, String content) throws IOException {
writeFile(file, new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
}
public static void writeFile(File file, InputStream content) throws IOException {
if (content == null) {
throw new IllegalArgumentException("content must not be null");
}
if (!file.exists()) {
file.createNewFile();
}
try (FileOutputStream stream = new FileOutputStream(file)) {
byte[] buffer = new byte[1024];
int length;
while ((length = content.read(buffer)) != -1) {
stream.write(buffer, 0, length);
}
}
content.close();
}
public static String readFile(File file) throws IOException {
if (!file.exists() || file.isDirectory()) {
throw new FileNotFoundException();
}
return readFile(new FileInputStream(file));
}
public static String readFile(String filename) throws IOException {
File file = new File(filename);
if (!file.exists() || file.isDirectory()) {
throw new FileNotFoundException();
}
return readFile(new FileInputStream(file));
}
public static String readFile(InputStream inputStream) throws IOException {
return readFile(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
private static String readFile(Reader reader) throws IOException {
try (BufferedReader br = new BufferedReader(reader)) {
String temp;
StringBuilder stringBuilder = new StringBuilder();
temp = br.readLine();
while (temp != null) {
if (stringBuilder.length() != 0) {
stringBuilder.append("\n");
}
stringBuilder.append(temp);
temp = br.readLine();
}
return stringBuilder.toString();
}
}
public static void copyFile(File from, File to) throws IOException {
if (!from.exists()) {
throw new FileNotFoundException();
}
if (from.isDirectory() || to.isDirectory()) {
throw new FileNotFoundException();
}
FileInputStream fi = null;
FileChannel in = null;
FileOutputStream fo = null;
FileChannel out = null;
try {
if (!to.exists()) {
to.createNewFile();
}
fi = new FileInputStream(from);
in = fi.getChannel();
fo = new FileOutputStream(to);
out = fo.getChannel();
in.transferTo(0, in.size(), out);
} finally {
if (fi != null) fi.close();
if (in != null) in.close();
if (fo != null) fo.close();
if (out != null) out.close();
}
}
public static String getAllThreadDumps() {
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
StringBuilder builder = new StringBuilder();
for (ThreadInfo info : threads) {
builder.append('\n').append(info);
}
return builder.toString();
}
public static String getExceptionMessage(Throwable e) {
StringWriter stringWriter = new StringWriter();
try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
e.printStackTrace(printWriter);
printWriter.flush();
}
return stringWriter.toString();
}
public static UUID dataToUUID(String... params) {
StringBuilder builder = new StringBuilder();
for (String param : params) {
builder.append(param);
}
return UUID.nameUUIDFromBytes(builder.toString().getBytes(StandardCharsets.UTF_8));
}
public static UUID dataToUUID(byte[]... params) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
for (byte[] param : params) {
try {
stream.write(param);
} catch (IOException e) {
break;
}
}
return UUID.nameUUIDFromBytes(stream.toByteArray());
}
public static String rtrim(String s, char character) {
int i = s.length() - 1;
while (i >= 0 && (s.charAt(i)) == character) {
i--;
}
return s.substring(0, i + 1);
}
public static boolean isByteArrayEmpty(final byte[] array) {
for (byte b : array) {
if (b != 0) {
return false;
}
}
return true;
}
public static long toRGB(byte r, byte g, byte b, byte a) {
long result = (int) r & 0xff;
result |= ((int) g & 0xff) << 8;
result |= ((int) b & 0xff) << 16;
result |= (long) ((int) a & 0xff) << 24;
return result & 0xFFFFFFFFL;
}
public static long toABGR(int argb) {
long result = argb & 0xFF00FF00L;
result |= (argb << 16) & 0x00FF0000L; // B to R
result |= (argb >>> 16) & 0xFFL; // R to B
return result & 0xFFFFFFFFL;
}
public static Object[][] splitArray(Object[] arrayToSplit, int chunkSize) {
if (chunkSize <= 0) {
return null;
}
int rest = arrayToSplit.length % chunkSize;
int chunks = arrayToSplit.length / chunkSize + (rest > 0 ? 1 : 0);
Object[][] arrays = new Object[chunks][];
for (int i = 0; i < (rest > 0 ? chunks - 1 : chunks); i++) {
arrays[i] = Arrays.copyOfRange(arrayToSplit, i * chunkSize, i * chunkSize + chunkSize);
}
if (rest > 0) {
arrays[chunks - 1] = Arrays.copyOfRange(arrayToSplit, (chunks - 1) * chunkSize, (chunks - 1) * chunkSize + rest);
}
return arrays;
}
public static void reverseArray(T[] data) {
reverseArray(data, false);
}
public static T[] reverseArray(T[] array, boolean copy) {
T[] data = array;
if (copy) {
data = Arrays.copyOf(array, array.length);
}
for (int left = 0, right = data.length - 1; left < right; left++, right--) {
// swap the values at the left and right indices
T temp = data[left];
data[left] = data[right];
data[right] = temp;
}
return data;
}
public static T[][] clone2dArray(T[][] array) {
T[][] newArray = Arrays.copyOf(array, array.length);
for (int i = 0; i < array.length; i++) {
newArray[i] = Arrays.copyOf(array[i], array[i].length);
}
return newArray;
}
public static Map getOrCreate(Map> map, T key) {
Map existing = map.get(key);
if (existing == null) {
ConcurrentHashMap toPut = new ConcurrentHashMap<>();
existing = map.putIfAbsent(key, toPut);
if (existing == null) {
existing = toPut;
}
}
return existing;
}
public static U getOrCreate(Map map, Class clazz, T key) {
U existing = map.get(key);
if (existing != null) {
return existing;
}
try {
U toPut = clazz.newInstance();
existing = map.putIfAbsent(key, toPut);
if (existing == null) {
return toPut;
}
return existing;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static int toInt(Object number) {
if (number instanceof Integer) {
return (Integer) number;
}
return (int) Math.round((double) number);
}
public static byte[] parseHexBinary(String s) {
final int len = s.length();
// "111" is not a valid hex encoding.
if (len % 2 != 0)
throw new IllegalArgumentException("hexBinary needs to be even-length: " + s);
byte[] out = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int h = hexToBin(s.charAt(i));
int l = hexToBin(s.charAt(i + 1));
if (h == -1 || l == -1)
throw new IllegalArgumentException("contains illegal character for hexBinary: " + s);
out[i / 2] = (byte) (h * 16 + l);
}
return out;
}
private static int hexToBin(char ch) {
if ('0' <= ch && ch <= '9') return ch - '0';
if ('A' <= ch && ch <= 'F') return ch - 'A' + 10;
if ('a' <= ch && ch <= 'f') return ch - 'a' + 10;
return -1;
}
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static int rand(int min, int max) {
if (min == max) {
return max;
}
return random.nextInt(max + 1 - min) + min;
}
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static double rand(double min, double max) {
if (min == max) {
return max;
}
return min + random.nextDouble() * (max - min);
}
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static boolean rand() {
return random.nextBoolean();
}
/**
* A way to tell the java compiler to do not replace the users of a {@code public static final int} constant
* with the value defined in it, forcing the JVM to get the value directly from the class, preventing
* binary incompatible changes.
*
* @param value The value to be assigned to the field.
* @return The same value that was passed as parameter
*/
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static int dynamic(int value) {
return value;
}
/**
* A way to tell the java compiler to do not replace the users of a {@code public static final} constant
* with the value defined in it, forcing the JVM to get the value directly from the class, preventing
* binary incompatible changes.
*
* @param value The value to be assigned to the field.
* @return The same value that was passed as parameter
*/
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static T dynamic(T value) {
return value;
}
@PowerNukkitOnly
@Since("1.4.0.0-PN")
public static void zipFolder(Path sourceFolderPath, Path zipPath) throws IOException {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipPath.toFile()))) {
Files.walkFileTree(sourceFolderPath, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
zos.putNextEntry(new ZipEntry(sourceFolderPath.relativize(file).toString()));
Files.copy(file, zos);
zos.closeEntry();
return FileVisitResult.CONTINUE;
}
});
}
}
@PowerNukkitOnly
@Since("1.5.1.0-PN")
public static 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 < '0' || c > '9') {
return false;
}
}
return true;
}
//used for commands /fill , /clone and so on
//todo: using other methods instead of this one
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static Block[] getLevelBlocks(Level level, AxisAlignedBB bb) {
int minX = NukkitMath.floorDouble(Math.min(bb.getMinX(), bb.getMaxX()));
int minY = NukkitMath.floorDouble(Math.min(bb.getMinY(), bb.getMaxY()));
int minZ = NukkitMath.floorDouble(Math.min(bb.getMinZ(), bb.getMaxZ()));
int maxX = NukkitMath.floorDouble(Math.max(bb.getMinX(), bb.getMaxX()));
int maxY = NukkitMath.floorDouble(Math.max(bb.getMinY(), bb.getMaxY()));
int maxZ = NukkitMath.floorDouble(Math.max(bb.getMinZ(), bb.getMaxZ()));
List blocks = new ArrayList<>();
Vector3 vec = new Vector3();
for (int z = minZ; z <= maxZ; ++z) {
for (int x = minX; x <= maxX; ++x) {
for (int y = minY; y <= maxY; ++y) {
blocks.add(level.getBlock(vec.setComponents(x, y, z), false));
}
}
}
return blocks.toArray(Block.EMPTY_ARRAY);
}
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static final int ACCORDING_X_OBTAIN_Y = 0;
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static final int ACCORDING_Y_OBTAIN_X = 1;
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static double calLinearFunction(Vector3 pos1, Vector3 pos2, double element, int type) {
if (pos1.getFloorY() != pos2.getFloorY()) return Double.MAX_VALUE;
if (pos1.getX() == pos2.getX()) {
if (type == ACCORDING_Y_OBTAIN_X) return pos1.getX();
else return Double.MAX_VALUE;
} else if (pos1.getZ() == pos2.getZ()) {
if (type == ACCORDING_X_OBTAIN_Y) return pos1.getZ();
else return Double.MAX_VALUE;
} else {
if (type == ACCORDING_X_OBTAIN_Y) {
return (element - pos1.getX()) * (pos1.getZ() - pos2.getZ()) / (pos1.getX() - pos2.getX()) + pos1.getZ();
} else {
return (element - pos1.getZ()) * (pos1.getX() - pos2.getX()) / (pos1.getZ() - pos2.getZ()) + pos1.getX();
}
}
}
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static boolean hasCollisionBlocks(Level level, AxisAlignedBB bb) {
int minX = NukkitMath.floorDouble(bb.getMinX());
int minY = NukkitMath.floorDouble(bb.getMinY());
int minZ = NukkitMath.floorDouble(bb.getMinZ());
int maxX = NukkitMath.ceilDouble(bb.getMaxX());
int maxY = NukkitMath.ceilDouble(bb.getMaxY());
int maxZ = NukkitMath.ceilDouble(bb.getMaxZ());
for (int z = minZ; z <= maxZ; ++z) {
for (int x = minX; x <= maxX; ++x) {
for (int y = minY; y <= maxY; ++y) {
Block block = level.getBlock(x, y, z, false);
//判断是否和非空气方块有碰撞
if (block != null && !block.canPassThrough() && block.collidesWithBB(bb)) {
return true;
}
}
}
}
return false;
}
@PowerNukkitXOnly
@Since("1.6.0.0-PNX")
public static boolean hasCollisionTickCachedBlocks(Level level, AxisAlignedBB bb) {
int minX = NukkitMath.floorDouble(bb.getMinX());
int minY = NukkitMath.floorDouble(bb.getMinY());
int minZ = NukkitMath.floorDouble(bb.getMinZ());
int maxX = NukkitMath.ceilDouble(bb.getMaxX());
int maxY = NukkitMath.ceilDouble(bb.getMaxY());
int maxZ = NukkitMath.ceilDouble(bb.getMaxZ());
for (int z = minZ; z <= maxZ; ++z) {
for (int x = minX; x <= maxX; ++x) {
for (int y = minY; y <= maxY; ++y) {
Block block = level.getTickCachedBlock(x, y, z, false);
//判断是否和非空气方块有碰撞
if (block != null && block.collidesWithBB(bb) && !block.canPassThrough()) {
return true;
}
}
}
}
return false;
}
public static final boolean[] isPlant = new boolean[2048];
static {
isPlant[Block.AIR] = true; //gap
isPlant[Block.LOG] = true;
isPlant[Block.LEAVES] = true;
isPlant[Block.TALL_GRASS] = true;
isPlant[Block.DEAD_BUSH] = true;
isPlant[Block.DANDELION] = true;
isPlant[Block.RED_FLOWER] = true;
isPlant[Block.BROWN_MUSHROOM] = true;
isPlant[Block.RED_MUSHROOM] = true;
isPlant[Block.SNOW_LAYER] = true; //falls on trees
isPlant[Block.CACTUS] = true;
isPlant[Block.REEDS] = true;
isPlant[Block.PUMPKIN] = true;
isPlant[Block.BROWN_MUSHROOM_BLOCK] = true;
isPlant[Block.RED_MUSHROOM_BLOCK] = true;
isPlant[Block.MELON_BLOCK] = true;
isPlant[Block.VINE] = true;
isPlant[Block.WATERLILY] = true;
isPlant[Block.COCOA] = true;
isPlant[Block.LEAVES2] = true;
isPlant[Block.LOG2] = true;
isPlant[Block.DOUBLE_PLANT] = true;
}
public static final boolean[] isPlantOrFluid = isPlant.clone();
static {
isPlantOrFluid[Block.FLOWING_WATER] = true;
isPlantOrFluid[Block.STILL_WATER] = true;
isPlantOrFluid[Block.FLOWING_LAVA] = true;
isPlantOrFluid[Block.STILL_LAVA] = true;
isPlantOrFluid[Block.ICE] = true; //solid water
isPlantOrFluid[Block.PACKED_ICE] = true; //solid water
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy