From 79a5a4bfa539270fc1a9966eb0f536b002b5d31b Mon Sep 17 00:00:00 2001 From: JernejTDO Date: Tue, 16 Jun 2026 22:40:57 +0200 Subject: [PATCH] Upload --- .gitignore | 6 + README.md | 58 +++ pom.xml | 85 +++++ .../triler/jernejtdo/jholo/JHoloPlugin.java | 39 ++ .../jholo/commands/JHoloCommand.java | 332 ++++++++++++++++++ .../jholo/hologram/JHoloManager.java | 248 +++++++++++++ src/main/resources/config.yml | 20 ++ src/main/resources/plugin.yml | 17 + 8 files changed, 805 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/net/triler/jernejtdo/jholo/JHoloPlugin.java create mode 100644 src/main/java/net/triler/jernejtdo/jholo/commands/JHoloCommand.java create mode 100644 src/main/java/net/triler/jernejtdo/jholo/hologram/JHoloManager.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..434dfcf --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +target/ +.idea/ +*.iml +.classpath +.project +.settings/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a4954f --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# jHolo + +jHolo je simple Spigot/Paper hologram plugin za Minecraft 1.21.2. +Uporablja Triler repo dependency `net.triler:minecraft-holograms:1.0`. + +## Build + +```bash +mvn clean package +``` + +JAR bo v: + +```text +target/jHolo-1.0.0.jar +``` + +## Namestitev + +1. Buildaj plugin z `mvn clean package`. +2. Kopiraj `target/jHolo-1.0.0.jar` v server `plugins` mapo. +3. Zaženi server. +4. Uredi `plugins/jHolo/config.yml` ali uporabi ukaze. + +## Ukazi + +```text +/jholo help +/jholo create +/jholo delete +/jholo list +/jholo movehere +/jholo tp +/jholo info +/jholo addline +/jholo setline <št.> +/jholo removeline <št.> +/jholo billboard +/jholo reload +``` + +Za več vrstic pri create uporabi `|`: + +```text +/jholo create spawn &b&lSPAWN | &7Dobrodošel na serverju | &eUporabi /menu +``` + +## Permission + +```text +jholo.admin +``` + +## Package + +```text +net.triler.jernejtdo.jholo +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1ddc937 --- /dev/null +++ b/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + net.triler.jernejtdo + jholo + 1.0.0 + jar + + jHolo + Simple config based hologram plugin using Triler minecraft-holograms library. + + + 25 + UTF-8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + trilergit + https://git.triler.eu/api/packages/TrilerCode/maven + + + + + + org.spigotmc + spigot-api + 26.1.2-R0.1-SNAPSHOT + provided + + + + net.triler + minecraft-holograms + 1.0 + + + + + jHolo-${project.version} + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + ${java.version} + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + false + false + + + + + + + diff --git a/src/main/java/net/triler/jernejtdo/jholo/JHoloPlugin.java b/src/main/java/net/triler/jernejtdo/jholo/JHoloPlugin.java new file mode 100644 index 0000000..a3c8397 --- /dev/null +++ b/src/main/java/net/triler/jernejtdo/jholo/JHoloPlugin.java @@ -0,0 +1,39 @@ +package net.triler.jernejtdo.jholo; + +import net.triler.HologramManager; +import net.triler.jernejtdo.jholo.commands.JHoloCommand; +import net.triler.jernejtdo.jholo.hologram.JHoloManager; +import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.java.JavaPlugin; + +public final class JHoloPlugin extends JavaPlugin { + + private JHoloManager holoManager; + + @Override + public void onEnable() { + saveDefaultConfig(); + + this.holoManager = new JHoloManager(this); + this.holoManager.loadFromConfig(); + + JHoloCommand command = new JHoloCommand(this, holoManager); + PluginCommand pluginCommand = getCommand("jholo"); + if (pluginCommand != null) { + pluginCommand.setExecutor(command); + pluginCommand.setTabCompleter(command); + } + + getLogger().info("jHolo enabled. Loaded " + holoManager.getHologramIds().size() + " holograms."); + } + + @Override + public void onDisable() { + HologramManager.removeAll(); + getLogger().info("jHolo disabled. All spawned holograms removed."); + } + + public JHoloManager getHoloManager() { + return holoManager; + } +} diff --git a/src/main/java/net/triler/jernejtdo/jholo/commands/JHoloCommand.java b/src/main/java/net/triler/jernejtdo/jholo/commands/JHoloCommand.java new file mode 100644 index 0000000..013ac85 --- /dev/null +++ b/src/main/java/net/triler/jernejtdo/jholo/commands/JHoloCommand.java @@ -0,0 +1,332 @@ +package net.triler.jernejtdo.jholo.commands; + +import net.triler.jernejtdo.jholo.JHoloPlugin; +import net.triler.jernejtdo.jholo.hologram.JHoloManager; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Display; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +public final class JHoloCommand implements CommandExecutor, TabCompleter { + + private final JHoloPlugin plugin; + private final JHoloManager manager; + + private static final List SUB_COMMANDS = List.of( + "help", "reload", "list", "create", "delete", "movehere", "tp", "info", + "addline", "setline", "removeline", "billboard" + ); + + public JHoloCommand(JHoloPlugin plugin, JHoloManager manager) { + this.plugin = plugin; + this.manager = manager; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!sender.hasPermission("jholo.admin")) { + send(sender, "&cYou do not have permission: &fjholo.admin"); + return true; + } + + if (args.length == 0 || args[0].equalsIgnoreCase("help")) { + sendHelp(sender); + return true; + } + + String sub = args[0].toLowerCase(Locale.ROOT); + + switch (sub) { + case "reload" -> { + plugin.reloadConfig(); + manager.loadFromConfig(); + send(sender, "&aConfig reloaded. Holograms: &f" + manager.getHologramIds().size()); + } + case "list" -> list(sender); + case "create" -> create(sender, args); + case "delete", "remove" -> delete(sender, args); + case "movehere" -> moveHere(sender, args); + case "tp", "teleport" -> teleport(sender, args); + case "info" -> info(sender, args); + case "addline" -> addLine(sender, args); + case "setline" -> setLine(sender, args); + case "removeline", "delline" -> removeLine(sender, args); + case "billboard" -> billboard(sender, args); + default -> send(sender, "&cUnknown command. Use &f/jholo help&c."); + } + + return true; + } + + private void sendHelp(CommandSender sender) { + send(sender, "&bjHolo commands:"); + raw(sender, "&e/jholo create &7- create a hologram at your location"); + raw(sender, "&e/jholo delete &7- delete a hologram"); + raw(sender, "&e/jholo list &7- list holograms"); + raw(sender, "&e/jholo movehere &7- move a hologram to your location"); + raw(sender, "&e/jholo tp &7- teleport to a hologram"); + raw(sender, "&e/jholo addline &7- add a line"); + raw(sender, "&e/jholo setline &7- change a line"); + raw(sender, "&e/jholo removeline &7- remove a line"); + raw(sender, "&e/jholo billboard &7- display direction"); + raw(sender, "&e/jholo reload &7- reload config"); + raw(sender, "&7For multiple lines in create, use &f| &7or &f\\n&7. Example: &f/jholo create test &bTitle | &7description"); + } + + private void list(CommandSender sender) { + Set ids = manager.getHologramIds(); + if (ids.isEmpty()) { + send(sender, "&7No holograms loaded."); + return; + } + send(sender, "&7Holograms (&f" + ids.size() + "&7): &f" + String.join("&7, &f", ids)); + } + + private void create(CommandSender sender, String[] args) { + if (!(sender instanceof Player player)) { + send(sender, "&cOnly players can use this command."); + return; + } + if (args.length < 3) { + send(sender, "&cUsage: &f/jholo create "); + return; + } + + String id = args[1]; + if (!manager.isValidId(id)) { + send(sender, "&cID may only contain A-Z, 0-9, _ and - and must be max 32 characters."); + return; + } + if (manager.exists(id)) { + send(sender, "&cA hologram with this ID already exists. First use &f/jholo delete " + id); + return; + } + + List lines = parseLines(join(args, 2)); + manager.create(id, player.getLocation(), lines, Display.Billboard.CENTER); + send(sender, "&aHologram &f" + id + " &acreated."); + } + + private void delete(CommandSender sender, String[] args) { + if (args.length < 2) { + send(sender, "&cUsage: &f/jholo delete "); + return; + } + if (manager.delete(args[1])) { + send(sender, "&aHologram &f" + args[1] + " &adeleted."); + } else { + send(sender, "&cHologram does not exist: &f" + args[1]); + } + } + + private void moveHere(CommandSender sender, String[] args) { + if (!(sender instanceof Player player)) { + send(sender, "&cOnly players can use this command."); + return; + } + if (args.length < 2) { + send(sender, "&cUsage: &f/jholo movehere "); + return; + } + if (manager.moveTo(args[1], player.getLocation())) { + send(sender, "&aHologram &f" + args[1] + " &amoved to your location."); + } else { + send(sender, "&cHologram does not exist: &f" + args[1]); + } + } + + private void teleport(CommandSender sender, String[] args) { + if (!(sender instanceof Player player)) { + send(sender, "&cOnly players can use this command."); + return; + } + if (args.length < 2) { + send(sender, "&cUsage: &f/jholo tp "); + return; + } + Location location = manager.getLocation(args[1]); + if (location == null) { + send(sender, "&cHologram does not exist or the world is not loaded: &f" + args[1]); + return; + } + player.teleport(location); + send(sender, "&aTeleported to hologram &f" + args[1] + "&a."); + } + + private void info(CommandSender sender, String[] args) { + if (args.length < 2) { + send(sender, "&cUsage: &f/jholo info "); + return; + } + String id = args[1]; + if (!manager.exists(id)) { + send(sender, "&cHologram does not exist: &f" + id); + return; + } + Location location = manager.getLocation(id); + send(sender, "&bInfo for &f" + id + "&b:"); + if (location != null && location.getWorld() != null) { + raw(sender, "&7World: &f" + location.getWorld().getName()); + raw(sender, "&7Location: &fx=" + round(location.getX()) + " y=" + round(location.getY()) + " z=" + round(location.getZ())); + } + raw(sender, "&7Billboard: &f" + manager.getBillboard(id).name()); + List lines = manager.getLines(id); + for (int i = 0; i < lines.size(); i++) { + raw(sender, "&7" + (i + 1) + ". &f" + lines.get(i)); + } + } + + private void addLine(CommandSender sender, String[] args) { + if (args.length < 3) { + send(sender, "&cUsage: &f/jholo addline "); + return; + } + if (manager.addLine(args[1], join(args, 2))) { + send(sender, "&aLine added to &f" + args[1] + "&a."); + } else { + send(sender, "&cHologram does not exist: &f" + args[1]); + } + } + + private void setLine(CommandSender sender, String[] args) { + if (args.length < 4) { + send(sender, "&cUsage: &f/jholo setline "); + return; + } + Integer line = parseInt(args[2]); + if (line == null) { + send(sender, "&cLine number is not valid."); + return; + } + if (manager.setLine(args[1], line, join(args, 3))) { + send(sender, "&aLine &f" + line + " &achanged in &f" + args[1] + "&a."); + } else { + send(sender, "&cHologram or line does not exist."); + } + } + + private void removeLine(CommandSender sender, String[] args) { + if (args.length < 3) { + send(sender, "&cUsage: &f/jholo removeline "); + return; + } + Integer line = parseInt(args[2]); + if (line == null) { + send(sender, "&cLine number is not valid."); + return; + } + if (manager.removeLine(args[1], line)) { + send(sender, "&aLine &f" + line + " &aremoved from &f" + args[1] + "&a."); + } else { + send(sender, "&cHologram or line does not exist."); + } + } + + private void billboard(CommandSender sender, String[] args) { + if (args.length < 3) { + send(sender, "&cUsage: &f/jholo billboard "); + return; + } + Display.Billboard billboard; + try { + billboard = Display.Billboard.valueOf(args[2].toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException exception) { + send(sender, "&cInvalid billboard. Use: &fCENTER, FIXED, HORIZONTAL, VERTICAL"); + return; + } + if (manager.setBillboard(args[1], billboard)) { + send(sender, "&aBillboard for &f" + args[1] + " &aset to &f" + billboard.name() + "&a."); + } else { + send(sender, "&cHologram does not exist: &f" + args[1]); + } + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (!sender.hasPermission("jholo.admin")) { + return Collections.emptyList(); + } + + if (args.length == 1) { + return filter(SUB_COMMANDS, args[0]); + } + + String sub = args[0].toLowerCase(Locale.ROOT); + if (args.length == 2 && List.of("delete", "remove", "movehere", "tp", "teleport", "info", "addline", "setline", "removeline", "delline", "billboard").contains(sub)) { + return filter(new ArrayList<>(manager.getHologramIds()), args[1]); + } + + if (sub.equals("billboard") && args.length == 3) { + return filter(Arrays.stream(Display.Billboard.values()).map(Enum::name).toList(), args[2]); + } + + return Collections.emptyList(); + } + + private List filter(List options, String input) { + String lower = input.toLowerCase(Locale.ROOT); + List result = new ArrayList<>(); + for (String option : options) { + if (option.toLowerCase(Locale.ROOT).startsWith(lower)) { + result.add(option); + } + } + return result; + } + + private List parseLines(String raw) { + String[] split = raw.split("\\s*\\|\\s*|\\\\n"); + List lines = new ArrayList<>(); + for (String line : split) { + if (!line.isBlank()) { + lines.add(line); + } + } + if (lines.isEmpty()) { + lines.add("&cEmpty hologram"); + } + return lines; + } + + private String join(String[] args, int start) { + StringBuilder builder = new StringBuilder(); + for (int i = start; i < args.length; i++) { + if (i > start) { + builder.append(' '); + } + builder.append(args[i]); + } + return builder.toString(); + } + + private Integer parseInt(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException exception) { + return null; + } + } + + private String round(double value) { + return String.format(Locale.US, "%.2f", value); + } + + private void send(CommandSender sender, String message) { + sender.sendMessage(manager.getPrefix() + ChatColor.translateAlternateColorCodes('&', message)); + } + + private void raw(CommandSender sender, String message) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); + } +} diff --git a/src/main/java/net/triler/jernejtdo/jholo/hologram/JHoloManager.java b/src/main/java/net/triler/jernejtdo/jholo/hologram/JHoloManager.java new file mode 100644 index 0000000..f7dd188 --- /dev/null +++ b/src/main/java/net/triler/jernejtdo/jholo/hologram/JHoloManager.java @@ -0,0 +1,248 @@ +package net.triler.jernejtdo.jholo.hologram; + +import net.triler.HologramManager; +import net.triler.jernejtdo.jholo.JHoloPlugin; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Display; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +public final class JHoloManager { + + private final JHoloPlugin plugin; + private final Set hologramIds = new LinkedHashSet<>(); + + public JHoloManager(JHoloPlugin plugin) { + this.plugin = plugin; + } + + public void loadFromConfig() { + HologramManager.removeAll(); + hologramIds.clear(); + + ConfigurationSection section = plugin.getConfig().getConfigurationSection("holograms"); + if (section == null) { + return; + } + + for (String id : section.getKeys(false)) { + spawnFromConfig(id); + } + } + + public boolean spawnFromConfig(String id) { + String path = path(id); + World world = Bukkit.getWorld(plugin.getConfig().getString(path + ".world", "world")); + if (world == null) { + plugin.getLogger().warning("Cannot load hologram '" + id + "': world does not exist."); + return false; + } + + double x = plugin.getConfig().getDouble(path + ".x"); + double y = plugin.getConfig().getDouble(path + ".y"); + double z = plugin.getConfig().getDouble(path + ".z"); + float yaw = (float) plugin.getConfig().getDouble(path + ".yaw", 0.0); + float pitch = (float) plugin.getConfig().getDouble(path + ".pitch", 0.0); + Location location = new Location(world, x, y, z, yaw, pitch); + + Display.Billboard billboard = parseBillboard( + plugin.getConfig().getString(path + ".billboard", plugin.getConfig().getString("settings.default-billboard", "CENTER")) + ); + List lines = getLines(id); + + HologramManager.create(id, location, billboard, toDisplayText(lines)); + hologramIds.add(id); + return true; + } + + public void create(String id, Location location, List lines, Display.Billboard billboard) { + writeConfig(id, location, lines, billboard); + HologramManager.create(id, location, billboard, toDisplayText(lines)); + hologramIds.add(id); + } + + public boolean delete(String id) { + if (!exists(id)) { + return false; + } + HologramManager.remove(id); + hologramIds.remove(id); + plugin.getConfig().set(path(id), null); + plugin.saveConfig(); + return true; + } + + public boolean moveTo(String id, Location location) { + if (!exists(id)) { + return false; + } + List lines = getLines(id); + Display.Billboard billboard = getBillboard(id); + writeConfig(id, location, lines, billboard); + HologramManager.create(id, location, billboard, toDisplayText(lines)); + hologramIds.add(id); + return true; + } + + public boolean setLines(String id, List lines) { + if (!exists(id)) { + return false; + } + plugin.getConfig().set(path(id) + ".lines", lines); + plugin.saveConfig(); + HologramManager.update(id, toDisplayText(lines)); + return true; + } + + public boolean addLine(String id, String line) { + if (!exists(id)) { + return false; + } + List lines = getLines(id); + lines.add(line); + return setLines(id, lines); + } + + public boolean setLine(String id, int lineNumber, String text) { + if (!exists(id)) { + return false; + } + List lines = getLines(id); + int index = lineNumber - 1; + if (index < 0 || index >= lines.size()) { + return false; + } + lines.set(index, text); + return setLines(id, lines); + } + + public boolean removeLine(String id, int lineNumber) { + if (!exists(id)) { + return false; + } + List lines = getLines(id); + int index = lineNumber - 1; + if (index < 0 || index >= lines.size()) { + return false; + } + lines.remove(index); + if (lines.isEmpty()) { + lines.add("&cEmpty hologram"); + } + return setLines(id, lines); + } + + public boolean setBillboard(String id, Display.Billboard billboard) { + if (!exists(id)) { + return false; + } + plugin.getConfig().set(path(id) + ".billboard", billboard.name()); + plugin.saveConfig(); + Location location = getLocation(id); + if (location == null) { + return false; + } + HologramManager.create(id, location, billboard, toDisplayText(getLines(id))); + hologramIds.add(id); + return true; + } + + public Location getLocation(String id) { + if (!exists(id)) { + return null; + } + String path = path(id); + World world = Bukkit.getWorld(plugin.getConfig().getString(path + ".world", "world")); + if (world == null) { + return null; + } + return new Location( + world, + plugin.getConfig().getDouble(path + ".x"), + plugin.getConfig().getDouble(path + ".y"), + plugin.getConfig().getDouble(path + ".z"), + (float) plugin.getConfig().getDouble(path + ".yaw", 0.0), + (float) plugin.getConfig().getDouble(path + ".pitch", 0.0) + ); + } + + public List getLines(String id) { + List lines = plugin.getConfig().getStringList(path(id) + ".lines"); + if (lines == null || lines.isEmpty()) { + return new ArrayList<>(Collections.singletonList("&cEmpty hologram")); + } + return new ArrayList<>(lines); + } + + public Display.Billboard getBillboard(String id) { + return parseBillboard(plugin.getConfig().getString(path(id) + ".billboard", "CENTER")); + } + + public boolean exists(String id) { + return plugin.getConfig().isConfigurationSection(path(id)); + } + + public Set getHologramIds() { + List sorted = new ArrayList<>(hologramIds); + sorted.sort(Comparator.naturalOrder()); + return new LinkedHashSet<>(sorted); + } + + public String getPrefix() { + return color(plugin.getConfig().getString("settings.prefix", "&8[&bjHolo&8] &7")); + } + + public boolean isValidId(String id) { + return id != null && id.matches("[A-Za-z0-9_-]{1,32}"); + } + + public Display.Billboard parseBillboard(String input) { + if (input == null) { + return Display.Billboard.CENTER; + } + try { + return Display.Billboard.valueOf(input.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException exception) { + return Display.Billboard.CENTER; + } + } + + public String color(String text) { + return ChatColor.translateAlternateColorCodes('&', text == null ? "" : text); + } + + public String toDisplayText(List lines) { + List colored = new ArrayList<>(); + for (String line : lines) { + colored.add(color(line)); + } + return String.join("\n", colored); + } + + private void writeConfig(String id, Location location, List lines, Display.Billboard billboard) { + String path = path(id); + plugin.getConfig().set(path + ".world", location.getWorld() == null ? "world" : location.getWorld().getName()); + plugin.getConfig().set(path + ".x", location.getX()); + plugin.getConfig().set(path + ".y", location.getY()); + plugin.getConfig().set(path + ".z", location.getZ()); + plugin.getConfig().set(path + ".yaw", location.getYaw()); + plugin.getConfig().set(path + ".pitch", location.getPitch()); + plugin.getConfig().set(path + ".billboard", billboard.name()); + plugin.getConfig().set(path + ".lines", lines); + plugin.saveConfig(); + } + + private String path(String id) { + return "holograms." + id; + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..4186b6c --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,20 @@ +# jHolo config +# Holograms are loaded automatically on server start and /jholo reload. + +settings: + prefix: "&8[&bjHolo&8] &7" + default-billboard: "CENTER" + +holograms: + welcome: + world: "world" + x: 0.5 + y: 100.0 + z: 0.5 + yaw: 0.0 + pitch: 0.0 + billboard: "CENTER" + lines: + - "&b&ljHolo" + - "&7Primer holograma iz config.yml" + - "&e/jholo help" diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..3ab0b79 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: jHolo +version: '${project.version}' +main: net.triler.jernejtdo.jholo.JHoloPlugin +api-version: '26.1.2' +author: JernejTDO +description: Config based hologram plugin using Triler minecraft-holograms library. + +commands: + jholo: + description: Main jHolo command. + usage: /jholo help + aliases: [holo, hologram, holograms] + +permissions: + jholo.admin: + description: Allows using all jHolo commands. + default: op