package net.minecraft.server; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.commons.lang3.exception.ExceptionUtils; import org.bukkit.Location; import org.bukkit.craftbukkit.CraftWorld; import org.spigotmc.AsyncCatcher; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.function.Supplier; public final class MCUtil { private static final Executor asyncExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build()); private MCUtil() {} /** * Quickly generate a stack trace for current location * * @return Stacktrace */ public static String stack() { return ExceptionUtils.getStackTrace(new Throwable()); } /** * Quickly generate a stack trace for current location with message * * @param str * @return Stacktrace */ public static String stack(String str) { return ExceptionUtils.getStackTrace(new Throwable(str)); } /** * Ensures the target code is running on the main thread * @param reason * @param run * @param <T> * @return */ public static <T> T ensureMain(String reason, Supplier<T> run) { AsyncCatcher.catchOp(reason); /* new IllegalStateException( "Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); // This never return Waitable<T> wait = new Waitable<T>() { @Override protected T evaluate() { return run.get(); } }; MinecraftServer.getServer().processQueue.add(wait); try { return wait.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return null; */ return run.get(); } /** * Calculates distance between 2 entities * @param e1 * @param e2 * @return */ public static double distance(Entity e1, Entity e2) { return Math.sqrt(distanceSq(e1, e2)); } /** * Calculates distance between 2 block positions * @param e1 * @param e2 * @return */ public static double distance(BlockPosition e1, BlockPosition e2) { return Math.sqrt(distanceSq(e1, e2)); } /** * Gets the distance between 2 positions * @param x1 * @param y1 * @param z1 * @param x2 * @param y2 * @param z2 * @return */ public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) { return Math.sqrt(distanceSq(x1, y1, z1, x2, y2, z2)); } /** * Get's the distance squared between 2 entities * @param e1 * @param e2 * @return */ public static double distanceSq(Entity e1, Entity e2) { return distanceSq(e1.locX,e1.locY,e1.locZ, e2.locX,e2.locY,e2.locZ); } /** * Gets the distance sqaured between 2 block positions * @param pos1 * @param pos2 * @return */ public static double distanceSq(BlockPosition pos1, BlockPosition pos2) { return distanceSq(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX(), pos2.getY(), pos2.getZ()); } /** * Gets the distance squared between 2 positions * @param x1 * @param y1 * @param z1 * @param x2 * @param y2 * @param z2 * @return */ public static double distanceSq(double x1, double y1, double z1, double x2, double y2, double z2) { return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2); } /** * Converts a NMS World/BlockPosition to Bukkit Location * @param world * @param pos * @return */ public static Location toLocation(World world, BlockPosition pos) { return new Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ()); } /** * Converts an NMS entity's current location to a Bukkit Location * @param entity * @return */ public static Location toLocation(Entity entity) { return new Location(entity.getWorld().getWorld(), entity.locX, entity.locY, entity.locZ); } public static BlockPosition toBlockPosition(Location loc) { return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); } public static boolean isEdgeOfChunk(BlockPosition pos) { final int modX = pos.getX() & 15; final int modZ = pos.getZ() & 15; return (modX == 0 || modX == 15 || modZ == 0 || modZ == 15); } /** * Gets a chunk without changing its boolean for should unload * @param world * @param x chunkX * @param z chunkZ * @return */ @Nullable public static Chunk getLoadedChunkWithoutMarkingActive(World world, int x, int z) { return ((ChunkProviderServer) world.chunkProvider).chunks.get(ChunkCoordIntPair.chunkXZ2Int(x, z)); } /** * Gets a chunk without changing its boolean for should unload * @param provider * @param x chunkX * @param z chunkZ * @return */ @Nullable public static Chunk getLoadedChunkWithoutMarkingActive(IChunkProvider provider, int x, int z) { return ((ChunkProviderServer) provider).chunks.get(ChunkCoordIntPair.chunkXZ2Int(x, z)); } /** * Posts a task to be executed asynchronously * @param run */ public static void scheduleAsyncTask(Runnable run) { asyncExecutor.execute(run); } @Nullable public static TileEntityHopper getHopper(World world, BlockPosition pos) { Chunk chunk = world.getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4); if (chunk != null && chunk.getBlockData(pos).getBlock() == Blocks.HOPPER) { TileEntity tileEntity = chunk.getTileEntityImmediately(pos); if (tileEntity instanceof TileEntityHopper) { return (TileEntityHopper) tileEntity; } } return null; } @Nonnull public static World getNMSWorld(@Nonnull org.bukkit.World world) { return ((CraftWorld) world).getHandle(); } public static World getNMSWorld(@Nonnull org.bukkit.entity.Entity entity) { return getNMSWorld(entity.getWorld()); } }