Stage 14: Polish and utilities

This commit is contained in:
loki5512344 2026-05-31 20:56:00 +02:00
parent 7cbc4e397e
commit 362b791774
3 changed files with 145 additions and 391 deletions

View file

@ -4,162 +4,82 @@ import org.bukkit.Location;
import org.bukkit.util.Vector;
public final class MathUtil {
private MathUtil() {}
public static int clamp(int value, int min, int max) {
return Math.max(min, Math.min(max, value));
public static int clamp(int v, int min, int max) { return Math.max(min, Math.min(max, v)); }
public static long clamp(long v, long min, long max) { return Math.max(min, Math.min(max, v)); }
public static float clamp(float v, float min, float max) { return Math.max(min, Math.min(max, v)); }
public static double clamp(double v, double min, double max) { return Math.max(min, Math.min(max, v)); }
public static double lerp(double a, double b, double t) { return a + (b - a) * clamp(t, 0, 1); }
public static float lerp(float a, float b, float t) { return a + (b - a) * clamp(t, 0f, 1f); }
public static Vector lerp(Vector a, Vector b, double t) {
t = clamp(t, 0, 1);
return a.clone().multiply(1 - t).add(b.clone().multiply(t));
}
public static long clamp(long value, long min, long max) {
return Math.max(min, Math.min(max, value));
public static Location lerpLoc(Location a, Location b, double t) {
t = clamp(t, 0, 1);
return new Location(a.getWorld(), lerp(a.getX(), b.getX(), t), lerp(a.getY(), b.getY(), t),
lerp(a.getZ(), b.getZ(), t), lerp(a.getYaw(), b.getYaw(), (float) t),
lerp(a.getPitch(), b.getPitch(), (float) t));
}
public static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
public static double inverseLerp(double a, double b, double v) {
return b == a ? 0 : clamp((v - a) / (b - a), 0, 1);
}
public static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
public static double lerp(double start, double end, double t) {
t = clamp(t, 0.0, 1.0);
return start + (end - start) * t;
}
public static float lerp(float start, float end, float t) {
t = clamp(t, 0.0f, 1.0f);
return start + (end - start) * t;
}
public static Vector lerp(Vector start, Vector end, double t) {
t = clamp(t, 0.0, 1.0);
return start.clone().multiply(1 - t).add(end.clone().multiply(t));
}
public static Location lerp(Location start, Location end, double t) {
t = clamp(t, 0.0, 1.0);
double x = lerp(start.getX(), end.getX(), t);
double y = lerp(start.getY(), end.getY(), t);
double z = lerp(start.getZ(), end.getZ(), t);
float yaw = lerp(start.getYaw(), end.getYaw(), (float) t);
float pitch = lerp(start.getPitch(), end.getPitch(), (float) t);
return new Location(start.getWorld(), x, y, z, yaw, pitch);
}
public static double inverseLerp(double start, double end, double value) {
if (end == start) return 0;
return clamp((value - start) / (end - start), 0.0, 1.0);
}
public static double map(double value, double inMin, double inMax, double outMin, double outMax) {
double t = inverseLerp(inMin, inMax, value);
return lerp(outMin, outMax, t);
public static double map(double v, double inMin, double inMax, double outMin, double outMax) {
return lerp(outMin, outMax, inverseLerp(inMin, inMax, v));
}
public static double smoothStep(double edge0, double edge1, double x) {
double t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
double t = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return t * t * (3 - 2 * t);
}
public static double smootherStep(double edge0, double edge1, double x) {
double t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * t * (t * (t * 6 - 15) + 10);
}
public static int wrap(int value, int min, int max) {
public static int wrap(int v, int min, int max) {
int range = max - min;
if (range == 0) return min;
return ((value - min) % range + range) % range + min;
return range == 0 ? min : ((v - min) % range + range) % range + min;
}
public static double wrap(double value, double min, double max) {
public static double wrap(double v, double min, double max) {
double range = max - min;
if (range == 0) return min;
return ((value - min) % range + range) % range + min;
return range == 0 ? min : ((v - min) % range + range) % range + min;
}
public static double normalizeAngle(double angle) {
return wrap(angle, -180.0, 180.0);
}
public static double normalizeAngle(double a) { return wrap(a, -180, 180); }
public static double angleDiff(double a1, double a2) { return Math.abs(normalizeAngle(a2 - a1)); }
public static boolean inRange(double v, double min, double max) { return v >= min && v <= max; }
public static boolean inRange(int v, int min, int max) { return v >= min && v <= max; }
public static double angleDifference(double angle1, double angle2) {
double diff = normalizeAngle(angle2 - angle1);
return Math.abs(diff);
}
public static int roundUp(int v, int m) { return m == 0 ? v : ((v + m - 1) / m) * m; }
public static double roundTo(double v, int d) { double f = Math.pow(10, d); return Math.round(v * f) / f; }
public static boolean isInRange(double value, double min, double max) {
return value >= min && value <= max;
}
public static boolean isInRange(int value, int min, int max) {
return value >= min && value <= max;
}
public static int roundUp(int value, int multiple) {
if (multiple == 0) return value;
return ((value + multiple - 1) / multiple) * multiple;
}
public static double roundToDecimals(double value, int decimals) {
double factor = Math.pow(10, decimals);
return Math.round(value * factor) / factor;
}
public static double distanceSquared(double x1, double y1, double z1, double x2, double y2, double z2) {
double dx = x2 - x1;
double dy = y2 - y1;
double dz = z2 - z1;
public static double distSq(double x1, double y1, double z1, double x2, double y2, double z2) {
double dx = x2 - x1, dy = y2 - y1, dz = z2 - z1;
return dx * dx + dy * dy + dz * dz;
}
public static double horizontalDistanceSquared(double x1, double z1, double x2, double z2) {
double dx = x2 - x1;
double dz = z2 - z1;
public static double distSqH(double x1, double z1, double x2, double z2) {
double dx = x2 - x1, dz = z2 - z1;
return dx * dx + dz * dz;
}
public static int sign(double value) {
if (value > 0) return 1;
if (value < 0) return -1;
return 0;
public static int sign(double v) { return v > 0 ? 1 : (v < 0 ? -1 : 0); }
public static double approach(double cur, double tgt, double delta) {
return cur < tgt ? Math.min(cur + delta, tgt) : Math.max(cur - delta, tgt);
}
public static double approach(double current, double target, double maxDelta) {
if (current < target) {
return Math.min(current + maxDelta, target);
}
return Math.max(current - maxDelta, target);
public static float approach(float cur, float tgt, float delta) {
return cur < tgt ? Math.min(cur + delta, tgt) : Math.max(cur - delta, tgt);
}
public static float approach(float current, float target, float maxDelta) {
if (current < target) {
return Math.min(current + maxDelta, target);
}
return Math.max(current - maxDelta, target);
}
public static double pingPong(double value, double length) {
return length - Math.abs(wrap(value, -length, length));
}
public static double toRadians(double degrees) {
return Math.toRadians(degrees);
}
public static double toDegrees(double radians) {
return Math.toDegrees(radians);
}
public static Vector rotateVector(Vector vector, double yawDegrees) {
double yaw = Math.toRadians(yawDegrees);
double cos = Math.cos(yaw);
double sin = Math.sin(yaw);
double x = vector.getX() * cos - vector.getZ() * sin;
double z = vector.getX() * sin + vector.getZ() * cos;
return new Vector(x, vector.getY(), z);
public static Vector rotateVector(Vector v, double yawDeg) {
double yaw = Math.toRadians(yawDeg);
double cos = Math.cos(yaw), sin = Math.sin(yaw);
return new Vector(v.getX() * cos - v.getZ() * sin, v.getY(), v.getX() * sin + v.getZ() * cos);
}
}

View file

@ -6,199 +6,94 @@ import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.List;
public final class ParticleUtil {
private static final int DEFAULT_COUNT = 1;
private static final double DEFAULT_SPREAD = 0.0;
private static final float DEFAULT_SIZE = 1.0f;
private ParticleUtil() {}
public static void spawn(Particle particle, Location location) {
spawn(particle, location, DEFAULT_COUNT);
public static void spawn(Particle p, Location loc, int count) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(p, loc, count);
}
public static void spawn(Particle particle, Location location, int count) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(particle, location, count);
public static void spawn(Particle p, Location loc, int count, double sx, double sy, double sz, double speed) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(p, loc, count, sx, sy, sz, speed);
}
public static void spawn(Particle particle, Location location, int count, double spreadX, double spreadY, double spreadZ) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ);
public static void spawnDust(Location loc, Color c, float size) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.DUST, loc, 1, new Particle.DustOptions(c, size));
}
public static void spawn(Particle particle, Location location, int count, double spreadX, double spreadY, double spreadZ, double speed) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ, speed);
public static void spawnDust(Location loc, Color c, float size, int count) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.DUST, loc, count, 0.1, 0.1, 0.1, 0, new Particle.DustOptions(c, size));
}
public static void spawnDust(Location location, Color color, float size) {
World world = location.getWorld();
if (world == null) return;
Particle.DustOptions dustOptions = new Particle.DustOptions(color, size);
world.spawnParticle(Particle.DUST, location, 1, dustOptions);
public static void drawLine(Particle p, Location a, Location b, int points) {
if (!a.getWorld().equals(b.getWorld())) return;
Vector dir = b.toVector().subtract(a.toVector());
double dist = dir.length(); dir.normalize();
double step = dist / points; Location cur = a.clone();
for (int i = 0; i <= points; i++) { spawn(p, cur, 1); cur.add(dir.clone().multiply(step)); }
}
public static void spawnDust(Location location, Color color, float size, int count) {
World world = location.getWorld();
if (world == null) return;
Particle.DustOptions dustOptions = new Particle.DustOptions(color, size);
world.spawnParticle(Particle.DUST, location, count, 0.1, 0.1, 0.1, 0, dustOptions);
}
public static void spawnToPlayer(Particle particle, Player player, Location location, int count) {
player.spawnParticle(particle, location, count);
}
public static void spawnToPlayer(Particle particle, Player player, Location location, int count, double spreadX, double spreadY, double spreadZ) {
player.spawnParticle(particle, location, count, spreadX, spreadY, spreadZ);
}
public static void drawLine(Particle particle, Location start, Location end, int points) {
if (!start.getWorld().equals(end.getWorld())) return;
Vector direction = end.toVector().subtract(start.toVector());
double distance = direction.length();
direction.normalize();
double step = distance / points;
Location current = start.clone();
for (int i = 0; i <= points; i++) {
spawn(particle, current);
current.add(direction.clone().multiply(step));
}
}
public static void drawCircle(Particle particle, Location center, double radius, int points) {
World world = center.getWorld();
if (world == null) return;
public static void drawCircle(Particle p, Location c, double r, int points) {
World w = c.getWorld(); if (w == null) return;
for (int i = 0; i < points; i++) {
double angle = 2 * Math.PI * i / points;
double x = center.getX() + radius * Math.cos(angle);
double z = center.getZ() + radius * Math.sin(angle);
Location point = new Location(world, x, center.getY(), z);
spawn(particle, point);
double a = 2 * Math.PI * i / points;
spawn(p, new Location(w, c.getX() + r * Math.cos(a), c.getY(), c.getZ() + r * Math.sin(a)), 1);
}
}
public static void drawCircle(Particle particle, Location center, double radius, int points, Color color, float size) {
World world = center.getWorld();
if (world == null) return;
public static void drawCircle(Particle p, Location c, double r, int points, Color col, float size) {
World w = c.getWorld(); if (w == null) return;
for (int i = 0; i < points; i++) {
double angle = 2 * Math.PI * i / points;
double x = center.getX() + radius * Math.cos(angle);
double z = center.getZ() + radius * Math.sin(angle);
Location point = new Location(world, x, center.getY(), z);
spawnDust(point, color, size);
double a = 2 * Math.PI * i / points;
spawnDust(new Location(w, c.getX() + r * Math.cos(a), c.getY(), c.getZ() + r * Math.sin(a)), col, size);
}
}
public static void drawSphere(Particle particle, Location center, double radius, int points) {
World world = center.getWorld();
if (world == null) return;
for (int i = 0; i < points; i++) {
double theta = Math.random() * 2 * Math.PI;
double phi = Math.random() * Math.PI;
double x = center.getX() + radius * Math.sin(phi) * Math.cos(theta);
double y = center.getY() + radius * Math.cos(phi);
double z = center.getZ() + radius * Math.sin(phi) * Math.sin(theta);
Location point = new Location(world, x, y, z);
spawn(particle, point);
}
public static void burst(Particle p, Location c, int count, double speed) {
World w = c.getWorld(); if (w == null) return;
w.spawnParticle(p, c, count, 0.5, 0.5, 0.5, speed);
}
public static void drawBox(Particle particle, Location min, Location max, double step) {
if (!min.getWorld().equals(max.getWorld())) return;
World world = min.getWorld();
if (world == null) return;
for (double x = min.getX(); x <= max.getX(); x += step) {
for (double y = min.getY(); y <= max.getY(); y += step) {
for (double z = min.getZ(); z <= max.getZ(); z += step) {
boolean edge = (x <= min.getX() + step || x >= max.getX() - step) ||
(y <= min.getY() + step || y >= max.getY() - step) ||
(z <= min.getZ() + step || z >= max.getZ() - step);
if (edge) {
spawn(particle, new Location(world, x, y, z));
}
}
}
}
public static void burstToPlayers(Particle p, List<Player> players, Location c, int count, double speed) {
for (Player pl : players) pl.spawnParticle(p, c, count, 0.5, 0.5, 0.5, speed);
}
public static void burst(Particle particle, Location center, int count, double speed) {
World world = center.getWorld();
if (world == null) return;
world.spawnParticle(particle, center, count, 0.5, 0.5, 0.5, speed);
public static void trail(Particle p, Location start, Vector dir, int len, double spacing) {
Location cur = start.clone(); Vector step = dir.clone().normalize().multiply(spacing);
for (int i = 0; i < len; i++) { spawn(p, cur, 1); cur.add(step); }
}
public static void burstToPlayers(Particle particle, List<Player> players, Location center, int count, double speed) {
for (Player player : players) {
player.spawnParticle(particle, center, count, 0.5, 0.5, 0.5, speed);
}
public static void shockwave(Particle p, Location c, double maxR, int rings, double speed) {
for (int i = 1; i <= rings; i++) drawCircle(p, c, (maxR / rings) * i, (int) (16 * (maxR / rings) * i));
}
public static void trail(Particle particle, Location start, Vector direction, int length, double spacing) {
Location current = start.clone();
Vector step = direction.clone().normalize().multiply(spacing);
for (int i = 0; i < length; i++) {
spawn(particle, current);
current.add(step);
}
public static void damage(Location loc) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.DAMAGE_INDICATOR, loc, 10, 0.3, 0.3, 0.3, 0);
}
public static void shockwave(Particle particle, Location center, double maxRadius, int rings, double speed) {
World world = center.getWorld();
if (world == null) return;
for (int ring = 1; ring <= rings; ring++) {
double radius = (maxRadius / rings) * ring;
int points = (int) (16 * radius);
drawCircle(particle, center, radius, points);
}
}
public static void damageIndicator(Location location) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(Particle.DAMAGE_INDICATOR, location, 10, 0.3, 0.3, 0.3, 0);
}
public static void blood(Location location) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(Particle.BLOCK_CRUMBLE, location, 20, 0.2, 0.2, 0.2, 0,
public static void blood(Location loc) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.BLOCK_CRUMBLE, loc, 20, 0.2, 0.2, 0.2, 0,
org.bukkit.Material.REDSTONE_BLOCK.createBlockData());
}
public static void sparks(Location location) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(Particle.ELECTRIC_SPARK, location, 5, 0.1, 0.1, 0.1, 0.1);
public static void sparks(Location loc) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.ELECTRIC_SPARK, loc, 5, 0.1, 0.1, 0.1, 0.1);
}
public static void heal(Location location) {
World world = location.getWorld();
if (world == null) return;
spawnDust(location, Color.LIME, 1.0f, 10);
}
public static void heal(Location loc) { spawnDust(loc, Color.LIME, 1.0f, 10); }
public static void teleportEffect(Location location) {
World world = location.getWorld();
if (world == null) return;
world.spawnParticle(Particle.PORTAL, location, 100, 0.5, 1.0, 0.5, 1.0);
world.spawnParticle(Particle.REVERSE_PORTAL, location, 50, 0.3, 0.5, 0.3, 0.5);
public static void teleportEffect(Location loc) {
World w = loc.getWorld(); if (w == null) return;
w.spawnParticle(Particle.PORTAL, loc, 100, 0.5, 1.0, 0.5, 1.0);
w.spawnParticle(Particle.REVERSE_PORTAL, loc, 50, 0.3, 0.5, 0.3, 0.5);
}
}

View file

@ -6,147 +6,86 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
public final class PlayerUtil {
private PlayerUtil() {}
public static boolean isSneaking(Player player) {
return player != null && player.isSneaking();
public static boolean isSneaking(Player p) { return p != null && p.isSneaking(); }
public static boolean isSprinting(Player p) { return p != null && p.isSprinting(); }
public static boolean isGrounded(Player p) { return p != null && p.isOnGround(); }
public static boolean isFlying(Player p) { return p != null && p.isFlying(); }
public static boolean isSwimming(Player p) { return p != null && p.isSwimming(); }
public static boolean isGliding(Player p) { return p != null && p.isGliding(); }
public static boolean hasPotionEffect(Player p, PotionEffectType type) { return p != null && p.hasPotionEffect(type); }
public static double getMovementSpeed(Player p) {
if (p == null) return 0;
Vector v = p.getVelocity();
return Math.sqrt(v.getX() * v.getX() + v.getZ() * v.getZ());
}
public static boolean isSprinting(Player player) {
return player != null && player.isSprinting();
public static double getHorizontalSpeed(Player p) {
if (p == null) return 0;
return LocationUtil.distanceHorizontal(p.getLocation(), p.getLocation().add(p.getVelocity()));
}
public static boolean isGrounded(Player player) {
return player != null && player.isOnGround();
public static Vector getLookDirection(Player p) { return p == null ? new Vector(0, 0, 0) : p.getLocation().getDirection(); }
public static Location getTargetLocation(Player p, double maxDist) {
if (p == null) return null;
return p.getEyeLocation().add(p.getEyeLocation().getDirection().multiply(maxDist));
}
public static boolean isFlying(Player player) {
return player != null && player.isFlying();
}
public static boolean isLookingDown(Player p, double t) { return p != null && p.getLocation().getPitch() > t; }
public static boolean isLookingUp(Player p, double t) { return p != null && p.getLocation().getPitch() < -t; }
public static boolean isSwimming(Player player) {
return player != null && player.isSwimming();
}
public static void setWalkingSpeed(Player p, float s) { if (p != null) p.setWalkSpeed(MathUtil.clamp(s, -1f, 1f)); }
public static void setFlyingSpeed(Player p, float s) { if (p != null) p.setFlySpeed(MathUtil.clamp(s, -1f, 1f)); }
public static void resetSpeed(Player p) { if (p != null) { p.setWalkSpeed(0.2f); p.setFlySpeed(0.1f); } }
public static boolean isGliding(Player player) {
return player != null && player.isGliding();
}
public static boolean hasPotionEffect(Player player, PotionEffectType type) {
return player != null && player.hasPotionEffect(type);
}
public static double getMovementSpeed(Player player) {
if (player == null) return 0;
Vector velocity = player.getVelocity();
return Math.sqrt(velocity.getX() * velocity.getX() + velocity.getZ() * velocity.getZ());
}
public static double getHorizontalSpeed(Player player) {
if (player == null) return 0;
Location from = player.getLocation();
Location to = from.clone().add(player.getVelocity());
return LocationUtil.distanceHorizontal(from, to);
}
public static Vector getLookDirection(Player player) {
if (player == null) return new Vector(0, 0, 0);
return player.getLocation().getDirection();
}
public static Location getTargetLocation(Player player, double maxDistance) {
if (player == null) return null;
Location eyeLoc = player.getEyeLocation();
Vector direction = eyeLoc.getDirection();
return eyeLoc.clone().add(direction.multiply(maxDistance));
}
public static boolean isLookingDown(Player player, double threshold) {
if (player == null) return false;
return player.getLocation().getPitch() > threshold;
}
public static boolean isLookingUp(Player player, double threshold) {
if (player == null) return false;
return player.getLocation().getPitch() < -threshold;
}
public static void setWalkingSpeed(Player player, float speed) {
if (player == null) return;
player.setWalkSpeed(MathUtil.clamp(speed, -1.0f, 1.0f));
}
public static void setFlyingSpeed(Player player, float speed) {
if (player == null) return;
player.setFlySpeed(MathUtil.clamp(speed, -1.0f, 1.0f));
}
public static void resetSpeed(Player player) {
if (player == null) return;
player.setWalkSpeed(0.2f);
player.setFlySpeed(0.1f);
}
public static void hidePlayerFrom(Player hidden, Player viewer) {
public static void hideFrom(Player hidden, Player viewer) {
if (hidden == null || viewer == null) return;
viewer.hidePlayer(hidden.getServer().getPluginManager().getPlugin("mdbd"), hidden);
}
public static void showPlayerTo(Player hidden, Player viewer) {
public static void showTo(Player hidden, Player viewer) {
if (hidden == null || viewer == null) return;
viewer.showPlayer(hidden.getServer().getPluginManager().getPlugin("mdbd"), hidden);
}
public static boolean hasInventorySpace(Player player, org.bukkit.inventory.ItemStack item) {
if (player == null || item == null) return false;
return player.getInventory().firstEmpty() != -1 ||
player.getInventory().containsAtLeast(item, item.getAmount());
public static boolean hasInventorySpace(Player p, org.bukkit.inventory.ItemStack item) {
if (p == null || item == null) return false;
return p.getInventory().firstEmpty() != -1 || p.getInventory().containsAtLeast(item, item.getAmount());
}
public static int getEmptySlots(Player player) {
if (player == null) return 0;
public static int getEmptySlots(Player p) {
if (p == null) return 0;
int count = 0;
for (org.bukkit.inventory.ItemStack item : player.getInventory().getStorageContents()) {
if (item == null || item.getType().isAir()) {
count++;
}
for (var item : p.getInventory().getStorageContents()) {
if (item == null || item.getType().isAir()) count++;
}
return count;
}
public static void clearEffects(Player player) {
if (player == null) return;
for (org.bukkit.potion.PotionEffect effect : player.getActivePotionEffects()) {
player.removePotionEffect(effect.getType());
}
public static void clearEffects(Player p) {
if (p == null) return;
for (var e : p.getActivePotionEffects()) p.removePotionEffect(e.getType());
}
public static boolean hasPermission(Player player, String permission) {
return player != null && player.hasPermission(permission);
public static boolean hasPermission(Player p, String perm) { return p != null && p.hasPermission(perm); }
public static void sendActionBar(Player p, String msg) {
if (p == null || !p.isOnline()) return;
p.sendActionBar(net.kyori.adventure.text.Component.text(msg));
}
public static void sendActionBar(Player player, String message) {
if (player == null || !player.isOnline()) return;
player.sendActionBar(net.kyori.adventure.text.Component.text(message));
}
public static void sendTitle(Player player, String title, String subtitle, int fadeIn, int stay, int fadeOut) {
if (player == null || !player.isOnline()) return;
player.showTitle(net.kyori.adventure.title.Title.title(
public static void sendTitle(Player p, String title, String sub, int in, int stay, int out) {
if (p == null || !p.isOnline()) return;
var t = net.kyori.adventure.title.Title.title(
net.kyori.adventure.text.Component.text(title != null ? title : ""),
net.kyori.adventure.text.Component.text(subtitle != null ? subtitle : ""),
net.kyori.adventure.text.Component.text(sub != null ? sub : ""),
net.kyori.adventure.title.Title.Times.times(
java.time.Duration.ofMillis(fadeIn * 50L),
java.time.Duration.ofMillis(in * 50L),
java.time.Duration.ofMillis(stay * 50L),
java.time.Duration.ofMillis(fadeOut * 50L)
)
));
java.time.Duration.ofMillis(out * 50L)));
p.showTitle(t);
}
}