From e395b8d25daf8ca2c33a15672294907dd6ae9d71 Mon Sep 17 00:00:00 2001 From: TubYoub Date: Thu, 4 Jul 2024 15:32:54 +0200 Subject: [PATCH 1/8] Config added Changes: - Config file added - ConfigManager added TODO: - Commands - Code Cleanup - Code Documentation - Timer for how long a Grave is valid - more probably coming Took 4 hours 22 minutes --- .../dev/pluginz/graveplugin/GravePlugin.java | 15 +- .../graveplugin/listener/GraveListener.java | 6 +- .../graveplugin/manager/ConfigManager.java | 18 +- .../graveplugin/manager/GraveManager.java | 166 ++++++++---------- .../dev/pluginz/graveplugin/util/Grave.java | 11 +- src/main/resources/config.yml | 17 ++ 6 files changed, 117 insertions(+), 116 deletions(-) diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index b6f4dc7..f57ba3b 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -2,27 +2,36 @@ import dev.pluginz.graveplugin.command.GraveCommand; import dev.pluginz.graveplugin.listener.GraveListener; +import dev.pluginz.graveplugin.manager.ConfigManager; import dev.pluginz.graveplugin.manager.GraveManager; import org.bukkit.plugin.java.JavaPlugin; +import java.util.UUID; + public class GravePlugin extends JavaPlugin { private GraveManager graveManager; + private ConfigManager configManager; @Override public void onEnable() { - saveDefaultConfig(); + configManager = new ConfigManager(this); + configManager.loadConfig(); graveManager = new GraveManager(this); graveManager.loadGraves(); - getServer().getPluginManager().registerEvents(new GraveListener(this, graveManager), this); + getServer().getPluginManager().registerEvents(new GraveListener(this), this); getCommand("grave").setExecutor(new GraveCommand(this)); } @Override public void onDisable() { - + graveManager.saveGraves(); + configManager.saveConfig(); } public GraveManager getGraveManager() { return graveManager; } + public ConfigManager getConfigManager(){ + return configManager; + } } diff --git a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java index c1f7511..4a442da 100644 --- a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java +++ b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java @@ -29,9 +29,9 @@ public class GraveListener implements Listener { private final GravePlugin plugin; private final GraveManager graveManager; - public GraveListener(GravePlugin plugin, GraveManager graveManager) { + public GraveListener(GravePlugin plugin) { this.plugin = plugin; - this.graveManager = graveManager; + this.graveManager = plugin.getGraveManager(); } @EventHandler @@ -150,4 +150,4 @@ public void onInventoryDrag(InventoryDragEvent event) { event.setCancelled(true); } } -} +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java b/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java index 1df1216..4f6a722 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java @@ -38,7 +38,7 @@ public class ConfigManager { private YamlDocument config; - private int combatTimeout; + private int graveTimeout; private boolean checkVersion; private final GravePlugin plugin; @@ -57,7 +57,7 @@ public void loadConfig() { UpdaterSettings.builder().setVersioning(new BasicVersioning("fileversion")) .setOptionSorting(UpdaterSettings.OptionSorting.SORT_BY_DEFAULTS).build()); - combatTimeout = config.getInt("combatTimeout", 30); + graveTimeout = config.getInt("graveTimeout", 60); checkVersion = config.getBoolean("checkVersion", true); } catch (IOException e) { //plugin.getLogger().severe("Could not load configuration: " + e.getMessage()); @@ -75,17 +75,11 @@ public void saveConfig() { public void reloadConfig() { loadConfig(); } - public int getCombatTimeout(){ - return combatTimeout; + public int getGraveTimeout(){ + return graveTimeout; } - public void setCombatTimeout(int combatTimeout) throws IOException { - if (this.combatTimeout == combatTimeout){ - return; - }else { - this.combatTimeout = combatTimeout; - config.set("combatTimeout", combatTimeout); - config.save(); - } + public void setGraveTimeout(int graveTimeout) { + this.graveTimeout = graveTimeout; } public boolean isCheckVersion() { diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java index ba8407f..6789310 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java @@ -2,10 +2,7 @@ import dev.pluginz.graveplugin.GravePlugin; import dev.pluginz.graveplugin.util.Grave; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; @@ -31,16 +28,14 @@ import java.util.UUID; public class GraveManager { - private final GravePlugin plugin; + private static GravePlugin plugin = null; private Map graves; private Map inventoryGraveMap; - private final long graveExpirationTime; public GraveManager(GravePlugin plugin) { this.plugin = plugin; this.graves = new HashMap<>(); this.inventoryGraveMap = new HashMap<>(); - this.graveExpirationTime = plugin.getConfig().getLong("graveExpirationTime", 12000); } @@ -48,7 +43,7 @@ public UUID createGrave(Player player, Location location, ItemStack[] items, Ite double x = location.getX() >= 0 ? 0.5 : -0.5; double y = -1.0; double z = location.getZ() >= 0 ? 0.5 : -0.5; - location.add(x,y,z); + location.add(x, y, z); ArmorStand armorStand = (ArmorStand) location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND); armorStand.setVisible(false); armorStand.setGravity(false); @@ -58,42 +53,34 @@ public UUID createGrave(Player player, Location location, ItemStack[] items, Ite UUID graveId = UUID.randomUUID(); UUID armorStandId = armorStand.getUniqueId(); - long expirationTime = System.currentTimeMillis() + graveExpirationTime * 50L; - graves.put(graveId, new Grave(graveId, location, items, armor, offHand, armorStandId, expirationTime)); - new BukkitRunnable() { - @Override - public void run() { - if (graves.containsKey(graveId) && System.currentTimeMillis() >= expirationTime) { - removeGrave(graveId); - } - } - }.runTaskLater(plugin, graveExpirationTime); + graves.put(graveId, new Grave(plugin, graveId, location, items, armor, offHand, armorStandId)); plugin.getLogger().info("Created grave for player " + player.getName() + " at " + location); - for (ItemStack item : items) { - if (item != null) { - plugin.getLogger().info("Item: " + item.getType()); - } - } - this.saveGraves(); + saveGraves(); return graveId; } - public boolean isGraveArmorStand(UUID uuid) { - return graves.containsKey(uuid); - } - - public void createGraveText(Location location, String playerName, long expirationTime) { - ArmorStand textStand = (ArmorStand) location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND); - textStand.setVisible(false); - textStand.setGravity(false); - textStand.setCustomName(playerName + "'s Grave\nExpires in: " + (expirationTime / 1200) + " minutes"); - textStand.setCustomNameVisible(true); - textStand.setInvulnerable(true); - plugin.getLogger().info("Created grave text for player " + playerName + " at " + location); + public static String getColoredTime(int remainingTime) { + int hours = remainingTime / 3600; + int minutes = (remainingTime % 3600) / 60; + int seconds = remainingTime % 60; + + String time = String.format("%02dh %02dm %02ds", hours, minutes, seconds); + + double ratio = (double) remainingTime / plugin.getConfigManager().getGraveTimeout(); + ChatColor color; + if (ratio > 0.66) { + color = ChatColor.GREEN; + } else if (ratio > 0.33) { + color = ChatColor.GOLD; // ChatColor.GOLD is similar to orange + } else { + color = ChatColor.RED; + } + return color + time; } + public void openGrave(Player player, UUID graveId) { Grave grave = graves.get(graveId); if (grave == null) return; @@ -231,13 +218,14 @@ public void dropGraveItems(Inventory inventory, Player player, UUID graveId) { public void removeGrave(UUID graveId) { - Grave grave = graves.get(graveId); + Grave grave = graves.remove(graveId); if (grave != null) { - ArmorStand armorStand = grave.getLocation().getWorld().getEntitiesByClass(ArmorStand.class).stream() - .filter(entity -> entity.getUniqueId().equals(grave.getArmorStandId())) - .findFirst() - .orElse(null); + ArmorStand armorStand = (ArmorStand) grave.getLocation().getWorld().getEntitiesByClass(ArmorStand.class).stream() + .filter(entity -> entity.getUniqueId().equals(grave.getArmorStandId())) + .findFirst() + .orElse(null); if (armorStand != null) { + Bukkit.getScheduler().runTaskLater(plugin, () -> armorStand.remove(), 1); armorStand.remove(); } @@ -246,12 +234,10 @@ public void removeGrave(UUID graveId) { block.setType(Material.AIR); } - graves.remove(graveId); - this.saveGraves(); + saveGraves(); plugin.getLogger().info("Removed grave with UUID: " + graveId); } } - private ItemStack createGlassPane(Material material, String name) { ItemStack pane = new ItemStack(material); ItemMeta meta = pane.getItemMeta(); @@ -267,10 +253,6 @@ public UUID getGraveIdFromInventory(Inventory inventory) { return inventoryGraveMap.get(inventory); } - - public long getGraveExpirationTime() { - return graveExpirationTime; - } public Grave getGraveFromUUID(UUID armorStandUUID) { for (Grave grave : graves.values()) { if (grave.getArmorStandId().equals(armorStandUUID)) { @@ -280,6 +262,10 @@ public Grave getGraveFromUUID(UUID armorStandUUID) { return null; } + public boolean isGraveArmorStand(UUID uuid) { + return graves.containsKey(uuid); + } + public UUID getGraveIdFromArmorStand(UUID armorStandId) { for (Map.Entry entry : graves.entrySet()) { if (entry.getValue().getArmorStandId().equals(armorStandId)) { @@ -303,7 +289,6 @@ public void saveGraves() { config.set(path + ".location", grave.getLocation().toVector()); config.set(path + ".world", grave.getLocation().getWorld().getName()); config.set(path + ".armorStandId", grave.getArmorStandId().toString()); - config.set(path + ".expirationTime", grave.getExpirationTime()); config.set(path + ".items", itemStackArrayToBase64(grave.getItems())); config.set(path + ".armorItems", itemStackArrayToBase64(grave.getArmorItems())); config.set(path + ".offHand", itemStackToBase64(grave.getOffHand())); @@ -316,20 +301,53 @@ public void saveGraves() { } } - private String itemStackArrayToBase64(ItemStack[] items) throws IllegalStateException { + public void loadGraves() { + File file = new File(plugin.getDataFolder(), "graves.yml"); + if (!file.exists()) { + return; + } + + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + ConfigurationSection section = config.getConfigurationSection("graves"); + + if (section != null) { + for (String key : section.getKeys(false)) { + String path = "graves." + key; + String worldName = config.getString(path + ".world"); + if (worldName == null) { + plugin.getLogger().severe("World name is null for grave " + key); + continue; + } + + World world = Bukkit.getWorld(worldName); + if (world == null) { + plugin.getLogger().severe("World " + worldName + " does not exist on the server"); + continue; + } + + Location location = config.getVector(path + ".location").toLocation(world); + UUID armorStandId = UUID.fromString(config.getString(path + ".armorStandId")); + long expirationTime = config.getLong(path + ".relativeExpirationTime"); + ItemStack[] items = itemStackArrayFromBase64(config.getString(path + ".items")); + ItemStack[] armorItems = itemStackArrayFromBase64(config.getString(path + ".armorItems")); + ItemStack offHand = itemStackFromBase64(config.getString(path + ".offHand")); + + graves.put(UUID.fromString(key), new Grave(plugin, UUID.fromString(key), location, items, armorItems, offHand, armorStandId)); + } + } + } + + private String itemStackArrayToBase64(ItemStack[] items) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); - // Write the size of the inventory dataOutput.writeInt(items.length); - // Save every element in the list - for (int i = 0; i < items.length; i++) { - dataOutput.writeObject(items[i]); + for (ItemStack item : items) { + dataOutput.writeObject(item); } - // Serialize that array dataOutput.close(); return Base64.getEncoder().encodeToString(outputStream.toByteArray()); } catch (Exception e) { @@ -337,7 +355,7 @@ private String itemStackArrayToBase64(ItemStack[] items) throws IllegalStateExce } } - private String itemStackToBase64(ItemStack item) throws IllegalStateException { + private String itemStackToBase64(ItemStack item) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); @@ -351,39 +369,6 @@ private String itemStackToBase64(ItemStack item) throws IllegalStateException { } } - public void loadGraves() { - File file = new File(plugin.getDataFolder(), "graves.yml"); - if (!file.exists()) { - return; - } - - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - ConfigurationSection section = config.getConfigurationSection("graves"); - - for (String key : section.getKeys(false)) { - String path = "graves." + key; - String worldName = config.getString(path + ".world"); - if (worldName == null) { - plugin.getLogger().severe("World name is null for grave " + key); - continue; - } - - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getLogger().severe("World " + worldName + " does not exist on the server"); - continue; - } - - Location location = config.getVector(path + ".location").toLocation(Bukkit.getWorld(config.getString(path + ".world"))); - UUID armorStandId = UUID.fromString(config.getString(path + ".armorStandId")); - long expirationTime = config.getLong(path + ".expirationTime"); - ItemStack[] items = itemStackArrayFromBase64(config.getString(path + ".items")); - ItemStack[] armorItems = itemStackArrayFromBase64(config.getString(path + ".armorItems")); - ItemStack offHand = itemStackFromBase64(config.getString(path + ".offHand")); - - graves.put(UUID.fromString(key), new Grave(UUID.fromString(key), location, items, armorItems, offHand, armorStandId, expirationTime)); - } - } private ItemStack[] itemStackArrayFromBase64(String data) { try { @@ -391,7 +376,6 @@ private ItemStack[] itemStackArrayFromBase64(String data) { BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); ItemStack[] items = new ItemStack[dataInput.readInt()]; - // Read the serialized inventory for (int i = 0; i < items.length; i++) { items[i] = (ItemStack) dataInput.readObject(); } @@ -416,4 +400,4 @@ private ItemStack itemStackFromBase64(String data) { throw new IllegalStateException("Unable to load item stack.", e); } } -} +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/util/Grave.java b/src/main/java/dev/pluginz/graveplugin/util/Grave.java index f54fae0..fb7936f 100644 --- a/src/main/java/dev/pluginz/graveplugin/util/Grave.java +++ b/src/main/java/dev/pluginz/graveplugin/util/Grave.java @@ -1,27 +1,28 @@ package dev.pluginz.graveplugin.util; +import dev.pluginz.graveplugin.GravePlugin; import org.bukkit.Location; import org.bukkit.inventory.ItemStack; import java.util.UUID; public class Grave { + private final GravePlugin plugin; private final UUID graveId; private final Location location; private final ItemStack[] items; private final ItemStack[] armorItems; private final ItemStack offHand; private final UUID armorStandId; - private final long expirationTime; - public Grave(UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId, long expirationTime) { + public Grave(GravePlugin plugin, UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId) { + this.plugin = plugin; this.graveId = graveId; this.location = location; this.items = items; this.armorItems = armorItems; this.offHand = offHand; this.armorStandId = armorStandId; - this.expirationTime = expirationTime; } public UUID getGraveId() { @@ -47,8 +48,4 @@ public ItemStack getOffHand() { public UUID getArmorStandId() { return armorStandId; } - - public long getExpirationTime() { - return expirationTime; - } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e69de29..d9bfd2d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -0,0 +1,17 @@ +################################ +# Tub's StatusPlugin # +# by BT Pluginz # +################################ +#------------------------------ +# Do not touch this value +# The Plugin updates the config file using it. +fileversion: 1 +#------------------------------ + +# If the Plugin should check for new Versions +# default: true +checkVersion: true +# How long should graves last (in minutes) +# If you don't want to delete graves +# default: 60 +graveTimeout: 60 \ No newline at end of file From 2b71c246f5e6a836eb6ed8a36956f1b72c5e8db0 Mon Sep 17 00:00:00 2001 From: TubYoub Date: Sat, 6 Jul 2024 16:11:22 +0200 Subject: [PATCH 2/8] Grave Timeout Changes: - The grave now has a Timeout which can be set in the config - The Plugin saves the time elapsed every 10 seconds - Graves do not expire/update while being in an unloaded chunk/ 50 blocks in range of a player - Player names are now saved in grave data - The Grave Expiration Time is shown in the Grave Name - Graves drop the Items when expiring TODO: - Commands - Code Cleanup - Code Documentation - more probably coming Took 5 hours 31 minutes --- .../dev/pluginz/graveplugin/GravePlugin.java | 6 +- .../graveplugin/manager/GraveManager.java | 129 +++++++++++++++--- .../dev/pluginz/graveplugin/util/Grave.java | 39 +++++- 3 files changed, 148 insertions(+), 26 deletions(-) diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index f57ba3b..ce4aa1d 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -6,8 +6,6 @@ import dev.pluginz.graveplugin.manager.GraveManager; import org.bukkit.plugin.java.JavaPlugin; -import java.util.UUID; - public class GravePlugin extends JavaPlugin { private GraveManager graveManager; private ConfigManager configManager; @@ -25,7 +23,7 @@ public void onEnable() { @Override public void onDisable() { graveManager.saveGraves(); - configManager.saveConfig(); + //configManager.saveConfig(); } public GraveManager getGraveManager() { @@ -34,4 +32,4 @@ public GraveManager getGraveManager() { public ConfigManager getConfigManager(){ return configManager; } -} +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java index 6789310..1496743 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java @@ -7,6 +7,7 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -22,20 +23,19 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class GraveManager { private static GravePlugin plugin = null; private Map graves; private Map inventoryGraveMap; + private long lastUpdateTime; public GraveManager(GravePlugin plugin) { this.plugin = plugin; this.graves = new HashMap<>(); this.inventoryGraveMap = new HashMap<>(); + this.startGraveTimeoutTask(); } @@ -54,26 +54,35 @@ public UUID createGrave(Player player, Location location, ItemStack[] items, Ite UUID graveId = UUID.randomUUID(); UUID armorStandId = armorStand.getUniqueId(); - graves.put(graveId, new Grave(plugin, graveId, location, items, armor, offHand, armorStandId)); + long maxActiveTime = plugin.getConfigManager().getGraveTimeout() * 60 * 1000; + if(plugin.getConfigManager().getGraveTimeout() == -1) { + maxActiveTime = -1; + } + + Grave grave = new Grave(player.getName(), graveId, location, items, armor, offHand, armorStandId, maxActiveTime, false); + graves.put(graveId, grave); plugin.getLogger().info("Created grave for player " + player.getName() + " at " + location); saveGraves(); return graveId; } - public static String getColoredTime(int remainingTime) { - int hours = remainingTime / 3600; - int minutes = (remainingTime % 3600) / 60; - int seconds = remainingTime % 60; + public static String getColoredTime(Grave grave) { + long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); + int remainingSeconds = (int) (remainingTime / 1000); // Convert milliseconds to seconds + + int hours = remainingSeconds / 3600; + int minutes = (remainingSeconds % 3600) / 60; + int seconds = remainingSeconds % 60; String time = String.format("%02dh %02dm %02ds", hours, minutes, seconds); - double ratio = (double) remainingTime / plugin.getConfigManager().getGraveTimeout(); + double ratio = (double) grave.getActiveTime() / grave.getMaxActiveTime(); ChatColor color; - if (ratio > 0.66) { + if (ratio < 0.33) { color = ChatColor.GREEN; - } else if (ratio > 0.33) { - color = ChatColor.GOLD; // ChatColor.GOLD is similar to orange + } else if (ratio < 0.66) { + color = ChatColor.GOLD; } else { color = ChatColor.RED; } @@ -81,9 +90,12 @@ public static String getColoredTime(int remainingTime) { } + public void openGrave(Player player, UUID graveId) { Grave grave = graves.get(graveId); - if (grave == null) return; + if (grave == null || grave.isExpired()){ + return; + } int inventorySize = 54; Inventory graveInventory = Bukkit.createInventory(null, inventorySize, "Grave of " + player.getName()); @@ -216,6 +228,17 @@ public void dropGraveItems(Inventory inventory, Player player, UUID graveId) { inventoryGraveMap.remove(inventory); } + private void dropGraveItemsAtLocation(Grave grave) { + World world = grave.getLocation().getWorld(); + if (world == null) return; + + // Drop main inventory items + for (ItemStack item : grave.getItems()) { + if (item != null) { + world.dropItemNaturally(grave.getLocation(), item); + } + } + } public void removeGrave(UUID graveId) { Grave grave = graves.remove(graveId); @@ -261,6 +284,13 @@ public Grave getGraveFromUUID(UUID armorStandUUID) { } return null; } + public UUID getArmorStandIdFromGraveId(UUID graveId) { + Grave grave = graves.get(graveId); + if (grave != null) { + return grave.getArmorStandId(); + } + return null; + } public boolean isGraveArmorStand(UUID uuid) { return graves.containsKey(uuid); @@ -279,6 +309,62 @@ public Map getGraves() { return graves; } + private void startGraveTimeoutTask() { + lastUpdateTime = System.currentTimeMillis(); + plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + long currentTime = System.currentTimeMillis(); + long elapsedTime = currentTime - lastUpdateTime; + lastUpdateTime = currentTime; + + Iterator iterator = graves.values().iterator(); + + while (iterator.hasNext()) { + Grave grave = iterator.next(); + grave.incrementActiveTime(elapsedTime); + + if (grave.getMaxActiveTime() == -1) { + continue; + } + + if (grave.isExpired()) { + if (grave.getLocation().getWorld().isChunkLoaded(grave.getLocation().getBlockX() >> 4, grave.getLocation().getBlockZ() >> 4)) { + if (isPlayerNearby(grave.getLocation(), 50)) { + this.dropGraveItemsAtLocation(grave); + this.removeGrave(grave.getGraveId()); + iterator.remove(); + } + } + } else if (isPlayerNearby(grave.getLocation(), 50)) { + long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); + String coloredTime = getColoredTime(grave); + updateGraveName(grave.getGraveId(), coloredTime); + } + } + }, 0L, 20L); // 20 ticks = 1 second + + plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + saveGraves(); + }, 0L, 200L); // 200 ticks = 10 seconds + } + + private boolean isPlayerNearby(Location location, double radius) { + return location.getWorld().getPlayers().stream() + .anyMatch(player -> player.getLocation().distanceSquared(location) <= radius * radius); + } + + public void updateGraveName(UUID graveId, String coloredTime) { + Grave grave = graves.get(graveId); + if (grave != null) { + Location location = grave.getLocation(); + World world = location.getWorld(); + if (world != null) { + ArmorStand armorStand = (ArmorStand) Bukkit.getEntity(this.getArmorStandIdFromGraveId(graveId)); + String playerName = grave.getPlayerName(); // Assuming you have this method in Grave class + armorStand.setCustomName(playerName + "'s Grave - " + coloredTime); + } + } + } + public void saveGraves() { File file = new File(plugin.getDataFolder(), "graves.yml"); YamlConfiguration config = new YamlConfiguration(); @@ -286,9 +372,13 @@ public void saveGraves() { for (Map.Entry entry : graves.entrySet()) { String path = "graves." + entry.getKey(); Grave grave = entry.getValue(); + config.set(path + ".playerName", grave.getPlayerName()); config.set(path + ".location", grave.getLocation().toVector()); config.set(path + ".world", grave.getLocation().getWorld().getName()); config.set(path + ".armorStandId", grave.getArmorStandId().toString()); + config.set(path + ".activeTime", grave.getActiveTime()); + config.set(path + ".maxActiveTime", grave.getMaxActiveTime()); + config.set(path + ".expired", grave.isExpired()); config.set(path + ".items", itemStackArrayToBase64(grave.getItems())); config.set(path + ".armorItems", itemStackArrayToBase64(grave.getArmorItems())); config.set(path + ".offHand", itemStackToBase64(grave.getOffHand())); @@ -325,14 +415,20 @@ public void loadGraves() { continue; } + String playerName = config.getString(path + ".playerName"); Location location = config.getVector(path + ".location").toLocation(world); UUID armorStandId = UUID.fromString(config.getString(path + ".armorStandId")); - long expirationTime = config.getLong(path + ".relativeExpirationTime"); + long activeTime = config.getLong(path + ".activeTime", 0); + long maxActiveTime = config.getLong(path + ".maxActiveTime"); + boolean expired = config.getBoolean(path + ".expired", false); ItemStack[] items = itemStackArrayFromBase64(config.getString(path + ".items")); ItemStack[] armorItems = itemStackArrayFromBase64(config.getString(path + ".armorItems")); ItemStack offHand = itemStackFromBase64(config.getString(path + ".offHand")); - graves.put(UUID.fromString(key), new Grave(plugin, UUID.fromString(key), location, items, armorItems, offHand, armorStandId)); + Grave grave = new Grave(playerName, UUID.fromString(key), location, items, armorItems, offHand, armorStandId, maxActiveTime, expired); + graves.put(UUID.fromString(key), grave); + grave.incrementActiveTime(activeTime); + } } } @@ -369,7 +465,6 @@ private String itemStackToBase64(ItemStack item) { } } - private ItemStack[] itemStackArrayFromBase64(String data) { try { ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data)); diff --git a/src/main/java/dev/pluginz/graveplugin/util/Grave.java b/src/main/java/dev/pluginz/graveplugin/util/Grave.java index fb7936f..c7b26e0 100644 --- a/src/main/java/dev/pluginz/graveplugin/util/Grave.java +++ b/src/main/java/dev/pluginz/graveplugin/util/Grave.java @@ -1,28 +1,37 @@ package dev.pluginz.graveplugin.util; -import dev.pluginz.graveplugin.GravePlugin; import org.bukkit.Location; import org.bukkit.inventory.ItemStack; import java.util.UUID; public class Grave { - private final GravePlugin plugin; + private final String playerName; private final UUID graveId; private final Location location; private final ItemStack[] items; private final ItemStack[] armorItems; private final ItemStack offHand; private final UUID armorStandId; + private long activeTime; + private final long maxActiveTime; + private boolean expired; - public Grave(GravePlugin plugin, UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId) { - this.plugin = plugin; + public Grave(String playerName, UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId, long maxActiveTime, boolean expired) { + this.playerName = playerName; this.graveId = graveId; this.location = location; this.items = items; this.armorItems = armorItems; this.offHand = offHand; this.armorStandId = armorStandId; + this.activeTime = 0; + this.maxActiveTime = maxActiveTime; + this.expired = expired; + } + + public String getPlayerName() { + return playerName; } public UUID getGraveId() { @@ -48,4 +57,24 @@ public ItemStack getOffHand() { public UUID getArmorStandId() { return armorStandId; } -} + + public void incrementActiveTime(long time) { + this.activeTime += time; + } + + public long getActiveTime() { + return activeTime; + } + + public long getMaxActiveTime() { + return maxActiveTime; + } + + public boolean isExpired() { + return expired; + } + + public void setExpired(boolean expired) { + this.expired = expired; + } +} \ No newline at end of file From 7a349c74ea9316c90cadae4c5545a9dd4588ef28 Mon Sep 17 00:00:00 2001 From: TubYoub Date: Mon, 8 Jul 2024 15:01:56 +0200 Subject: [PATCH 3/8] Graves open only for the Player who it is from Changes: - Graves only open for the Player who it is from TODO: - Commands - Code Cleanup - Code Documentation - more probably coming Took 38 minutes --- .../java/dev/pluginz/graveplugin/listener/GraveListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java index 4a442da..6a7d019 100644 --- a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java +++ b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java @@ -22,6 +22,7 @@ import org.bukkit.block.Block; import org.bukkit.block.Skull; import java.util.Arrays; +import java.util.Objects; import java.util.UUID; public class GraveListener implements Listener { @@ -108,7 +109,7 @@ public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) { plugin.getLogger().warning(player.getName() + " clicked on " + entity.getType() + " with the UUID " + entity.getUniqueId()); Grave grave = graveManager.getGraveFromUUID(entity.getUniqueId()); - if (grave == null){ + if (grave == null || !Objects.equals(grave.getPlayerName(), player.getName())) { return; } UUID graveId = grave.getGraveId(); From c6266247aa5b31811f5ea82dac4f6cbd44258d1f Mon Sep 17 00:00:00 2001 From: TubYoub Date: Tue, 9 Jul 2024 14:59:01 +0200 Subject: [PATCH 4/8] Commands & bugs Changes: - Added info and reload command - added getPluginPrefix() to GravePlugin - added sendPluginMessages(..) to GravePlugin - removed double dropitems method - added VersionChecker - Plugin checks for a new Version on Server startup - before the Armor is beeing restored the Plugin now check if the item is null TODO: - Change information in handleInfo() - Code Cleanup - Code Documentation - more probably coming Took 2 hours 15 minutes --- .../dev/pluginz/graveplugin/GravePlugin.java | 36 +++++++ .../graveplugin/command/GraveCommand.java | 77 ++++++++++++++- .../graveplugin/listener/GraveListener.java | 6 +- .../graveplugin/manager/GraveManager.java | 31 +++--- .../graveplugin/util/VersionChecker.java | 96 +++++++++++++++++++ 5 files changed, 223 insertions(+), 23 deletions(-) create mode 100644 src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index ce4aa1d..8b49a19 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -4,16 +4,31 @@ import dev.pluginz.graveplugin.listener.GraveListener; import dev.pluginz.graveplugin.manager.ConfigManager; import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.VersionChecker; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; public class GravePlugin extends JavaPlugin { + private final String version = "1.0"; + private final String project = "qiyG0tnT"; + private GraveManager graveManager; private ConfigManager configManager; + private boolean newVersion; @Override public void onEnable() { configManager = new ConfigManager(this); configManager.loadConfig(); + + if (configManager.isCheckVersion()) { + newVersion = VersionChecker.isNewVersionAvailable(version, project); + if (newVersion) { + this.getLogger().warning("There is a new Version available for BT's CombatLogger"); + } + } + graveManager = new GraveManager(this); graveManager.loadGraves(); getServer().getPluginManager().registerEvents(new GraveListener(this), this); @@ -26,6 +41,27 @@ public void onDisable() { //configManager.saveConfig(); } + public String getPluginPrefix() { + return ChatColor.WHITE + "[" + ChatColor.DARK_GRAY + "BTG" + ChatColor.WHITE + "] "; + } + + public void sendPluginMessages(CommandSender sender, String type) { + if ("title".equals(type)) { + sender.sendMessage(ChatColor.BLACK + "◢◤" + ChatColor.DARK_GRAY + "BT" + ChatColor.BLACK + "'"+ ChatColor.DARK_GRAY + "s" + ChatColor.DARK_PURPLE + " CombatLogger" + ChatColor.BLACK + "◥◣"); + } else if ("line".equals(type)) { + sender.sendMessage(ChatColor.BLACK + "-" + ChatColor.DARK_GRAY + "-" + ChatColor.GRAY + "-" + ChatColor.YELLOW + "-" + ChatColor.LIGHT_PURPLE + "-" + ChatColor.DARK_PURPLE + "-" + + ChatColor.BLACK + "-" + ChatColor.DARK_GRAY + "-" + ChatColor.GRAY + "-" + ChatColor.YELLOW + "-" + ChatColor.LIGHT_PURPLE + "-" + ChatColor.DARK_PURPLE + "-" + + ChatColor.BLACK + "-" + ChatColor.DARK_GRAY + "-" + ChatColor.GRAY + "-" + ChatColor.YELLOW + "-" + ChatColor.LIGHT_PURPLE + "-" + ChatColor.DARK_PURPLE + "-" + + ChatColor.BLACK + "-"); + } + } + + public String getVersion(){ + return version; + } + public boolean isNewVersion(){ + return newVersion; + } public GraveManager getGraveManager() { return graveManager; } diff --git a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java index a549b00..cba9431 100644 --- a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java +++ b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java @@ -1,22 +1,93 @@ package dev.pluginz.graveplugin.command; - import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +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.entity.Player; + +import java.util.List; +import java.util.Map; +import java.util.UUID; public class GraveCommand implements CommandExecutor { private final GravePlugin plugin; + private final GraveManager graveManager; + private final String prefix; public GraveCommand(GravePlugin plugin) { this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + this.prefix = plugin.getPluginPrefix(); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - //TODO: I guess, everything? + if (args.length == 0) { + sendHelp(sender); + return true; + } + + switch (args[0].toLowerCase()) { + case "info": + handleInfo(sender, args); + return true; + case "reload": + return handleReload(sender); + default: + sendHelp(sender); + return true; + } + } + + private void handleInfo(CommandSender sender, String[] args) { + plugin.sendPluginMessages(sender, "title"); + sender.sendMessage(ChatColor.GREEN + "Author: BTPluginz"); + sender.sendMessage(ChatColor.GREEN + "Version: " + plugin.getVersion()); + if (plugin.getConfigManager().isCheckVersion()) { + if (plugin.isNewVersion()) { + sender.sendMessage(ChatColor.YELLOW + "A new version is available! Update at: " + ChatColor.UNDERLINE + "https://modrinth.com/project/bts-combatlogger"); + } else { + sender.sendMessage(ChatColor.GREEN + "You are using the latest version!"); + } + } else { + sender.sendMessage(ChatColor.GOLD + "Version checking is disabled!"); + } + TextComponent githubLink = new TextComponent(ChatColor.DARK_GRAY + "" + ChatColor.UNDERLINE + "GitHub"); + githubLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/BT-Pluginz/CombatLogger")); + githubLink.setUnderlined(true); + sender.spigot().sendMessage(githubLink); + + TextComponent discordLink = new TextComponent(ChatColor.BLUE + "" + ChatColor.UNDERLINE + "Discord"); + discordLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://discord.pluginz.dev")); + discordLink.setUnderlined(true); + sender.spigot().sendMessage(discordLink); + + sender.sendMessage("If you have any issues please report them on GitHub or on the Discord."); + plugin.sendPluginMessages(sender, "line"); + } + + public boolean handleReload(CommandSender sender) { + if (sender.hasPermission("graveplugin.reload")) { + plugin.getConfigManager().reloadConfig(); + sender.sendMessage(prefix + "The config reloaded."); + } else { + sender.sendMessage(prefix + ChatColor.RED + "You do not have permission to reload the config."); + } + return true; + } - return false; + private void sendHelp(CommandSender sender) { + sender.sendMessage(prefix + "Grave Plugin Commands:"); + sender.sendMessage(prefix + "/grave info - Show plugin information"); + sender.sendMessage(prefix + "/grave list - List all active graves"); + sender.sendMessage(prefix + "/grave list - List graves for a specific player"); } } diff --git a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java index 6a7d019..35b45a7 100644 --- a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java +++ b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java @@ -9,6 +9,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryDragEvent; @@ -132,13 +134,15 @@ public void onInventoryClick(InventoryClickEvent event) { if (event.getCurrentItem() != null) { Material type = event.getCurrentItem().getType(); UUID graveId = graveManager.getGraveIdFromInventory(inventory); + Grave grave = graveManager.getGraveFromGraveID(graveId); if (graveId != null) { if (type == Material.GREEN_STAINED_GLASS_PANE) { player.closeInventory(); graveManager.restoreInventory(player, inventory, graveId); } else if (type == Material.RED_STAINED_GLASS_PANE) { player.closeInventory(); - graveManager.dropGraveItems(inventory, player, graveId); + graveManager.dropGraveItems(grave); + graveManager.removeGrave(graveId); } } } diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java index 1496743..e0930f7 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; public class GraveManager { private static GravePlugin plugin = null; @@ -38,7 +39,6 @@ public GraveManager(GravePlugin plugin) { this.startGraveTimeoutTask(); } - public UUID createGrave(Player player, Location location, ItemStack[] items, ItemStack[] armor, ItemStack offHand) { double x = location.getX() >= 0 ? 0.5 : -0.5; double y = -1.0; @@ -89,8 +89,6 @@ public static String getColoredTime(Grave grave) { return color + time; } - - public void openGrave(Player player, UUID graveId) { Grave grave = graves.get(graveId); if (grave == null || grave.isExpired()){ @@ -152,6 +150,9 @@ public void run() { PlayerInventory inv = player.getInventory(); for (int i = 0; i < armorItems.length; i++) { ItemStack item = armorItems[i]; + if (item == null || item.getType() == Material.AIR) { + continue; + } switch (i) { case 0: if (inv.getHelmet() == null) { @@ -215,20 +216,7 @@ public void run() { }.runTask(plugin); } - - public void dropGraveItems(Inventory inventory, Player player, UUID graveId) { - Location location = player.getLocation(); - for (int i = 0; i < 45; i++) { - ItemStack item = inventory.getItem(i); - if (item != null && item.getType() != Material.AIR) { - location.getWorld().dropItemNaturally(location, item); - } - } - removeGrave(graveId); - inventoryGraveMap.remove(inventory); - } - - private void dropGraveItemsAtLocation(Grave grave) { + public void dropGraveItems(Grave grave) { World world = grave.getLocation().getWorld(); if (world == null) return; @@ -261,6 +249,7 @@ public void removeGrave(UUID graveId) { plugin.getLogger().info("Removed grave with UUID: " + graveId); } } + private ItemStack createGlassPane(Material material, String name) { ItemStack pane = new ItemStack(material); ItemMeta meta = pane.getItemMeta(); @@ -271,7 +260,6 @@ private ItemStack createGlassPane(Material material, String name) { return pane; } - public UUID getGraveIdFromInventory(Inventory inventory) { return inventoryGraveMap.get(inventory); } @@ -284,6 +272,7 @@ public Grave getGraveFromUUID(UUID armorStandUUID) { } return null; } + public UUID getArmorStandIdFromGraveId(UUID graveId) { Grave grave = graves.get(graveId); if (grave != null) { @@ -305,6 +294,10 @@ public UUID getGraveIdFromArmorStand(UUID armorStandId) { return null; } + public Grave getGraveFromGraveID(UUID graveId) { + return graves.get(graveId); + } + public Map getGraves() { return graves; } @@ -329,7 +322,7 @@ private void startGraveTimeoutTask() { if (grave.isExpired()) { if (grave.getLocation().getWorld().isChunkLoaded(grave.getLocation().getBlockX() >> 4, grave.getLocation().getBlockZ() >> 4)) { if (isPlayerNearby(grave.getLocation(), 50)) { - this.dropGraveItemsAtLocation(grave); + this.dropGraveItems(grave); this.removeGrave(grave.getGraveId()); iterator.remove(); } diff --git a/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java b/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java new file mode 100644 index 0000000..2d7cfa6 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java @@ -0,0 +1,96 @@ +/* + * This file is part of BT's GravePlugin, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.pluginz.graveplugin.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Class responsible for checking if a new version of the plugin is available. + */ +public class VersionChecker { + public VersionChecker(){} + + /** + * Checks if a new version of the plugin is available. + * @param version The current version of the plugin. + * @return A boolean indicating whether a new version is available. + */ + public static boolean isNewVersionAvailable(String version, String project) { + try { + URL url = new URL("https://api.modrinth.com/v2/project/" + project + "/version"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setRequestProperty("User-Agent", "BTPluginz/GravePlugin/"+ version ); + + int responseCode = connection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String jsonResponse = reader.lines().collect(Collectors.joining("\n")); + + List> versions = parseJsonArray(jsonResponse); + if (!versions.isEmpty()) { + String latestVersion = (String) versions.get(0).get("version_number"); + + String currentVersion = version; + return !latestVersion.equals(currentVersion); + } else { + return false; + } + } + } else { + return false; + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + /** + * Parses a JSON array into a list of maps. + * @param jsonArray The JSON array to be parsed. + * @return A list of maps representing the JSON array. + */ + private static List> parseJsonArray(String jsonArray) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.readValue(jsonArray, new TypeReference>>(){}); + } catch (IOException e) { + e.printStackTrace(); + return Arrays.asList(); // Handle parsing exception + } + } +} \ No newline at end of file From 36007ee7cdd5c7b556d9a67ba34c63e268b94c66 Mon Sep 17 00:00:00 2001 From: TubYoub Date: Fri, 12 Jul 2024 04:01:36 +0200 Subject: [PATCH 5/8] Grave cant be destroyed, Level, Cleanup, License Changes: - Added XP to the Grave - Added How much % of the Players Lvl should be kept by the Grave - Added License tp every File - GraveManager got split up into multiple Managers which all got their purpose - GraveListener got also split up - The Player Head cant be destroyed by any Liquid, explosions or pistons - You can configure small armor stands in the config now - info for Info Command is updated - on plugin initializing the Plugin prints a Logo plus its Version - Changed Plugin Name from BT's GravePlugin to BT Graves TODO: - Tab Completion - Code Documentation - more probably coming Took 9 hours 14 minutes --- pom.xml | 2 +- .../dev/pluginz/graveplugin/GravePlugin.java | 80 +++- .../graveplugin/command/GraveCommand.java | 40 +- .../listener/BlockBreakListener.java | 63 +++ .../listener/BlockExplodeListener.java | 85 ++++ .../listener/BlockPistonExtendListener.java | 72 +++ .../listener/BlockPlaceListener.java | 58 +++ .../graveplugin/listener/GraveListener.java | 158 ------- .../listener/InventoryListener.java | 84 ++++ .../listener/LiquidFlowListener.java | 62 +++ .../listener/PlayerDeathListener.java | 160 +++++++ .../listener/PlayerInteractListener.java | 67 +++ .../graveplugin/manager/ConfigManager.java | 26 +- .../manager/GraveInventoryManager.java | 274 +++++++++++ .../graveplugin/manager/GraveManager.java | 442 ++---------------- .../manager/GravePersistenceManager.java | 192 ++++++++ .../manager/GraveTimeoutManager.java | 135 ++++++ .../dev/pluginz/graveplugin/util/Grave.java | 39 +- .../graveplugin/util/VersionChecker.java | 5 +- src/main/resources/config.yml | 15 +- src/main/resources/plugin.yml | 7 +- 21 files changed, 1478 insertions(+), 588 deletions(-) create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/BlockBreakListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/BlockExplodeListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/BlockPistonExtendListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/BlockPlaceListener.java delete mode 100644 src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/InventoryListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/LiquidFlowListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/PlayerDeathListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/listener/PlayerInteractListener.java create mode 100644 src/main/java/dev/pluginz/graveplugin/manager/GraveInventoryManager.java create mode 100644 src/main/java/dev/pluginz/graveplugin/manager/GravePersistenceManager.java create mode 100644 src/main/java/dev/pluginz/graveplugin/manager/GraveTimeoutManager.java diff --git a/pom.xml b/pom.xml index 8dc3e64..d552540 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 1.0 jar - BT's GravePlugin + BT Graves 1.8 diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index 8b49a19..a1ccd60 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -1,24 +1,58 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.pluginz.graveplugin; import dev.pluginz.graveplugin.command.GraveCommand; -import dev.pluginz.graveplugin.listener.GraveListener; -import dev.pluginz.graveplugin.manager.ConfigManager; -import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.listener.*; +import dev.pluginz.graveplugin.manager.*; import dev.pluginz.graveplugin.util.VersionChecker; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; public class GravePlugin extends JavaPlugin { private final String version = "1.0"; - private final String project = "qiyG0tnT"; + private final String project = "WGgaXko0"; private GraveManager graveManager; + private GraveInventoryManager graveInventoryManager; + private GravePersistenceManager gravePersistenceManager; + private GraveTimeoutManager graveTimeoutManager; private ConfigManager configManager; private boolean newVersion; @Override public void onEnable() { + this.getLogger().info("_____________________________ "); + this.getLogger().info("\\______ \\__ ___/ _____/ "); + this.getLogger().info(" | | _/ | | / \\ ___ "); + this.getLogger().info(" | | \\ | | \\ \\_\\ \\"); + this.getLogger().info(" |______ / |____| \\______ /" + " BT Graves v" + version); + this.getLogger().info(" \\/ \\/ " + " Running on " + Bukkit.getServer().getName() + " using Blackmagic"); + configManager = new ConfigManager(this); configManager.loadConfig(); @@ -30,15 +64,33 @@ public void onEnable() { } graveManager = new GraveManager(this); - graveManager.loadGraves(); - getServer().getPluginManager().registerEvents(new GraveListener(this), this); + graveInventoryManager = new GraveInventoryManager(this); + gravePersistenceManager = new GravePersistenceManager(this); + graveTimeoutManager = new GraveTimeoutManager(this); + + graveManager.setPersistenceManager(gravePersistenceManager); + gravePersistenceManager.loadGraves(); + graveTimeoutManager.startGraveTimeoutTask(); + getServer().getPluginManager().registerEvents(new BlockBreakListener(this), this); + getServer().getPluginManager().registerEvents(new BlockExplodeListener(this), this); + getServer().getPluginManager().registerEvents(new BlockPistonExtendListener(this), this); + getServer().getPluginManager().registerEvents(new InventoryListener(this), this); + getServer().getPluginManager().registerEvents(new LiquidFlowListener(this), this); + getServer().getPluginManager().registerEvents(new PlayerDeathListener(this), this); + getServer().getPluginManager().registerEvents(new PlayerInteractListener(this), this); + getServer().getPluginManager().registerEvents(new BlockPlaceListener(this), this); getCommand("grave").setExecutor(new GraveCommand(this)); } @Override public void onDisable() { - graveManager.saveGraves(); - //configManager.saveConfig(); + gravePersistenceManager.saveGraves(); + configManager.saveConfig(); + this.getLogger().info("-----"); + this.getLogger().info(" "); + this.getLogger().warning("If this is a reload please note that this could break the Plugin"); + this.getLogger().info(" "); + this.getLogger().info("-----"); } public String getPluginPrefix() { @@ -47,7 +99,7 @@ public String getPluginPrefix() { public void sendPluginMessages(CommandSender sender, String type) { if ("title".equals(type)) { - sender.sendMessage(ChatColor.BLACK + "◢◤" + ChatColor.DARK_GRAY + "BT" + ChatColor.BLACK + "'"+ ChatColor.DARK_GRAY + "s" + ChatColor.DARK_PURPLE + " CombatLogger" + ChatColor.BLACK + "◥◣"); + sender.sendMessage(ChatColor.BLACK + "◢◤" + ChatColor.DARK_GRAY + "BT" + ChatColor.BLACK + "'"+ ChatColor.DARK_GRAY + "s" + ChatColor.DARK_PURPLE + " Graves" + ChatColor.BLACK + "◥◣"); } else if ("line".equals(type)) { sender.sendMessage(ChatColor.BLACK + "-" + ChatColor.DARK_GRAY + "-" + ChatColor.GRAY + "-" + ChatColor.YELLOW + "-" + ChatColor.LIGHT_PURPLE + "-" + ChatColor.DARK_PURPLE + "-" + ChatColor.BLACK + "-" + ChatColor.DARK_GRAY + "-" + ChatColor.GRAY + "-" + ChatColor.YELLOW + "-" + ChatColor.LIGHT_PURPLE + "-" + ChatColor.DARK_PURPLE + "-" @@ -68,4 +120,14 @@ public GraveManager getGraveManager() { public ConfigManager getConfigManager(){ return configManager; } + + public GraveInventoryManager getGraveInventoryManager() { + return graveInventoryManager; + } + public GravePersistenceManager getGravePersistenceManager() { + return gravePersistenceManager; + } + public GraveTimeoutManager getGraveTimeoutManager() { + return graveTimeoutManager; + } } \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java index cba9431..143deee 100644 --- a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java +++ b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java @@ -1,21 +1,37 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.pluginz.graveplugin.command; import dev.pluginz.graveplugin.GravePlugin; import dev.pluginz.graveplugin.manager.GraveManager; -import dev.pluginz.graveplugin.util.Grave; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Bukkit; 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.entity.Player; - -import java.util.List; -import java.util.Map; -import java.util.UUID; public class GraveCommand implements CommandExecutor { private final GravePlugin plugin; @@ -53,7 +69,7 @@ private void handleInfo(CommandSender sender, String[] args) { sender.sendMessage(ChatColor.GREEN + "Version: " + plugin.getVersion()); if (plugin.getConfigManager().isCheckVersion()) { if (plugin.isNewVersion()) { - sender.sendMessage(ChatColor.YELLOW + "A new version is available! Update at: " + ChatColor.UNDERLINE + "https://modrinth.com/project/bts-combatlogger"); + sender.sendMessage(ChatColor.YELLOW + "A new version is available! Update at: " + ChatColor.UNDERLINE + "https://modrinth.com/project/bts-graves"); } else { sender.sendMessage(ChatColor.GREEN + "You are using the latest version!"); } @@ -61,7 +77,7 @@ private void handleInfo(CommandSender sender, String[] args) { sender.sendMessage(ChatColor.GOLD + "Version checking is disabled!"); } TextComponent githubLink = new TextComponent(ChatColor.DARK_GRAY + "" + ChatColor.UNDERLINE + "GitHub"); - githubLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/BT-Pluginz/CombatLogger")); + githubLink.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/BT-Pluginz/GravePlugin")); githubLink.setUnderlined(true); sender.spigot().sendMessage(githubLink); @@ -70,12 +86,12 @@ private void handleInfo(CommandSender sender, String[] args) { discordLink.setUnderlined(true); sender.spigot().sendMessage(discordLink); - sender.sendMessage("If you have any issues please report them on GitHub or on the Discord."); + sender.sendMessage("If you have any issues please report them on GitHub or on our the Discord."); plugin.sendPluginMessages(sender, "line"); } public boolean handleReload(CommandSender sender) { - if (sender.hasPermission("graveplugin.reload")) { + if (sender.hasPermission("btgraves.reload")) { plugin.getConfigManager().reloadConfig(); sender.sendMessage(prefix + "The config reloaded."); } else { diff --git a/src/main/java/dev/pluginz/graveplugin/listener/BlockBreakListener.java b/src/main/java/dev/pluginz/graveplugin/listener/BlockBreakListener.java new file mode 100644 index 0000000..edd1ee5 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/BlockBreakListener.java @@ -0,0 +1,63 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; + +public class BlockBreakListener implements Listener { + private final GravePlugin plugin; + private final GraveManager graveManager; + + public BlockBreakListener(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + Block block = event.getBlock(); + if (block.getType() == Material.PLAYER_HEAD || block.getType() == Material.PLAYER_WALL_HEAD) { + Location blockLocation = new Location(block.getWorld(), block.getX(), block.getY(), block.getZ()); + for (Grave grave : graveManager.getGraves().values()) { + if (grave.getLocation().getBlockX() == blockLocation.getBlockX() && + grave.getLocation().getBlockY() == blockLocation.getBlockY() && + grave.getLocation().getBlockZ() == blockLocation.getBlockZ()) { + event.setCancelled(true); + event.getPlayer().sendMessage(plugin.getPluginPrefix() + "You cannot break a grave's player head."); + break; + } + } + } + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/listener/BlockExplodeListener.java b/src/main/java/dev/pluginz/graveplugin/listener/BlockExplodeListener.java new file mode 100644 index 0000000..866d4eb --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/BlockExplodeListener.java @@ -0,0 +1,85 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.Location; +import org.bukkit.event.entity.EntityExplodeEvent; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class BlockExplodeListener implements Listener { + private final GravePlugin plugin; + private final GraveManager graveManager; + + public BlockExplodeListener(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockExplode(EntityExplodeEvent event) { + // Temporarily store locations of grave blocks to restore them later + List graveBlocksToRestore = new ArrayList<>(); + Iterator blockIterator = event.blockList().iterator(); + while (blockIterator.hasNext()) { + Block block = blockIterator.next(); + if (isGraveBlock(block.getLocation())) { + graveBlocksToRestore.add(block.getLocation()); + blockIterator.remove(); // Remove the grave block from the list to be exploded + } + } + + // Schedule a task to restore grave blocks after the explosion + Bukkit.getScheduler().runTaskLater(plugin, () -> { + for (Location loc : graveBlocksToRestore) { + if (loc.getBlock().getType() == Material.AIR) { + loc.getBlock().setType(Material.PLAYER_HEAD); + } + } + }, 1L); // Delay of 1 tick to allow the explosion to process + } + + private boolean isGraveBlock(Location location) { + for (Grave grave : graveManager.getGraves().values()) { + if (grave.getLocation().getBlockX() == location.getBlockX() && + grave.getLocation().getBlockY() == location.getBlockY() && + grave.getLocation().getBlockZ() == location.getBlockZ()) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/listener/BlockPistonExtendListener.java b/src/main/java/dev/pluginz/graveplugin/listener/BlockPistonExtendListener.java new file mode 100644 index 0000000..9e769a1 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/BlockPistonExtendListener.java @@ -0,0 +1,72 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPistonExtendEvent; +import dev.pluginz.graveplugin.GravePlugin; + +import java.util.List; + +public class BlockPistonExtendListener implements Listener { + + private final GraveManager graveManager; + + public BlockPistonExtendListener(GravePlugin plugin) { + this.graveManager = plugin.getGraveManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + List blocks = event.getBlocks(); + for (Block block : blocks) { + if (block.getType() == Material.PLAYER_HEAD || block.getType() == Material.PLAYER_WALL_HEAD) { + if (isGraveHead(block)) { + event.setCancelled(true); + return; + } + } + } + } + + private boolean isGraveHead(Block block) { + Location location = block.getLocation(); + for (Grave grave : graveManager.getGraves().values()) { + if (grave.getLocation().getBlockX() == location.getBlockX() && + grave.getLocation().getBlockY() == location.getBlockY() && + grave.getLocation().getBlockZ() == location.getBlockZ()) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/listener/BlockPlaceListener.java b/src/main/java/dev/pluginz/graveplugin/listener/BlockPlaceListener.java new file mode 100644 index 0000000..2a1707a --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/BlockPlaceListener.java @@ -0,0 +1,58 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerBucketEmptyEvent; + +public class BlockPlaceListener implements Listener { + + private final GraveManager graveManager; + + public BlockPlaceListener(GravePlugin plugin) { + this.graveManager = plugin.getGraveManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { + Block clickedBlock = event.getBlockClicked(); + Material bucketContent = event.getBucket(); + for (Grave grave : graveManager.getGraves().values()) { + if (grave.getLocation().getBlockX() == clickedBlock.getLocation().getBlockX() && + grave.getLocation().getBlockY() == clickedBlock.getLocation().getBlockY() + 1 && + grave.getLocation().getBlockZ() == clickedBlock.getLocation().getBlockZ()) { + event.setCancelled(true); + return; + } + } + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java b/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java deleted file mode 100644 index 35b45a7..0000000 --- a/src/main/java/dev/pluginz/graveplugin/listener/GraveListener.java +++ /dev/null @@ -1,158 +0,0 @@ -package dev.pluginz.graveplugin.listener; - -import dev.pluginz.graveplugin.GravePlugin; -import dev.pluginz.graveplugin.manager.GraveManager; -import dev.pluginz.graveplugin.util.Grave; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.player.PlayerInteractAtEntityEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; -import org.bukkit.inventory.ItemStack; -import org.bukkit.GameRule; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Skull; -import java.util.Arrays; -import java.util.Objects; -import java.util.UUID; - -public class GraveListener implements Listener { - - private final GravePlugin plugin; - private final GraveManager graveManager; - - public GraveListener(GravePlugin plugin) { - this.plugin = plugin; - this.graveManager = plugin.getGraveManager(); - } - - @EventHandler - public void onPlayerDeath(PlayerDeathEvent event) { - Player player = event.getEntity(); - if (player.getWorld().getGameRuleValue(GameRule.KEEP_INVENTORY)) { - return; - } - - ItemStack[] items = player.getInventory().getContents(); - boolean hasItems = Arrays.stream(items).anyMatch(item -> item != null && item.getType() != Material.AIR); - ItemStack[] armor = player.getInventory().getArmorContents(); - ItemStack offHand = player.getInventory().getItemInOffHand(); - - if (!hasItems) { - return; - } - - Location location = player.getLocation(); - plugin.getLogger().warning("" + location); - location = getGroundLocation(location); - plugin.getLogger().warning("" + location); - - event.getDrops().clear(); - UUID graveId = graveManager.createGrave(player, location, items, armor, offHand); - placePlayerHead(player, location); - graveManager.openGrave(player, graveId); - } - - @EventHandler - public void onBlockBreak(BlockBreakEvent event) { - Block block = event.getBlock(); - if (block.getType() == Material.PLAYER_HEAD || block.getType() == Material.PLAYER_WALL_HEAD) { - Location blockLocation = new Location(block.getWorld(), block.getX(), block.getY(), block.getZ()); - for (Grave grave : graveManager.getGraves().values()) { - if (grave.getLocation().getBlockX() == blockLocation.getBlockX() && - grave.getLocation().getBlockY() == blockLocation.getBlockY() && - grave.getLocation().getBlockZ() == blockLocation.getBlockZ()) { - event.setCancelled(true); - event.getPlayer().sendMessage("You cannot break a grave's player head."); - break; - } - } - } - } - - private Location getGroundLocation(Location location) { - while (location.getBlock().getType() == Material.LAVA || location.getBlock().getType() == Material.WATER) { - location.add(0, 1, 0); - } - while (location.getBlock().getType() == Material.AIR || location.getBlock().getType() == Material.GRASS || location.getBlock().getType() == Material.TALL_GRASS) { - location.subtract(0, 1, 0); - } - - location.setX(location.getBlockX() >= 0 ? Math.round(location.getBlockX()) : Math.floor(location.getBlockX()) + 1); - location.setY(Math.round(location.getBlockY()) + 2); - location.setZ(location.getBlockZ() >= 0 ? Math.round(location.getBlockZ()) : Math.floor(location.getBlockZ()) + 1); - return location; - } - - private void placePlayerHead(Player player, Location location) { - Block block = location.getBlock(); - block.setType(Material.PLAYER_HEAD); - - Skull skull = (Skull) block.getState(); - skull.setOwningPlayer(player); - skull.update(); - } - - @EventHandler - public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) { - Player player = event.getPlayer(); - Entity entity = event.getRightClicked(); - - plugin.getLogger().warning(player.getName() + " clicked on " + entity.getType() + " with the UUID " + entity.getUniqueId()); - Grave grave = graveManager.getGraveFromUUID(entity.getUniqueId()); - if (grave == null || !Objects.equals(grave.getPlayerName(), player.getName())) { - return; - } - UUID graveId = grave.getGraveId(); - - if (entity.getType() == EntityType.ARMOR_STAND && graveManager.isGraveArmorStand(graveId)) { - event.setCancelled(true); - graveManager.openGrave(player, graveId); - } - } - - @EventHandler - public void onInventoryClick(InventoryClickEvent event) { - if (!(event.getWhoClicked() instanceof Player)) return; - Player player = (Player) event.getWhoClicked(); - Inventory inventory = event.getInventory(); - InventoryView inventoryView = event.getView(); - - if (graveManager.isGraveInventory(inventoryView)) { - event.setCancelled(true); - if (event.getCurrentItem() != null) { - Material type = event.getCurrentItem().getType(); - UUID graveId = graveManager.getGraveIdFromInventory(inventory); - Grave grave = graveManager.getGraveFromGraveID(graveId); - if (graveId != null) { - if (type == Material.GREEN_STAINED_GLASS_PANE) { - player.closeInventory(); - graveManager.restoreInventory(player, inventory, graveId); - } else if (type == Material.RED_STAINED_GLASS_PANE) { - player.closeInventory(); - graveManager.dropGraveItems(grave); - graveManager.removeGrave(graveId); - } - } - } - } - } - - @EventHandler - public void onInventoryDrag(InventoryDragEvent event) { - if (graveManager.isGraveInventory(event.getView())) { - event.setCancelled(true); - } - } -} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/listener/InventoryListener.java b/src/main/java/dev/pluginz/graveplugin/listener/InventoryListener.java new file mode 100644 index 0000000..94fe955 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/InventoryListener.java @@ -0,0 +1,84 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveInventoryManager; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import java.util.UUID; + +public class InventoryListener implements Listener { + + private final GraveManager graveManager; + private final GraveInventoryManager graveInventoryManager; + + public InventoryListener(GravePlugin plugin) { + this.graveManager = plugin.getGraveManager(); + this.graveInventoryManager = plugin.getGraveInventoryManager(); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) return; + Player player = (Player) event.getWhoClicked(); + Inventory inventory = event.getInventory(); + InventoryView inventoryView = event.getView(); + + if (graveInventoryManager.isGraveInventory(inventoryView)) { + event.setCancelled(true); + if (event.getCurrentItem() != null) { + Material type = event.getCurrentItem().getType(); + UUID graveId = graveInventoryManager.getGraveIdFromInventory(inventory); + Grave grave = graveManager.getGraveFromGraveID(graveId); + if (graveId != null) { + if (type == Material.GREEN_STAINED_GLASS_PANE) { + player.closeInventory(); + graveInventoryManager.restoreInventory(player, inventory, graveId); + } else if (type == Material.RED_STAINED_GLASS_PANE) { + player.closeInventory(); + graveInventoryManager.dropGraveItems(grave); + graveManager.removeGrave(graveId); + } + } + } + } + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + if (graveInventoryManager.isGraveInventory(event.getView())) { + event.setCancelled(true); + } + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/listener/LiquidFlowListener.java b/src/main/java/dev/pluginz/graveplugin/listener/LiquidFlowListener.java new file mode 100644 index 0000000..ce06f03 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/LiquidFlowListener.java @@ -0,0 +1,62 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.Location; + +public class LiquidFlowListener implements Listener { + private final GraveManager graveManager; + + public LiquidFlowListener(GravePlugin plugin) { + this.graveManager = plugin.getGraveManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onLiquidFlow(BlockFromToEvent event) { + Block toBlock = event.getToBlock(); + if (isGraveBlock(toBlock.getLocation())) { + event.setCancelled(true); + } + } + + private boolean isGraveBlock(Location location) { + for (Grave grave : graveManager.getGraves().values()) { + if (grave.getLocation().getBlockX() == location.getBlockX() && + grave.getLocation().getBlockY() == location.getBlockY() && + grave.getLocation().getBlockZ() == location.getBlockZ()) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/listener/PlayerDeathListener.java b/src/main/java/dev/pluginz/graveplugin/listener/PlayerDeathListener.java new file mode 100644 index 0000000..113c03a --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/PlayerDeathListener.java @@ -0,0 +1,160 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveInventoryManager; +import dev.pluginz.graveplugin.manager.GraveManager; +import org.bukkit.GameRule; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Skull; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.UUID; + +public class PlayerDeathListener implements Listener { + private final GravePlugin plugin; + private final GraveManager graveManager; + private final GraveInventoryManager graveInventoryManager; + + public PlayerDeathListener(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + this.graveInventoryManager = plugin.getGraveInventoryManager(); + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + if (player.getWorld().getGameRuleValue(GameRule.KEEP_INVENTORY)) { + return; + } + + ItemStack[] items = player.getInventory().getContents(); + boolean hasItems = Arrays.stream(items).anyMatch(item -> item != null && item.getType() != Material.AIR); + ItemStack[] armor = player.getInventory().getArmorContents(); + ItemStack offHand = player.getInventory().getItemInOffHand(); + int lvl = (int) (player.getLevel() * ((double) plugin.getConfigManager().getExpPercentage() / 100)); + float xp = player.getExp(); + + if (!hasItems) { + return; + } + + Location location = player.getLocation(); + location = getGroundLocation(location); + + event.getDrops().clear(); + event.setDroppedExp(0); + + UUID graveId = graveManager.createGrave(player, location, items, armor, offHand, lvl, xp); + placePlayerHead(player, location); + graveInventoryManager.openGrave(player, graveId); + } + + private Location getGroundLocation(Location location) { + World world = location.getWorld(); + int maxSearchDistance = 10; // Adjust this value as needed + Location original = location.clone(); + boolean isNether = world.getEnvironment() == World.Environment.NETHER; + int netherRoofY = isNether ? 127 : Integer.MAX_VALUE; // Nether roof is at Y=127 + + // First, check the original location + if (isSuitableLocation(original)) { + return adjustLocation(original); + } + + // Search downwards first + for (int y = 1; y <= maxSearchDistance; y++) { + Location check = original.clone().subtract(0, y, 0); + if (isSuitableLocation(check) && (check.getY() <= netherRoofY || original.getY() > netherRoofY)) { + return adjustLocation(check); + } + } + + // If not found, search upwards + for (int y = 1; y <= maxSearchDistance; y++) { + Location check = original.clone().add(0, y, 0); + if (isSuitableLocation(check) && (check.getY() <= netherRoofY || original.getY() > netherRoofY)) { + return adjustLocation(check); + } + } + + // If no suitable location found, return the original location + Location finalLocation = adjustLocation(original); + + // Check for liquids and move up if necessary + while (finalLocation.getBlock().getType() == Material.LAVA || + finalLocation.getBlock().getType() == Material.WATER || + finalLocation.getBlock().getType() == Material.BUBBLE_COLUMN) { + finalLocation.add(0, 1, 0); + // Prevent moving above Nether roof if death was below it + if (isNether && finalLocation.getY() > netherRoofY && original.getY() <= netherRoofY) { + finalLocation.setY(netherRoofY); + break; + } + } + + return finalLocation; + } + + private boolean isSuitableLocation(Location location) { + Block block = location.getBlock(); + Block above = block.getRelative(BlockFace.UP); + Block below = block.getRelative(BlockFace.DOWN); + + return (block.getType() == Material.AIR || block.getType() == Material.CAVE_AIR) && + (above.getType() == Material.AIR || above.getType() == Material.CAVE_AIR) && + (below.getType().isSolid() || below.isLiquid()); + } + private Location adjustLocation(Location location) { + location.setX(location.getBlockX() >= 0 ? Math.round(location.getBlockX()) : Math.floor(location.getBlockX()) + 1); + location.setY(Math.round(location.getBlockY())); + location.setZ(location.getBlockZ() >= 0 ? Math.round(location.getBlockZ()) : Math.floor(location.getBlockZ()) + 1); + + double x = location.getX() >= 0 ? 0.5 : -0.5; + double z = location.getZ() >= 0 ? 0.5 : -0.5; + location.add(x, 0, z); + return location; + } + + private void placePlayerHead(Player player, Location location) { + Block block = location.getBlock(); + block.setType(Material.PLAYER_HEAD); + + Skull skull = (Skull) block.getState(); + skull.setOwningPlayer(player); + skull.update(); + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/listener/PlayerInteractListener.java b/src/main/java/dev/pluginz/graveplugin/listener/PlayerInteractListener.java new file mode 100644 index 0000000..770e6f2 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/listener/PlayerInteractListener.java @@ -0,0 +1,67 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.listener; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.manager.GraveInventoryManager; +import dev.pluginz.graveplugin.manager.GraveManager; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; + +import java.util.Objects; +import java.util.UUID; + +public class PlayerInteractListener implements Listener { + + private final GraveManager graveManager; + private final GraveInventoryManager graveInventoryManager; + + public PlayerInteractListener(GravePlugin plugin) { + this.graveManager = plugin.getGraveManager(); + this.graveInventoryManager = plugin.getGraveInventoryManager(); + } + + @EventHandler + public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) { + Player player = event.getPlayer(); + Entity entity = event.getRightClicked(); + + Grave grave = graveManager.getGraveFromUUID(entity.getUniqueId()); + if (grave == null || !Objects.equals(grave.getPlayerName(), player.getName())) { + return; + } + UUID graveId = grave.getGraveId(); + + if (entity.getType() == EntityType.ARMOR_STAND && graveManager.isGraveArmorStand(graveId)) { + event.setCancelled(true); + graveInventoryManager.openGrave(player, graveId); + } + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java b/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java index 4f6a722..05ac30f 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/ConfigManager.java @@ -1,5 +1,5 @@ /* - * This file is part of BT's GravePlugin, licensed under the MIT License. + * This file is part of BT's Graves, licensed under the MIT License. * * Copyright (c) BT Pluginz * @@ -39,7 +39,9 @@ public class ConfigManager { private YamlDocument config; private int graveTimeout; + private int expPercentage; private boolean checkVersion; + private boolean smallArmorStand; private final GravePlugin plugin; public ConfigManager(GravePlugin plugin) { @@ -57,10 +59,20 @@ public void loadConfig() { UpdaterSettings.builder().setVersioning(new BasicVersioning("fileversion")) .setOptionSorting(UpdaterSettings.OptionSorting.SORT_BY_DEFAULTS).build()); - graveTimeout = config.getInt("graveTimeout", 60); checkVersion = config.getBoolean("checkVersion", true); + graveTimeout = config.getInt("graveTimeout", 60); + smallArmorStand = config.getBoolean("smallArmorStand", true); + expPercentage = config.getInt("expPercentage", 100); + plugin.getLogger().warning(expPercentage + ""); + if (expPercentage < 0) { + expPercentage = 0; + config.set("expPercentage", 0); + } else if (expPercentage > 100) { + expPercentage = 100; + config.set("expPercentage", 100); + } + plugin.getLogger().warning(expPercentage + ""); } catch (IOException e) { - //plugin.getLogger().severe("Could not load configuration: " + e.getMessage()); plugin.getLogger().severe("Error while loading the configuration"); } } @@ -82,6 +94,14 @@ public void setGraveTimeout(int graveTimeout) { this.graveTimeout = graveTimeout; } + public boolean isSmallArmorStand() { + return smallArmorStand; + } + + public int getExpPercentage() { + return expPercentage; + } + public boolean isCheckVersion() { return checkVersion; } diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveInventoryManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveInventoryManager.java new file mode 100644 index 0000000..e3afe1b --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveInventoryManager.java @@ -0,0 +1,274 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.manager; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.*; +import org.bukkit.entity.ExperienceOrb; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class GraveInventoryManager { + private final GravePlugin plugin; + private GraveManager graveManager; + + private Map inventoryGraveMap; + + public GraveInventoryManager(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + this.inventoryGraveMap = new HashMap<>(); + } + + public void openGrave(Player player, UUID graveId) { + Grave grave = graveManager.getGraveFromGraveID(graveId); + if (grave == null || grave.isExpired()) { + return; + } + int inventorySize = 54; + + Inventory graveInventory = Bukkit.createInventory(null, inventorySize, "Grave of " + player.getName()); + + // Add Armor (First Row) + int startIndex = 0; + for (int i = 3; i >= 0; i--) { + ItemStack armor = grave.getArmorItems()[i]; + if (armor != null && armor.getType() != Material.AIR) { + graveInventory.setItem(startIndex++, armor); + } else startIndex++; + } + ItemStack offHand = grave.getOffHand(); + if (offHand != null && offHand.getType() != Material.AIR) { + graveInventory.setItem(8, offHand); + } + + // Add Inventory (Rows 2-4) + for (int i = 0; i < 27; i++) { + ItemStack item = grave.getItems()[i + 9]; + if (item != null && item.getType() != Material.AIR) { + graveInventory.setItem(i + 9, item); + } + } + + // Add Hotbar (Row 5) + for (int i = 0; i < 9; i++) { + ItemStack item = grave.getItems()[i]; + if (item != null && item.getType() != Material.AIR) { + graveInventory.setItem(i + 36, item); + } + } + + ItemStack greenPane = createGlassPane(Material.GREEN_STAINED_GLASS_PANE, "Restore Items"); + ItemStack redPane = createGlassPane(Material.RED_STAINED_GLASS_PANE, "Drop Items"); + + int expLevels = grave.getLvl(); + ItemStack expBottles; + expBottles = new ItemStack(Material.EXPERIENCE_BOTTLE, 1); + ItemMeta meta = expBottles.getItemMeta(); + meta.setDisplayName(ChatColor.GREEN + "Level: " + ChatColor.YELLOW + expLevels); + expBottles.setItemMeta(meta); + graveInventory.setItem(49, expBottles); + + + graveInventory.setItem(45, greenPane); + graveInventory.setItem(53, redPane); + + inventoryGraveMap.put(graveInventory, graveId); + + player.openInventory(graveInventory); + } + + public void restoreInventory(Player player, Inventory inventory, UUID graveId) { + new BukkitRunnable() { + @Override + public void run() { + ItemStack[] armorItems = { inventory.getItem(0), inventory.getItem(1), inventory.getItem(2), inventory.getItem(3), inventory.getItem(8) }; + PlayerInventory inv = player.getInventory(); + for (int i = 0; i < armorItems.length; i++) { + ItemStack item = armorItems[i]; + if (item == null || item.getType() == Material.AIR) { + continue; + } + switch (i) { + case 0: + if (inv.getHelmet() == null) { + inv.setHelmet(item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + break; + case 1: + if (inv.getChestplate() == null) { + inv.setChestplate(item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + break; + case 2: + if (inv.getLeggings() == null) { + inv.setLeggings(item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + break; + case 3: + if (inv.getBoots() == null) { + inv.setBoots(item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + break; + case 4: + if (inv.getItemInOffHand().getType() == Material.AIR) { + inv.setItemInOffHand(item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + break; + } + } + for (int i = 9; i < 45; i++) { + ItemStack item = inventory.getItem(i); + if (item != null && item.getType() != Material.AIR) { + if (i >= 36 && i < 45) { + if (player.getInventory().getItem(i - 36) == null) { + player.getInventory().setItem(i - 36, item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + } else { + if (player.getInventory().getItem(i) == null) { + player.getInventory().setItem(i, item); + } else { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + } + } + } + + Grave grave = graveManager.getGrave(graveId); + player.setLevel(player.getLevel() + grave.getLvl()); + player.setExp(player.getExp() + grave.getExp()); + + graveManager.removeGrave(graveId); + inventoryGraveMap.remove(inventory); + } + }.runTask(plugin); + } + + public void dropGraveItems(Grave grave) { + World world = grave.getLocation().getWorld(); + if (world == null) return; + + // Drop main inventory items + for (ItemStack item : grave.getItems()) { + if (item != null) { + world.dropItemNaturally(grave.getLocation(), item); + } + } + + // Calculate total XP + int totalXp = calculateTotalXp(grave.getLvl(), grave.getExp()); + + // Spawn a single XP orb with the total XP + world.spawn(grave.getLocation(), ExperienceOrb.class, experienceOrb -> experienceOrb.setExperience(totalXp)); + } + + private int calculateTotalXp(int level, float expProgress) { + int xp = 0; + + // XP for completed levels + for (int i = 0; i < level; i++) { + xp += getXpForLevel(i); + } + + // Add XP progress towards next level + xp += Math.round(expProgress * getXpForLevel(level)); + + return xp; + } + + private int getXpForLevel(int level) { + if (level <= 15) { + return 2 * level + 7; + } else if (level <= 30) { + return 5 * level - 38; + } else { + return 9 * level - 158; + } + } + + + private ItemStack createGlassPane(Material material, String name) { + ItemStack pane = new ItemStack(material); + ItemMeta meta = pane.getItemMeta(); + if (meta != null) { + meta.setDisplayName(name); + pane.setItemMeta(meta); + } + return pane; + } + + public int calculateLevelFromExperience(double totalExp) { + int level = 0; + while (true) { + int expForNextLevel; + if (level <= 15) { + expForNextLevel = 2 * level + 7; + } else if (level <= 30) { + expForNextLevel = 5 * level - 38; + } else { + expForNextLevel = 9 * level - 158; + } + + totalExp -= expForNextLevel; + + if (totalExp < 0) { + break; + } + + level++; + } + return level; + } + + public boolean isGraveInventory(InventoryView inventoryView) { + return inventoryView.getTitle().startsWith("Grave of "); + } + + public UUID getGraveIdFromInventory(Inventory inventory) { + return inventoryGraveMap.get(inventory); + } +} \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java index e0930f7..21f1579 100644 --- a/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveManager.java @@ -1,235 +1,82 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.pluginz.graveplugin.manager; import dev.pluginz.graveplugin.GravePlugin; import dev.pluginz.graveplugin.util.Grave; import org.bukkit.*; import org.bukkit.block.Block; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.ArmorStand; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.io.BukkitObjectInputStream; -import org.bukkit.util.io.BukkitObjectOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; public class GraveManager { - private static GravePlugin plugin = null; + private static GravePlugin plugin; + private static GravePersistenceManager gravePersistenceManager; private Map graves; - private Map inventoryGraveMap; - private long lastUpdateTime; + public GraveManager(GravePlugin plugin) { this.plugin = plugin; this.graves = new HashMap<>(); - this.inventoryGraveMap = new HashMap<>(); - this.startGraveTimeoutTask(); + } + public static void setPersistenceManager(GravePersistenceManager gravePersistenceManager) { + GraveManager.gravePersistenceManager = gravePersistenceManager; } - public UUID createGrave(Player player, Location location, ItemStack[] items, ItemStack[] armor, ItemStack offHand) { - double x = location.getX() >= 0 ? 0.5 : -0.5; - double y = -1.0; - double z = location.getZ() >= 0 ? 0.5 : -0.5; - location.add(x, y, z); + public UUID createGrave(Player player, Location location, ItemStack[] items, ItemStack[] armor, ItemStack offHand, int lvl, float xp) { ArmorStand armorStand = (ArmorStand) location.getWorld().spawnEntity(location, EntityType.ARMOR_STAND); armorStand.setVisible(false); armorStand.setGravity(false); armorStand.setCustomName(player.getName() + "'s Grave"); armorStand.setCustomNameVisible(true); armorStand.setInvulnerable(true); + if (plugin.getConfigManager().isSmallArmorStand()) { + armorStand.setSmall(true); + } UUID graveId = UUID.randomUUID(); UUID armorStandId = armorStand.getUniqueId(); - long maxActiveTime = plugin.getConfigManager().getGraveTimeout() * 60 * 1000; + long maxActiveTime = (long) plugin.getConfigManager().getGraveTimeout() * 60 * 1000; if(plugin.getConfigManager().getGraveTimeout() == -1) { maxActiveTime = -1; } - Grave grave = new Grave(player.getName(), graveId, location, items, armor, offHand, armorStandId, maxActiveTime, false); + Grave grave = new Grave(player.getName(), graveId, location, items, armor, offHand, armorStandId, maxActiveTime, false, lvl, xp); graves.put(graveId, grave); - plugin.getLogger().info("Created grave for player " + player.getName() + " at " + location); - saveGraves(); + gravePersistenceManager.saveGraves(); return graveId; } - public static String getColoredTime(Grave grave) { - long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); - int remainingSeconds = (int) (remainingTime / 1000); // Convert milliseconds to seconds - - int hours = remainingSeconds / 3600; - int minutes = (remainingSeconds % 3600) / 60; - int seconds = remainingSeconds % 60; - - String time = String.format("%02dh %02dm %02ds", hours, minutes, seconds); - - double ratio = (double) grave.getActiveTime() / grave.getMaxActiveTime(); - ChatColor color; - if (ratio < 0.33) { - color = ChatColor.GREEN; - } else if (ratio < 0.66) { - color = ChatColor.GOLD; - } else { - color = ChatColor.RED; - } - return color + time; - } - - public void openGrave(Player player, UUID graveId) { - Grave grave = graves.get(graveId); - if (grave == null || grave.isExpired()){ - return; - } - int inventorySize = 54; - - Inventory graveInventory = Bukkit.createInventory(null, inventorySize, "Grave of " + player.getName()); - - // Add Armor (First Row) - int startIndex = 0; - for (int i = 3; i >= 0; i--) { - ItemStack armor = grave.getArmorItems()[i]; - if (armor != null && armor.getType() != Material.AIR) { - graveInventory.setItem(startIndex++, armor); - } else startIndex++; - } - ItemStack offHand = grave.getOffHand(); - if (offHand != null && offHand.getType() != Material.AIR) { - graveInventory.setItem(8, offHand); - } - - // Add Inventory (Rows 2-4) - for (int i = 0; i < 27; i++) { - ItemStack item = grave.getItems()[i + 9]; - if (item != null && item.getType() != Material.AIR) { - graveInventory.setItem(i + 9, item); - } - } - - // Add Hotbar (Row 5) - for (int i = 0; i < 9; i++) { - ItemStack item = grave.getItems()[i]; - if (item != null && item.getType() != Material.AIR) { - graveInventory.setItem(i + 36, item); - } - } - - ItemStack greenPane = createGlassPane(Material.GREEN_STAINED_GLASS_PANE, "Restore Items"); - ItemStack redPane = createGlassPane(Material.RED_STAINED_GLASS_PANE, "Drop Items"); - - graveInventory.setItem(45, greenPane); - graveInventory.setItem(53, redPane); - - inventoryGraveMap.put(graveInventory, graveId); - - player.openInventory(graveInventory); - } - - public boolean isGraveInventory(InventoryView inventoryView) { - return inventoryView.getTitle().startsWith("Grave of "); - } - - public void restoreInventory(Player player, Inventory inventory, UUID graveId) { - new BukkitRunnable() { - @Override - public void run() { - ItemStack[] armorItems = { inventory.getItem(0), inventory.getItem(1), inventory.getItem(2), inventory.getItem(3), inventory.getItem(8) }; - PlayerInventory inv = player.getInventory(); - for (int i = 0; i < armorItems.length; i++) { - ItemStack item = armorItems[i]; - if (item == null || item.getType() == Material.AIR) { - continue; - } - switch (i) { - case 0: - if (inv.getHelmet() == null) { - inv.setHelmet(item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - break; - case 1: - if (inv.getChestplate() == null) { - inv.setChestplate(item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - break; - case 2: - if (inv.getLeggings() == null) { - inv.setLeggings(item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - break; - case 3: - if (inv.getBoots() == null) { - inv.setBoots(item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - break; - case 4: - if (inv.getItemInOffHand().getType() == Material.AIR) { - inv.setItemInOffHand(item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - break; - } - } - for (int i = 9; i < 45; i++) { - ItemStack item = inventory.getItem(i); - if (item != null && item.getType() != Material.AIR) { - if (i >= 36 && i < 45) { - if (player.getInventory().getItem(i - 36) == null) { - player.getInventory().setItem(i - 36, item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - } else { - if (player.getInventory().getItem(i) == null) { - player.getInventory().setItem(i, item); - } else { - player.getWorld().dropItemNaturally(player.getLocation(), item); - } - } - plugin.getLogger().info(i + " " + item.getType()); - } - } - removeGrave(graveId); - inventoryGraveMap.remove(inventory); - } - }.runTask(plugin); - } - - public void dropGraveItems(Grave grave) { - World world = grave.getLocation().getWorld(); - if (world == null) return; - - // Drop main inventory items - for (ItemStack item : grave.getItems()) { - if (item != null) { - world.dropItemNaturally(grave.getLocation(), item); - } - } - } - public void removeGrave(UUID graveId) { - Grave grave = graves.remove(graveId); + Grave grave = this.getGraveFromGraveID(graveId); if (grave != null) { ArmorStand armorStand = (ArmorStand) grave.getLocation().getWorld().getEntitiesByClass(ArmorStand.class).stream() .filter(entity -> entity.getUniqueId().equals(grave.getArmorStandId())) @@ -245,23 +92,13 @@ public void removeGrave(UUID graveId) { block.setType(Material.AIR); } - saveGraves(); - plugin.getLogger().info("Removed grave with UUID: " + graveId); - } - } - - private ItemStack createGlassPane(Material material, String name) { - ItemStack pane = new ItemStack(material); - ItemMeta meta = pane.getItemMeta(); - if (meta != null) { - meta.setDisplayName(name); - pane.setItemMeta(meta); + graves.remove(graveId); + gravePersistenceManager.saveGraves(); } - return pane; } - public UUID getGraveIdFromInventory(Inventory inventory) { - return inventoryGraveMap.get(inventory); + public boolean isGraveArmorStand(UUID uuid) { + return graves.containsKey(uuid); } public Grave getGraveFromUUID(UUID armorStandUUID) { @@ -281,10 +118,6 @@ public UUID getArmorStandIdFromGraveId(UUID graveId) { return null; } - public boolean isGraveArmorStand(UUID uuid) { - return graves.containsKey(uuid); - } - public UUID getGraveIdFromArmorStand(UUID armorStandId) { for (Map.Entry entry : graves.entrySet()) { if (entry.getValue().getArmorStandId().equals(armorStandId)) { @@ -298,194 +131,15 @@ public Grave getGraveFromGraveID(UUID graveId) { return graves.get(graveId); } - public Map getGraves() { - return graves; - } - - private void startGraveTimeoutTask() { - lastUpdateTime = System.currentTimeMillis(); - plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { - long currentTime = System.currentTimeMillis(); - long elapsedTime = currentTime - lastUpdateTime; - lastUpdateTime = currentTime; - - Iterator iterator = graves.values().iterator(); - - while (iterator.hasNext()) { - Grave grave = iterator.next(); - grave.incrementActiveTime(elapsedTime); - - if (grave.getMaxActiveTime() == -1) { - continue; - } - - if (grave.isExpired()) { - if (grave.getLocation().getWorld().isChunkLoaded(grave.getLocation().getBlockX() >> 4, grave.getLocation().getBlockZ() >> 4)) { - if (isPlayerNearby(grave.getLocation(), 50)) { - this.dropGraveItems(grave); - this.removeGrave(grave.getGraveId()); - iterator.remove(); - } - } - } else if (isPlayerNearby(grave.getLocation(), 50)) { - long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); - String coloredTime = getColoredTime(grave); - updateGraveName(grave.getGraveId(), coloredTime); - } - } - }, 0L, 20L); // 20 ticks = 1 second - - plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { - saveGraves(); - }, 0L, 200L); // 200 ticks = 10 seconds - } - - private boolean isPlayerNearby(Location location, double radius) { - return location.getWorld().getPlayers().stream() - .anyMatch(player -> player.getLocation().distanceSquared(location) <= radius * radius); - } - - public void updateGraveName(UUID graveId, String coloredTime) { - Grave grave = graves.get(graveId); - if (grave != null) { - Location location = grave.getLocation(); - World world = location.getWorld(); - if (world != null) { - ArmorStand armorStand = (ArmorStand) Bukkit.getEntity(this.getArmorStandIdFromGraveId(graveId)); - String playerName = grave.getPlayerName(); // Assuming you have this method in Grave class - armorStand.setCustomName(playerName + "'s Grave - " + coloredTime); - } - } - } - - public void saveGraves() { - File file = new File(plugin.getDataFolder(), "graves.yml"); - YamlConfiguration config = new YamlConfiguration(); - - for (Map.Entry entry : graves.entrySet()) { - String path = "graves." + entry.getKey(); - Grave grave = entry.getValue(); - config.set(path + ".playerName", grave.getPlayerName()); - config.set(path + ".location", grave.getLocation().toVector()); - config.set(path + ".world", grave.getLocation().getWorld().getName()); - config.set(path + ".armorStandId", grave.getArmorStandId().toString()); - config.set(path + ".activeTime", grave.getActiveTime()); - config.set(path + ".maxActiveTime", grave.getMaxActiveTime()); - config.set(path + ".expired", grave.isExpired()); - config.set(path + ".items", itemStackArrayToBase64(grave.getItems())); - config.set(path + ".armorItems", itemStackArrayToBase64(grave.getArmorItems())); - config.set(path + ".offHand", itemStackToBase64(grave.getOffHand())); - } - - try { - config.save(file); - } catch (IOException e) { - plugin.getLogger().severe("Could not save graves: " + e.getMessage()); - } - } - - public void loadGraves() { - File file = new File(plugin.getDataFolder(), "graves.yml"); - if (!file.exists()) { - return; - } - - YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - ConfigurationSection section = config.getConfigurationSection("graves"); - - if (section != null) { - for (String key : section.getKeys(false)) { - String path = "graves." + key; - String worldName = config.getString(path + ".world"); - if (worldName == null) { - plugin.getLogger().severe("World name is null for grave " + key); - continue; - } - - World world = Bukkit.getWorld(worldName); - if (world == null) { - plugin.getLogger().severe("World " + worldName + " does not exist on the server"); - continue; - } - - String playerName = config.getString(path + ".playerName"); - Location location = config.getVector(path + ".location").toLocation(world); - UUID armorStandId = UUID.fromString(config.getString(path + ".armorStandId")); - long activeTime = config.getLong(path + ".activeTime", 0); - long maxActiveTime = config.getLong(path + ".maxActiveTime"); - boolean expired = config.getBoolean(path + ".expired", false); - ItemStack[] items = itemStackArrayFromBase64(config.getString(path + ".items")); - ItemStack[] armorItems = itemStackArrayFromBase64(config.getString(path + ".armorItems")); - ItemStack offHand = itemStackFromBase64(config.getString(path + ".offHand")); - - Grave grave = new Grave(playerName, UUID.fromString(key), location, items, armorItems, offHand, armorStandId, maxActiveTime, expired); - graves.put(UUID.fromString(key), grave); - grave.incrementActiveTime(activeTime); - - } - } - } - - private String itemStackArrayToBase64(ItemStack[] items) { - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); - - dataOutput.writeInt(items.length); - - for (ItemStack item : items) { - dataOutput.writeObject(item); - } - - dataOutput.close(); - return Base64.getEncoder().encodeToString(outputStream.toByteArray()); - } catch (Exception e) { - throw new IllegalStateException("Unable to save item stacks.", e); - } - } - - private String itemStackToBase64(ItemStack item) { - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); - - dataOutput.writeObject(item); - - dataOutput.close(); - return Base64.getEncoder().encodeToString(outputStream.toByteArray()); - } catch (Exception e) { - throw new IllegalStateException("Unable to save item stack.", e); - } + public void addGrave(UUID id, Grave grave) { + graves.put(id, grave); } - private ItemStack[] itemStackArrayFromBase64(String data) { - try { - ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data)); - BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); - ItemStack[] items = new ItemStack[dataInput.readInt()]; - - for (int i = 0; i < items.length; i++) { - items[i] = (ItemStack) dataInput.readObject(); - } - - dataInput.close(); - return items; - } catch (Exception e) { - throw new IllegalStateException("Unable to load item stacks.", e); - } + public Grave getGrave(UUID id) { + return graves.get(id); } - private ItemStack itemStackFromBase64(String data) { - try { - ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data)); - BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); - - ItemStack item = (ItemStack) dataInput.readObject(); - - dataInput.close(); - return item; - } catch (Exception e) { - throw new IllegalStateException("Unable to load item stack.", e); - } + public Map getGraves() { + return graves; } } \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GravePersistenceManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GravePersistenceManager.java new file mode 100644 index 0000000..1612f1e --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/manager/GravePersistenceManager.java @@ -0,0 +1,192 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.manager; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Base64; +import java.util.Map; +import java.util.UUID; + +public class GravePersistenceManager { + + private final GravePlugin plugin; + private GraveManager graveManager; + + + public GravePersistenceManager(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + } + + public void saveGraves() { + File file = new File(plugin.getDataFolder(), "graves.yml"); + YamlConfiguration config = new YamlConfiguration(); + Map graves = graveManager.getGraves(); + + for (Map.Entry entry : graves.entrySet()) { + String path = "graves." + entry.getKey(); + Grave grave = entry.getValue(); + config.set(path + ".playerName", grave.getPlayerName()); + config.set(path + ".location", grave.getLocation().toVector()); + config.set(path + ".world", grave.getLocation().getWorld().getName()); + config.set(path + ".armorStandId", grave.getArmorStandId().toString()); + config.set(path + ".activeTime", grave.getActiveTime()); + config.set(path + ".maxActiveTime", grave.getMaxActiveTime()); + config.set(path + ".expired", grave.isExpired()); + config.set(path + ".items", itemStackArrayToBase64(grave.getItems())); + config.set(path + ".armorItems", itemStackArrayToBase64(grave.getArmorItems())); + config.set(path + ".offHand", itemStackToBase64(grave.getOffHand())); + config.set(path + ".lvl", grave.getLvl()); + config.set(path + ".exp", grave.getExp()); + } + + try { + config.save(file); + } catch (IOException e) { + plugin.getLogger().severe("Could not save graves: " + e.getMessage()); + } + } + + public void loadGraves() { + File file = new File(plugin.getDataFolder(), "graves.yml"); + if (!file.exists()) { + return; + } + + YamlConfiguration config = YamlConfiguration.loadConfiguration(file); + ConfigurationSection section = config.getConfigurationSection("graves"); + + if (section != null) { + for (String key : section.getKeys(false)) { + String path = "graves." + key; + String worldName = config.getString(path + ".world"); + if (worldName == null) { + plugin.getLogger().severe("World name is null for grave " + key); + continue; + } + + World world = Bukkit.getWorld(worldName); + if (world == null) { + plugin.getLogger().severe("World " + worldName + " does not exist on the server"); + continue; + } + + String playerName = config.getString(path + ".playerName"); + Location location = config.getVector(path + ".location").toLocation(world); + UUID armorStandId = UUID.fromString(config.getString(path + ".armorStandId")); + long activeTime = config.getLong(path + ".activeTime", 0); + long maxActiveTime = config.getLong(path + ".maxActiveTime"); + boolean expired = config.getBoolean(path + ".expired", false); + ItemStack[] items = itemStackArrayFromBase64(config.getString(path + ".items")); + ItemStack[] armorItems = itemStackArrayFromBase64(config.getString(path + ".armorItems")); + ItemStack offHand = itemStackFromBase64(config.getString(path + ".offHand")); + int lvl = config.getInt(path + ".lvl", 0); + float exp = (float) config.getDouble(path + ".exp", 0.0); + + Grave grave = new Grave(playerName, UUID.fromString(key), location, items, armorItems, offHand, armorStandId, maxActiveTime, expired, lvl,exp); + graveManager.addGrave(UUID.fromString(key), grave); + grave.incrementActiveTime(activeTime); + + } + } + } + + private String itemStackArrayToBase64(ItemStack[] items) { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); + + dataOutput.writeInt(items.length); + + for (ItemStack item : items) { + dataOutput.writeObject(item); + } + + dataOutput.close(); + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } catch (Exception e) { + throw new IllegalStateException("Unable to save item stacks.", e); + } + } + + private String itemStackToBase64(ItemStack item) { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream); + + dataOutput.writeObject(item); + + dataOutput.close(); + return Base64.getEncoder().encodeToString(outputStream.toByteArray()); + } catch (Exception e) { + throw new IllegalStateException("Unable to save item stack.", e); + } + } + + private ItemStack[] itemStackArrayFromBase64(String data) { + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data)); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + ItemStack[] items = new ItemStack[dataInput.readInt()]; + + for (int i = 0; i < items.length; i++) { + items[i] = (ItemStack) dataInput.readObject(); + } + + dataInput.close(); + return items; + } catch (Exception e) { + throw new IllegalStateException("Unable to load item stacks.", e); + } + } + + private ItemStack itemStackFromBase64(String data) { + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data)); + BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream); + + ItemStack item = (ItemStack) dataInput.readObject(); + + dataInput.close(); + return item; + } catch (Exception e) { + throw new IllegalStateException("Unable to load item stack.", e); + } + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/manager/GraveTimeoutManager.java b/src/main/java/dev/pluginz/graveplugin/manager/GraveTimeoutManager.java new file mode 100644 index 0000000..8fa3922 --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/manager/GraveTimeoutManager.java @@ -0,0 +1,135 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.pluginz.graveplugin.manager; + +import dev.pluginz.graveplugin.GravePlugin; +import dev.pluginz.graveplugin.util.Grave; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.ArmorStand; + +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +public class GraveTimeoutManager { + + private final GravePlugin plugin; + private GraveManager graveManager; + private GraveInventoryManager graveInventoryManager; + private GravePersistenceManager gravePersistenceManager; + + private long lastUpdateTime; + + public GraveTimeoutManager(GravePlugin plugin) { + this.plugin = plugin; + this.graveManager = plugin.getGraveManager(); + this.graveInventoryManager = plugin.getGraveInventoryManager(); + this.gravePersistenceManager = plugin.getGravePersistenceManager(); + } + + public void startGraveTimeoutTask() { + lastUpdateTime = System.currentTimeMillis(); + plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + long currentTime = System.currentTimeMillis(); + long elapsedTime = currentTime - lastUpdateTime; + lastUpdateTime = currentTime; + Map graves = graveManager.getGraves(); + Iterator iterator = graves.values().iterator(); + + while (iterator.hasNext()) { + Grave grave = iterator.next(); + grave.incrementActiveTime(elapsedTime); + + if (grave.getMaxActiveTime() == -1) { + continue; + } + if (grave.getMaxActiveTime() != -1 && grave.getActiveTime() >= grave.getMaxActiveTime()) { + grave.setExpired(true); + } + + if (grave.isExpired()) { + if (grave.getLocation().getWorld().isChunkLoaded(grave.getLocation().getBlockX() >> 4, grave.getLocation().getBlockZ() >> 4)) { + if (isPlayerNearby(grave.getLocation(), 50)) { + graveInventoryManager.dropGraveItems(grave); + graveManager.removeGrave(grave.getGraveId()); + //iterator.remove(); + } + } + } else if (isPlayerNearby(grave.getLocation(), 50)) { + long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); + String coloredTime = getColoredTime(grave); + updateGraveName(grave.getGraveId(), coloredTime); + } + } + }, 0L, 20L); // 20 ticks = 1 second + + plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + gravePersistenceManager.saveGraves(); + }, 0L, 200L); // 200 ticks = 10 seconds + } + + public void updateGraveName(UUID graveId, String coloredTime) { + Grave grave = graveManager.getGrave(graveId); + if (grave != null) { + Location location = grave.getLocation(); + World world = location.getWorld(); + if (world != null) { + ArmorStand armorStand = (ArmorStand) Bukkit.getEntity(graveManager.getArmorStandIdFromGraveId(graveId)); + String playerName = grave.getPlayerName(); + armorStand.setCustomName(playerName + "'s Grave - " + coloredTime); + } + } + } + + private boolean isPlayerNearby(Location location, double radius) { + return location.getWorld().getPlayers().stream() + .anyMatch(player -> player.getLocation().distanceSquared(location) <= radius * radius); + } + + private static String getColoredTime(Grave grave) { + long remainingTime = grave.getMaxActiveTime() - grave.getActiveTime(); + int remainingSeconds = (int) (remainingTime / 1000); // Convert milliseconds to seconds + + int hours = remainingSeconds / 3600; + int minutes = (remainingSeconds % 3600) / 60; + int seconds = remainingSeconds % 60; + + String time = String.format("%02dh %02dm %02ds", hours, minutes, seconds); + + double ratio = (double) grave.getActiveTime() / grave.getMaxActiveTime(); + ChatColor color; + if (ratio < 0.33) { + color = ChatColor.GREEN; + } else if (ratio < 0.66) { + color = ChatColor.GOLD; + } else { + color = ChatColor.RED; + } + return color + time; + } +} diff --git a/src/main/java/dev/pluginz/graveplugin/util/Grave.java b/src/main/java/dev/pluginz/graveplugin/util/Grave.java index c7b26e0..1c30d12 100644 --- a/src/main/java/dev/pluginz/graveplugin/util/Grave.java +++ b/src/main/java/dev/pluginz/graveplugin/util/Grave.java @@ -1,3 +1,27 @@ +/* + * This file is part of BT's Graves, licensed under the MIT License. + * + * Copyright (c) BT Pluginz + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.pluginz.graveplugin.util; import org.bukkit.Location; @@ -16,8 +40,10 @@ public class Grave { private long activeTime; private final long maxActiveTime; private boolean expired; + private int lvl; + private float exp; - public Grave(String playerName, UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId, long maxActiveTime, boolean expired) { + public Grave(String playerName, UUID graveId, Location location, ItemStack[] items, ItemStack[] armorItems, ItemStack offHand, UUID armorStandId, long maxActiveTime, boolean expired, int lvl, float exp) { this.playerName = playerName; this.graveId = graveId; this.location = location; @@ -28,6 +54,8 @@ public Grave(String playerName, UUID graveId, Location location, ItemStack[] ite this.activeTime = 0; this.maxActiveTime = maxActiveTime; this.expired = expired; + this.lvl = lvl; + this.exp = exp; } public String getPlayerName() { @@ -77,4 +105,13 @@ public boolean isExpired() { public void setExpired(boolean expired) { this.expired = expired; } + + + public int getLvl() { + return lvl; + } + + public float getExp() { + return exp; + } } \ No newline at end of file diff --git a/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java b/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java index 2d7cfa6..37b5cd2 100644 --- a/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java +++ b/src/main/java/dev/pluginz/graveplugin/util/VersionChecker.java @@ -1,5 +1,5 @@ /* - * This file is part of BT's GravePlugin, licensed under the MIT License. + * This file is part of BT's Graves, licensed under the MIT License. * * Copyright (c) BT Pluginz * @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + package dev.pluginz.graveplugin.util; import com.fasterxml.jackson.core.type.TypeReference; @@ -53,7 +54,7 @@ public static boolean isNewVersionAvailable(String version, String project) { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); - connection.setRequestProperty("User-Agent", "BTPluginz/GravePlugin/"+ version ); + connection.setRequestProperty("User-Agent", "BTPluginz/GravePlugin/"+ version + " (github@tubyoub.de)"); int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d9bfd2d..fd6a168 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,6 +1,6 @@ ################################ -# Tub's StatusPlugin # -# by BT Pluginz # +# BT Graves # +# by BT Pluginz # ################################ #------------------------------ # Do not touch this value @@ -14,4 +14,13 @@ checkVersion: true # How long should graves last (in minutes) # If you don't want to delete graves # default: 60 -graveTimeout: 60 \ No newline at end of file +graveTimeout: 60 +# If the Armor stand should be small or not +# this changes the Hitbox of the Grave and +# the Height of the "Hologram" (Text) +# default: true +smallArmorStand: true +# How much % of the Players Experience should be saved +# 0 - 100 +# default: 100 +expPercentage: 100 # % \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 66cbc28..7bed8c4 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,15 +1,12 @@ -name: BTsGravePlugin +name: BTGraves version: '${project.version}' main: dev.pluginz.graveplugin.GravePlugin api-version: 1.13 description: A plugin to create graves on player death. permissions: - graveplugin.admin.access: + btgraves.reload: description: Allows access to all graves. default: op - graveplugin.admin.manage: - description: Allows management of graves. - default: op commands: grave: description: Grave management command From be7045b1949041710938f22d4250e468f19c5f23 Mon Sep 17 00:00:00 2001 From: TubYoub Date: Fri, 12 Jul 2024 17:39:47 +0200 Subject: [PATCH 6/8] Tab Completion, changed Modrinth link Changes: - Added TabCompletion - changed Modrinth link in the info Command TODO: - Code Documentation - more probably coming Took 14 minutes --- .../dev/pluginz/graveplugin/GravePlugin.java | 2 ++ .../graveplugin/command/GraveCommand.java | 2 +- .../command/GraveTabCompleter.java | 32 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/main/java/dev/pluginz/graveplugin/command/GraveTabCompleter.java diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index a1ccd60..7159fc9 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -25,6 +25,7 @@ package dev.pluginz.graveplugin; import dev.pluginz.graveplugin.command.GraveCommand; +import dev.pluginz.graveplugin.command.GraveTabCompleter; import dev.pluginz.graveplugin.listener.*; import dev.pluginz.graveplugin.manager.*; import dev.pluginz.graveplugin.util.VersionChecker; @@ -80,6 +81,7 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new PlayerInteractListener(this), this); getServer().getPluginManager().registerEvents(new BlockPlaceListener(this), this); getCommand("grave").setExecutor(new GraveCommand(this)); + getCommand("grave").setTabCompleter(new GraveTabCompleter()); } @Override diff --git a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java index 143deee..fef5abc 100644 --- a/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java +++ b/src/main/java/dev/pluginz/graveplugin/command/GraveCommand.java @@ -69,7 +69,7 @@ private void handleInfo(CommandSender sender, String[] args) { sender.sendMessage(ChatColor.GREEN + "Version: " + plugin.getVersion()); if (plugin.getConfigManager().isCheckVersion()) { if (plugin.isNewVersion()) { - sender.sendMessage(ChatColor.YELLOW + "A new version is available! Update at: " + ChatColor.UNDERLINE + "https://modrinth.com/project/bts-graves"); + sender.sendMessage(ChatColor.YELLOW + "A new version is available! Update at: " + ChatColor.UNDERLINE + "https://modrinth.com/project/bt-graves"); } else { sender.sendMessage(ChatColor.GREEN + "You are using the latest version!"); } diff --git a/src/main/java/dev/pluginz/graveplugin/command/GraveTabCompleter.java b/src/main/java/dev/pluginz/graveplugin/command/GraveTabCompleter.java new file mode 100644 index 0000000..c64138a --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/command/GraveTabCompleter.java @@ -0,0 +1,32 @@ +package dev.pluginz.graveplugin.command; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.util.StringUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class GraveTabCompleter implements TabCompleter { + + public GraveTabCompleter() {} + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + List completions = new ArrayList<>(); + List commands = new ArrayList<>(); + + if (args.length == 1) { + if (sender.hasPermission("btgraves.reload")) { + commands.add("reload"); + } + commands.add("info"); + StringUtil.copyPartialMatches(args[0], commands, completions); + } + + Collections.sort(completions); + return completions; + } +} From 1eafb436e3c9edc193ff0bb766a44d9f70a822bd Mon Sep 17 00:00:00 2001 From: TubYoub Date: Sat, 13 Jul 2024 01:58:25 +0200 Subject: [PATCH 7/8] ReadMe, Metrics Changes: - Added ReadMe - Added Bstats Metrics TODO: - Code Documentation - more probably coming Took 37 minutes --- README.md | 55 ++ .../dev/pluginz/graveplugin/GravePlugin.java | 4 + .../dev/pluginz/graveplugin/util/Metrics.java | 881 ++++++++++++++++++ 3 files changed, 940 insertions(+) create mode 100644 README.md create mode 100644 src/main/java/dev/pluginz/graveplugin/util/Metrics.java diff --git a/README.md b/README.md new file mode 100644 index 0000000..fea7ef1 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# BT Graves +![Static Badge](https://img.shields.io/badge/MC-1.13-green) +![Static Badge](https://img.shields.io/badge/MC-1.14-green) +![Static Badge](https://img.shields.io/badge/MC-1.15-green) +![Static Badge](https://img.shields.io/badge/MC-1.16-green) +![Static Badge](https://img.shields.io/badge/MC-1.17-green) +![Static Badge](https://img.shields.io/badge/MC-1.18-green) +![Static Badge](https://img.shields.io/badge/MC-1.19-green) +![Static Badge](https://img.shields.io/badge/MC-1.20-green) +![Static Badge](https://img.shields.io/badge/MC-1.21-green) +![Modrinth Downloads](https://img.shields.io/modrinth/dt/WGgaXko0?logo=Modrinth&style=flat-square) + + +![forthebadge](https://forthebadge.com/images/badges/works-on-my-machine.svg) + +

+ + + +
+ Please join the Discord if you have questions! +

+
+ +This Minecraft Plugin spawn a Grave when they die which saves their inventory and also Exp. +## Features +- When a player dies the Grave Spawns a Grave, which has a Playerhead on the ground and a name over it. +- The Grave will always search for the next option to spawn: + - always on top of Liquids + - never in the Wall always the next best location +- Graves also cant be destroyed by a player. + - Liquid proof + - Explosion proof + - piston proof +#### Config +- disable/ enable Version checking +- change the armor stand which is used by the Plugin. Big/small +- change how much of the Level should be retained in the Grave +#### Commands +- `/grave info`: gives general info from the Plugin +- `/grave reload`: reloads the Config +## Permission +- `btgraves.reload`: allows a player to reload the config of the plugin +## Stats +The Plugin also uses Bstats for basic Stats collection, I just use the basic stats which are automatically collected like player count or Minecraft Version. +You can find the Plugin page [here](https://bstats.org/plugin/bukkit/Bt%20Graves/22622). +## Installation +To install the plugin, simply download the latest release file and place it in your server's plugins folder. Then, restart your server. +## Support +For Support open an issue or join the [Discord](https://discord.pluginz.dev) +## License + +This project is licensed under the [MIT License](LICENSE). + +[![forthebadge](https://forthebadge.com/images/badges/powered-by-black-magic.svg)](https://forthebadge.com) diff --git a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java index 7159fc9..e5c95eb 100644 --- a/src/main/java/dev/pluginz/graveplugin/GravePlugin.java +++ b/src/main/java/dev/pluginz/graveplugin/GravePlugin.java @@ -29,6 +29,7 @@ import dev.pluginz.graveplugin.listener.*; import dev.pluginz.graveplugin.manager.*; import dev.pluginz.graveplugin.util.VersionChecker; +import dev.pluginz.graveplugin.util.Metrics; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -37,6 +38,7 @@ public class GravePlugin extends JavaPlugin { private final String version = "1.0"; private final String project = "WGgaXko0"; + private final int pluginId = 22622; private GraveManager graveManager; private GraveInventoryManager graveInventoryManager; @@ -82,6 +84,8 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new BlockPlaceListener(this), this); getCommand("grave").setExecutor(new GraveCommand(this)); getCommand("grave").setTabCompleter(new GraveTabCompleter()); + + Metrics metrics = new Metrics(this, pluginId); } @Override diff --git a/src/main/java/dev/pluginz/graveplugin/util/Metrics.java b/src/main/java/dev/pluginz/graveplugin/util/Metrics.java new file mode 100644 index 0000000..d1719ba --- /dev/null +++ b/src/main/java/dev/pluginz/graveplugin/util/Metrics.java @@ -0,0 +1,881 @@ +/* + * This Metrics class was auto-generated and can be copied into your project if you are + * not using a build tool like Gradle or Maven for dependency management. + * + * IMPORTANT: You are not allowed to modify this class, except changing the package. + * + * Disallowed modifications include but are not limited to: + * - Remove the option for users to opt-out + * - Change the frequency for data submission + * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) + * - Reformat the code (if you use a linter, add an exception) + * + * Violations will result in a ban of your plugin and account from bStats. + */ +package dev.pluginz.graveplugin.util; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; +import javax.net.ssl.HttpsURLConnection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +public class Metrics { + + private final Plugin plugin; + + private final MetricsBase metricsBase; + + /** + * Creates a new Metrics instance. + * + * @param plugin Your plugin instance. + * @param serviceId The id of the service. It can be found at What is my plugin id? + */ + public Metrics(JavaPlugin plugin, int serviceId) { + this.plugin = plugin; + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + if (!config.isSet("serverUuid")) { + config.addDefault("enabled", true); + config.addDefault("serverUuid", UUID.randomUUID().toString()); + config.addDefault("logFailedRequests", false); + config.addDefault("logSentData", false); + config.addDefault("logResponseStatusText", false); + // Inform the server owners about bStats + config + .options() + .header( + "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" + + "many people use their plugin and their total player count. It's recommended to keep bStats\n" + + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" + + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" + + "anonymous.") + .copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { + } + } + // Load the data + boolean enabled = config.getBoolean("enabled", true); + String serverUUID = config.getString("serverUuid"); + boolean logErrors = config.getBoolean("logFailedRequests", false); + boolean logSentData = config.getBoolean("logSentData", false); + boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); + metricsBase = + new MetricsBase( + "bukkit", + serverUUID, + serviceId, + enabled, + this::appendPlatformData, + this::appendServiceData, + submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), + plugin::isEnabled, + (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), + (message) -> this.plugin.getLogger().log(Level.INFO, message), + logErrors, + logSentData, + logResponseStatusText); + } + + /** Shuts down the underlying scheduler service. */ + public void shutdown() { + metricsBase.shutdown(); + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + metricsBase.addCustomChart(chart); + } + + private void appendPlatformData(JsonObjectBuilder builder) { + builder.appendField("playerAmount", getPlayerAmount()); + builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); + builder.appendField("bukkitVersion", Bukkit.getVersion()); + builder.appendField("bukkitName", Bukkit.getName()); + builder.appendField("javaVersion", System.getProperty("java.version")); + builder.appendField("osName", System.getProperty("os.name")); + builder.appendField("osArch", System.getProperty("os.arch")); + builder.appendField("osVersion", System.getProperty("os.version")); + builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); + } + + private void appendServiceData(JsonObjectBuilder builder) { + builder.appendField("pluginVersion", plugin.getDescription().getVersion()); + } + + private int getPlayerAmount() { + try { + // Around MC 1.8 the return type was changed from an array to a collection, + // This fixes java.lang.NoSuchMethodError: + // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + return onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + // Just use the new method if the reflection failed + return Bukkit.getOnlinePlayers().size(); + } + } + + public static class MetricsBase { + + /** The version of the Metrics class. */ + public static final String METRICS_VERSION = "3.0.2"; + + private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; + + private final ScheduledExecutorService scheduler; + + private final String platform; + + private final String serverUuid; + + private final int serviceId; + + private final Consumer appendPlatformDataConsumer; + + private final Consumer appendServiceDataConsumer; + + private final Consumer submitTaskConsumer; + + private final Supplier checkServiceEnabledSupplier; + + private final BiConsumer errorLogger; + + private final Consumer infoLogger; + + private final boolean logErrors; + + private final boolean logSentData; + + private final boolean logResponseStatusText; + + private final Set customCharts = new HashSet<>(); + + private final boolean enabled; + + /** + * Creates a new MetricsBase class instance. + * + * @param platform The platform of the service. + * @param serviceId The id of the service. + * @param serverUuid The server uuid. + * @param enabled Whether or not data sending is enabled. + * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all platform-specific data. + * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and + * appends all service-specific data. + * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be + * used to delegate the data collection to a another thread to prevent errors caused by + * concurrency. Can be {@code null}. + * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. + * @param errorLogger A consumer that accepts log message and an error. + * @param infoLogger A consumer that accepts info log messages. + * @param logErrors Whether or not errors should be logged. + * @param logSentData Whether or not the sent data should be logged. + * @param logResponseStatusText Whether or not the response status text should be logged. + */ + public MetricsBase( + String platform, + String serverUuid, + int serviceId, + boolean enabled, + Consumer appendPlatformDataConsumer, + Consumer appendServiceDataConsumer, + Consumer submitTaskConsumer, + Supplier checkServiceEnabledSupplier, + BiConsumer errorLogger, + Consumer infoLogger, + boolean logErrors, + boolean logSentData, + boolean logResponseStatusText) { + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics")); + // We want delayed tasks (non-periodic) that will execute in the future to be + // cancelled when the scheduler is shutdown. + // Otherwise, we risk preventing the server from shutting down even when + // MetricsBase#shutdown() is called + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + this.scheduler = scheduler; + this.platform = platform; + this.serverUuid = serverUuid; + this.serviceId = serviceId; + this.enabled = enabled; + this.appendPlatformDataConsumer = appendPlatformDataConsumer; + this.appendServiceDataConsumer = appendServiceDataConsumer; + this.submitTaskConsumer = submitTaskConsumer; + this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; + this.errorLogger = errorLogger; + this.infoLogger = infoLogger; + this.logErrors = logErrors; + this.logSentData = logSentData; + this.logResponseStatusText = logResponseStatusText; + checkRelocation(); + if (enabled) { + // WARNING: Removing the option to opt-out will get your plugin banned from + // bStats + startSubmitting(); + } + } + + public void addCustomChart(CustomChart chart) { + this.customCharts.add(chart); + } + + public void shutdown() { + scheduler.shutdown(); + } + + private void startSubmitting() { + final Runnable submitTask = + () -> { + if (!enabled || !checkServiceEnabledSupplier.get()) { + // Submitting data or service is disabled + scheduler.shutdown(); + return; + } + if (submitTaskConsumer != null) { + submitTaskConsumer.accept(this::submitData); + } else { + this.submitData(); + } + }; + // Many servers tend to restart at a fixed time at xx:00 which causes an uneven + // distribution of requests on the + // bStats backend. To circumvent this problem, we introduce some randomness into + // the initial and second delay. + // WARNING: You must not modify and part of this Metrics class, including the + // submit delay or frequency! + // WARNING: Modifying this code will get your plugin banned on bStats. Just + // don't do it! + long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); + long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); + scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); + scheduler.scheduleAtFixedRate( + submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); + } + + private void submitData() { + final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); + appendPlatformDataConsumer.accept(baseJsonBuilder); + final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); + appendServiceDataConsumer.accept(serviceJsonBuilder); + JsonObjectBuilder.JsonObject[] chartData = + customCharts.stream() + .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) + .filter(Objects::nonNull) + .toArray(JsonObjectBuilder.JsonObject[]::new); + serviceJsonBuilder.appendField("id", serviceId); + serviceJsonBuilder.appendField("customCharts", chartData); + baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); + baseJsonBuilder.appendField("serverUUID", serverUuid); + baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); + JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); + scheduler.execute( + () -> { + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logErrors) { + errorLogger.accept("Could not submit bStats metrics data", e); + } + } + }); + } + + private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { + if (logSentData) { + infoLogger.accept("Sent bStats metrics data: " + data.toString()); + } + String url = String.format(REPORT_URL, platform); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setRequestProperty("User-Agent", "Metrics-Service/1"); + connection.setDoOutput(true); + try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { + outputStream.write(compressedData); + } + StringBuilder builder = new StringBuilder(); + try (BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + } + if (logResponseStatusText) { + infoLogger.accept("Sent data to bStats and received response: " + builder); + } + } + + /** Checks that the class was properly relocated. */ + private void checkRelocation() { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null + || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this + // little "trick" ... :D + final String defaultPackage = + new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); + final String examplePackage = + new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure no one just copy & pastes the example and uses the wrong + // package names + if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) + || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + /** + * Gzips the given string. + * + * @param str The string to gzip. + * @return The gzipped string. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + } + return outputStream.toByteArray(); + } + } + + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()}); + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + // Skip this invalid + continue; + } + allSkipped = false; + valuesBuilder.appendField(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObjectBuilder.JsonObject getChartData() throws Exception { + JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); + } + } + + public abstract static class CustomChart { + + private final String chartId; + + protected CustomChart(String chartId) { + if (chartId == null) { + throw new IllegalArgumentException("chartId must not be null"); + } + this.chartId = chartId; + } + + public JsonObjectBuilder.JsonObject getRequestJsonObject( + BiConsumer errorLogger, boolean logErrors) { + JsonObjectBuilder builder = new JsonObjectBuilder(); + builder.appendField("chartId", chartId); + try { + JsonObjectBuilder.JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + builder.appendField("data", data); + } catch (Throwable t) { + if (logErrors) { + errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return builder.build(); + } + + protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; + } + + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObjectBuilder.JsonObject getChartData() throws Exception { + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + return new JsonObjectBuilder().appendField("value", value).build(); + } + } + + /** + * An extremely simple JSON builder. + * + *

While this class is neither feature-rich nor the most performant one, it's sufficient enough + * for its use-case. + */ + public static class JsonObjectBuilder { + + private StringBuilder builder = new StringBuilder(); + + private boolean hasAtLeastOneField = false; + + public JsonObjectBuilder() { + builder.append("{"); + } + + /** + * Appends a null field to the JSON. + * + * @param key The key of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendNull(String key) { + appendFieldUnescaped(key, "null"); + return this; + } + + /** + * Appends a string field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String value) { + if (value == null) { + throw new IllegalArgumentException("JSON value must not be null"); + } + appendFieldUnescaped(key, "\"" + escape(value) + "\""); + return this; + } + + /** + * Appends an integer field to the JSON. + * + * @param key The key of the field. + * @param value The value of the field. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int value) { + appendFieldUnescaped(key, String.valueOf(value)); + return this; + } + + /** + * Appends an object to the JSON. + * + * @param key The key of the field. + * @param object The object. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject object) { + if (object == null) { + throw new IllegalArgumentException("JSON object must not be null"); + } + appendFieldUnescaped(key, object.toString()); + return this; + } + + /** + * Appends a string array to the JSON. + * + * @param key The key of the field. + * @param values The string array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, String[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values) + .map(value -> "\"" + escape(value) + "\"") + .collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an integer array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, int[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends an object array to the JSON. + * + * @param key The key of the field. + * @param values The integer array. + * @return A reference to this object. + */ + public JsonObjectBuilder appendField(String key, JsonObject[] values) { + if (values == null) { + throw new IllegalArgumentException("JSON values must not be null"); + } + String escapedValues = + Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); + appendFieldUnescaped(key, "[" + escapedValues + "]"); + return this; + } + + /** + * Appends a field to the object. + * + * @param key The key of the field. + * @param escapedValue The escaped value of the field. + */ + private void appendFieldUnescaped(String key, String escapedValue) { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + if (key == null) { + throw new IllegalArgumentException("JSON key must not be null"); + } + if (hasAtLeastOneField) { + builder.append(","); + } + builder.append("\"").append(escape(key)).append("\":").append(escapedValue); + hasAtLeastOneField = true; + } + + /** + * Builds the JSON string and invalidates this builder. + * + * @return The built JSON string. + */ + public JsonObject build() { + if (builder == null) { + throw new IllegalStateException("JSON has already been built"); + } + JsonObject object = new JsonObject(builder.append("}").toString()); + builder = null; + return object; + } + + /** + * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. + * + *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. + * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). + * + * @param value The value to escape. + * @return The escaped value. + */ + private static String escape(String value) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (c == '"') { + builder.append("\\\""); + } else if (c == '\\') { + builder.append("\\\\"); + } else if (c <= '\u000F') { + builder.append("\\u000").append(Integer.toHexString(c)); + } else if (c <= '\u001F') { + builder.append("\\u00").append(Integer.toHexString(c)); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * A super simple representation of a JSON object. + * + *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not + * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, + * JsonObject)}. + */ + public static class JsonObject { + + private final String value; + + private JsonObject(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + } +} \ No newline at end of file From f97aa5025ab913f45d3f6dd81deb1bb75bb2eabc Mon Sep 17 00:00:00 2001 From: TubYoub Date: Sat, 13 Jul 2024 02:39:18 +0200 Subject: [PATCH 8/8] Update Readme Changes: - Updated README.md TODO: - Code Documentation - more probably coming Took 13 minutes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index fea7ef1..15ce4b1 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,9 @@ This Minecraft Plugin spawn a Grave when they die which saves their inventory an - Liquid proof - Explosion proof - piston proof +- Graves Save to a file, so they will retain themselves after a restart or server crash. +- Timer for how long should last before dropping everything on the ground. +- Grave Names are only updated when a player is in reach of the Grave to prevent unnecessary lag. #### Config - disable/ enable Version checking - change the armor stand which is used by the Plugin. Big/small