This commit is contained in:
alex3025
2022-09-26 14:14:24 +02:00
parent 5530c751b5
commit beb4e23936
18 changed files with 912 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
package tk.alex3025.headstones;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import tk.alex3025.headstones.commands.HeadstonesCommand;
import tk.alex3025.headstones.commands.subcommands.ClearDatabaseCommand;
import tk.alex3025.headstones.commands.subcommands.ReloadConfigCommand;
import tk.alex3025.headstones.listeners.BlockBreakListener;
import tk.alex3025.headstones.listeners.PlayerDeathListener;
import tk.alex3025.headstones.listeners.RightClickListener;
import tk.alex3025.headstones.utils.ConfigFile;
public final class Headstones extends JavaPlugin {
private static Headstones instance;
private ConfigFile config;
private ConfigFile messages;
private ConfigFile database;
@Override
public void onEnable() {
instance = this;
this.loadConfigurationFiles();
this.registerListeners();
this.registerCommands();
}
@Override
public void onDisable() {
// Plugin shutdown logic
}
private void loadConfigurationFiles() {
this.config = new ConfigFile(this,"config.yml");
this.messages = new ConfigFile(this,"messages.yml");
this.database = new ConfigFile(this,"database.yml");
}
private void registerListeners() {
new PlayerDeathListener();
new BlockBreakListener();
new RightClickListener();
}
private void registerCommands() {
new HeadstonesCommand();
// Subcommands
new ClearDatabaseCommand();
new ReloadConfigCommand();
}
public static Headstones getInstance() {
return instance;
}
// Config getters
@Override
public @NotNull ConfigFile getConfig() {
return config;
}
public ConfigFile getMessages() {
return messages;
}
public ConfigFile getDatabase() {
return database;
}
}

View File

@@ -0,0 +1,74 @@
package tk.alex3025.headstones.commands;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import tk.alex3025.headstones.Headstones;
import tk.alex3025.headstones.commands.subcommands.SubcommandBase;
import tk.alex3025.headstones.utils.Message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HeadstonesCommand implements CommandExecutor, TabCompleter {
public HeadstonesCommand() {
PluginCommand command = Headstones.getInstance().getCommand("headstones");
if (command != null) {
command.setExecutor(this);
command.setTabCompleter(this);
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) {
if (args.length == 0) {
String prefix = Message.getTranslation("prefix");
Message.sendMessage(sender, "&8&m+----------+&r " + prefix + " &8&m+----------+");
sender.sendMessage("");
Message.sendMessage(sender, " &7Author: &balex3025");
sender.sendMessage("");
Message.sendMessage(sender, " &7Version: &b" + Headstones.getInstance().getDescription().getVersion());
sender.sendMessage("");
Message.sendMessage(sender, "&8&m+----------+&r " + prefix + " &8&m+----------+");
} else {
SubcommandBase subcommand = SubcommandBase.getSubcommand(args[0]);
if (subcommand != null) {
// Remove the subcommand name from the args
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, args.length - 1);
if (subcommand.isPlayersOnly() && !(sender instanceof Player)) {
new Message(sender).translation("player-only").send();
return true;
}
if (!subcommand.hasPermission(sender)) {
new Message(sender).translation("no-permissions").send();
return true;
}
return subcommand.onCommand(sender, newArgs);
}
new Message(sender).translation("unknown-subcommand").send();
}
return true;
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String @NotNull [] args) {
List<String> matches = new ArrayList<>();
if (args.length == 1) {
for (SubcommandBase subcommand : SubcommandBase.getRegisteredSubcommands())
if (subcommand.hasPermission(sender))
matches.add(subcommand.getName());
return StringUtil.copyPartialMatches(args[0], matches, new ArrayList<>());
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,47 @@
package tk.alex3025.headstones.commands.subcommands;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import tk.alex3025.headstones.Headstones;
import tk.alex3025.headstones.utils.ConfigFile;
import tk.alex3025.headstones.utils.Message;
import java.util.HashMap;
import java.util.Map;
public class ClearDatabaseCommand extends SubcommandBase {
private final Map<String, Long> waitingConfirmPlayers = new HashMap<>();;
public ClearDatabaseCommand() {
super("cleardb", "headstones.cleardb");
// Clear waiting players after 10 seconds
Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(Headstones.getInstance(), () -> {
for (Map.Entry<String, Long> entry : this.waitingConfirmPlayers.entrySet())
if (System.currentTimeMillis() - entry.getValue() > 10000)
this.waitingConfirmPlayers.remove(entry.getKey());
}, 0, 40);
}
@Override
public boolean onCommand(CommandSender sender, String[] args) {
Player player = (Player) sender;
if (this.waitingConfirmPlayers.containsKey(player.getUniqueId().toString())) {
this.waitingConfirmPlayers.remove(player.getUniqueId().toString());
ConfigFile headstonesFile = Headstones.getInstance().getDatabase();
headstonesFile.set("headstones", new HashMap<>());
headstonesFile.save();
Message.sendPrefixedMessage(player, "&aDatabase cleared!");
} else {
this.waitingConfirmPlayers.put(player.getUniqueId().toString(), System.currentTimeMillis());
Message.sendPrefixedMessage(player, "&eAre you sure you want to clear the database? &c&lTHIS WILL MAKE ALL EXISTING HEADSTONES USELESS. &7Type &f/headstones cleardb &7again to confirm.");
}
return true;
}
}

View File

@@ -0,0 +1,20 @@
package tk.alex3025.headstones.commands.subcommands;
import org.bukkit.command.CommandSender;
import tk.alex3025.headstones.utils.ConfigFile;
import tk.alex3025.headstones.utils.Message;
public class ReloadConfigCommand {
public ReloadConfigCommand() {
new SubcommandBase("reload", "headstones.reload") {
@Override
public boolean onCommand(CommandSender sender, String[] args) {
ConfigFile.reloadAll();
Message.sendPrefixedMessage(sender, "&aSuccessfully reloaded all configuration files!");
return true;
}
};
}
}

View File

@@ -0,0 +1,66 @@
package tk.alex3025.headstones.commands.subcommands;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public abstract class SubcommandBase {
private static final List<SubcommandBase> registeredSubcommands = new ArrayList<>();
private final String name;
private String permission = null;
private boolean playersOnly = false;
public SubcommandBase(@NotNull String name) {
this.name = name;
this.registerSubcommand();
}
public SubcommandBase(@NotNull String name, String permission) {
this(name);
this.permission = permission;
}
public SubcommandBase(@NotNull String name, String permission, boolean playersOnly) {
this(name, permission);
this.playersOnly = playersOnly;
}
public abstract boolean onCommand(CommandSender sender, String[] args);
public boolean hasPermission(CommandSender sender) {
return this.getPermission() == null || (this.getPermission() != null && sender.hasPermission(this.getPermission()));
}
public String getName() {
return this.name;
}
public String getPermission() {
return this.permission;
}
public boolean isPlayersOnly() {
return this.playersOnly;
}
public void registerSubcommand() {
registeredSubcommands.add(this);
}
public static List<SubcommandBase> getRegisteredSubcommands() {
return registeredSubcommands;
}
public static @Nullable SubcommandBase getSubcommand(String subcommand) {
for (SubcommandBase registered : registeredSubcommands)
if (registered.getName().equals(subcommand))
return registered;
return null;
}
}

View File

@@ -0,0 +1,24 @@
package tk.alex3025.headstones.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.BlockBreakEvent;
import org.jetbrains.annotations.NotNull;
import tk.alex3025.headstones.utils.Headstone;
import tk.alex3025.headstones.utils.Message;
public class BlockBreakListener extends ListenerBase {
@EventHandler
public void onBlockBreak(@NotNull BlockBreakEvent event) {
Headstone headstone = Headstone.fromBlock(event.getBlock());
if (headstone != null)
if (headstone.isOwner(event.getPlayer()))
headstone.onBreak(event);
else {
event.setCancelled(true);
new Message(event.getPlayer()).translation("cannot-break-others").prefixed(false).send();
}
}
}

View File

@@ -0,0 +1,13 @@
package tk.alex3025.headstones.listeners;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import tk.alex3025.headstones.Headstones;
public abstract class ListenerBase implements Listener {
public ListenerBase() {
Bukkit.getPluginManager().registerEvents(this, Headstones.getInstance());
}
}

View File

@@ -0,0 +1,28 @@
package tk.alex3025.headstones.listeners;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.jetbrains.annotations.NotNull;
import tk.alex3025.headstones.utils.ExperienceManager;
import tk.alex3025.headstones.utils.Headstone;
public class PlayerDeathListener extends ListenerBase {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerDeath(@NotNull PlayerDeathEvent event) {
Player player = event.getPlayer();
// Check if the player is a chunk loader from the WildLoaders plugin
if (player instanceof ChunkLoaderNPC) return;
boolean keepExperience = !event.getKeepLevel() && player.hasPermission("headstones.keep-experience");
boolean keepInventory = !event.getKeepInventory() && player.hasPermission("headstones.keep-inventory");
if (!(keepExperience && keepInventory) || !player.getInventory().isEmpty() || ExperienceManager.getExperience(player) != 0)
new Headstone(player).onPlayerDeath(event, keepExperience, keepInventory);
}
}

View File

@@ -0,0 +1,29 @@
package tk.alex3025.headstones.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;
import tk.alex3025.headstones.Headstones;
import tk.alex3025.headstones.utils.Headstone;
import tk.alex3025.headstones.utils.Message;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
public class RightClickListener extends ListenerBase {
@EventHandler
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
if (event.getClickedBlock() != null && event.getAction().isRightClick() && event.getHand().name().equals("HAND")) {
Headstone headstone = Headstone.fromBlock(event.getClickedBlock());
if (headstone != null)
new Message(event.getPlayer(), new HashMap<>() {{
put("username", headstone.getOwner().getName());
put("datetime", new SimpleDateFormat(Headstones.getInstance().getConfig().getString("date-format")).format(new Date(headstone.getTimestamp())));
}}).translation("headstone-info").send();
}
}
}

View File

@@ -0,0 +1,70 @@
package tk.alex3025.headstones.utils;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import tk.alex3025.headstones.Headstones;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ConfigFile extends YamlConfiguration {
private final static List<ConfigFile> CONFIGS = new ArrayList<>();
private final Headstones instance;
private File file;
public ConfigFile(Headstones instance, String filename) {
this.instance = instance;
try {
this.createOrLoadConfig(filename);
} catch (IOException | InvalidConfigurationException e) {
e.printStackTrace();
}
CONFIGS.add(this);
}
public void createOrLoadConfig(String filename) throws IOException, InvalidConfigurationException {
this.file = new File(this.instance.getDataFolder(), filename);
if (!this.file.exists()) {
this.file.getParentFile().mkdirs();
instance.saveResource(filename, false);
}
this.load(this.file);
}
public void save() {
try {
this.save(this.file);
this.reload();
} catch (IOException e) {
e.printStackTrace();
}
}
public void reload() {
try {
this.load(this.file);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
} catch (IOException ignored) {
try {
this.createOrLoadConfig(this.file.getName());
} catch (IOException | InvalidConfigurationException e) {
e.printStackTrace();
}
}
}
public static void reloadAll() {
for (ConfigFile config : CONFIGS)
config.reload();
}
}

View File

@@ -0,0 +1,48 @@
package tk.alex3025.headstones.utils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class ExperienceManager {
public static int getExperience(int level) {
int xp = 0;
if (level >= 0 && level <= 15)
xp = (int) Math.round(Math.pow(level, 2) + 6 * level);
else if (level > 15 && level <= 30)
xp = (int) Math.round((2.5 * Math.pow(level, 2) - 40.5 * level + 360));
else if (level > 30)
xp = (int) Math.round(((4.5 * Math.pow(level, 2) - 162.5 * level + 2220)));
return xp;
}
public static int getExperience(@NotNull Player player) {
return Math.round(player.getExp() * player.getExpToLevel()) + getExperience(player.getLevel());
}
public static void setExperience(Player player, int amount) {
float a = 0, b = 0, c = -amount;
if (amount > getExperience(0) && amount <= getExperience(15)) {
a = 1;
b = 6;
} else if (amount > getExperience(15) && amount <= getExperience(30)) {
a = 2.5f;
b = -40.5f;
c += 360;
} else if (amount > getExperience(30)) {
a = 4.5f;
b = -162.5f;
c += 2220;
}
int level = (int) Math.floor((-b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a));
int xp = amount - getExperience(level);
player.setLevel(level);
player.setExp(0);
player.giveExp(xp);
}
}

View File

@@ -0,0 +1,238 @@
package tk.alex3025.headstones.utils;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Skull;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Rotatable;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import tk.alex3025.headstones.Headstones;
import java.io.IOException;
import java.time.Instant;
import java.util.*;
public class Headstone {
private final String uuid;
private final OfflinePlayer owner;
private final Location location;
private final long timestamp;
private final int experience;
private final ItemStack[] inventory;
public Headstone(@NotNull Player player) {
this.uuid = UUID.randomUUID().toString();
this.owner = player;
this.location = player.getLocation();
this.timestamp = Instant.now().toEpochMilli();
this.experience = ExperienceManager.getExperience(player);
this.inventory = player.getInventory().getContents();
}
private Headstone(String uuid, OfflinePlayer player, Location location, long timestamp, int experience, ItemStack[] inventory) {
this.uuid = uuid;
this.owner = player;
this.location = location;
this.timestamp = timestamp;
this.experience = experience;
this.inventory = inventory;
}
public static @Nullable Headstone fromUUID(String uuid) {
ConfigurationSection headstones = Headstone.getHeadstonesData().getConfigurationSection("headstones");
if (headstones != null) {
ConfigurationSection hs = headstones.getConfigurationSection(uuid);
if (hs != null) {
Location location = new Location(Bukkit.getWorld(hs.getString("world")), hs.getDouble("x"), hs.getDouble("y"), hs.getDouble("z"));
ItemStack[] inventory = null;
if (hs.getString("inventory") != null)
try {
inventory = InventorySerializer.deserialize(hs.getString("inventory"));
} catch (IOException e) {
e.printStackTrace();
}
OfflinePlayer owner = Bukkit.getOfflinePlayer(UUID.fromString(hs.getString("owner")));
return new Headstone(uuid, owner, location, hs.getLong("timestamp"), hs.getInt("experience", 0), inventory);
}
}
return null;
}
public static @Nullable Headstone fromLocation(Location location) {
ConfigurationSection headstones = Headstone.getHeadstonesData().getConfigurationSection("headstones");
if (headstones != null)
for (String uuid : headstones.getKeys(false)) {
Headstone hs = Headstone.fromUUID(uuid);
if (hs != null)
if (location.equals(hs.getLocation()))
return hs;
}
return null;
}
public static @Nullable Headstone fromBlock(@NotNull Block block) {
return block.getType().equals(Material.PLAYER_HEAD) ? Headstone.fromLocation(block.getLocation()) : null;
}
public void onPlayerDeath(PlayerDeathEvent event, boolean keepExperience, boolean keepInventory) {
Location skullLocation = this.createPlayerSkull();
if (skullLocation != null) {
// Disable drops
event.getDrops().clear();
event.setShouldDropExperience(false);
this.savePlayerData(skullLocation, keepExperience, keepInventory);
}
}
public void onBreak(@NotNull BlockBreakEvent event) {
Player player = event.getPlayer();
this.restorePlayerInventory(player);
this.deletePlayerData();
event.setDropItems(Headstones.getInstance().getConfig().getBoolean("drop-player-head"));
player.spawnParticle(Particle.REDSTONE, this.location.add(0.5, 0.2, 0.5), 10, 0.2, 0.1, 0.2, new Particle.DustOptions(Color.fromRGB(255, 255, 255), 1.5F));
player.playSound(this.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 2.0F, 1.0F);
new Message(player).translation("headstone-broken").prefixed(false).send();
}
private void savePlayerData(@NotNull Location skullLocation, boolean keepExperience, boolean keepInventory) {
ConfigFile headstonesFile = Headstone.getHeadstonesData();
ConfigurationSection hs = headstonesFile.createSection("headstones." + this.uuid);
hs.set("owner", this.owner.getUniqueId().toString());
hs.set("x", (int) Math.floor(skullLocation.getX()));
hs.set("y", (int) Math.floor(skullLocation.getY()));
hs.set("z", (int) Math.floor(skullLocation.getZ()));
hs.set("world", skullLocation.getWorld().getName());
hs.set("timestamp", Instant.now().toEpochMilli());
if (keepExperience)
hs.set("experience", this.experience);
if (keepInventory)
hs.set("inventory", InventorySerializer.serialize(this.inventory));
headstonesFile.save();
}
private void deletePlayerData() {
ConfigFile headstonesFile = Headstone.getHeadstonesData();
ConfigurationSection headstones = headstonesFile.getConfigurationSection("headstones");
if (headstones != null)
headstones.set(this.uuid, null);
headstonesFile.save();
}
private void restorePlayerInventory(Player player) {
ExperienceManager.setExperience(player, this.experience);
if (this.inventory != null)
for (int i = 0, size = this.inventory.length; i < size; i++)
if (this.inventory[i] != null) {
PlayerInventory playerInventory = player.getInventory();
if (playerInventory.getItem(i) == null)
playerInventory.setItem(i, this.inventory[i]);
else {
HashMap<Integer, ItemStack> drops = playerInventory.addItem(this.inventory[i]);
for (ItemStack item : drops.values())
player.getWorld().dropItem(this.location, item);
}
}
}
private @Nullable Block checkForSafeBlock() {
int playerX = this.location.getBlockX();
int playerY = this.location.getBlockY();
int playerZ = this.location.getBlockZ();
int radius = 0;
for (int x = playerX - radius; x <= playerX + radius; x++) {
for (int y = playerY - radius; y <= playerY + radius; y++)
for (int z = playerZ - radius; z <= playerZ + radius; z++) {
Block block = this.location.getWorld().getBlockAt(x,y,z);
if (block.getType().isEmpty())
return block;
}
if (radius <= 5)
radius++;
}
return null;
}
private @Nullable Location createPlayerSkull() {
Block block = this.checkForSafeBlock();
if (block != null) {
block.setType(Material.PLAYER_HEAD);
if (block.getState() instanceof Skull skull) {
skull.setOwningPlayer(this.owner);
BlockData data = skull.getBlockData();
List<BlockFace> faces = new ArrayList<>(List.of(BlockFace.values()));
// Remove invalid faces
faces.remove(BlockFace.UP);
faces.remove(BlockFace.DOWN);
faces.remove(BlockFace.SELF);
((Rotatable) data).setRotation(faces.get(new Random().nextInt(faces.size())));
skull.setBlockData(data);
skull.update();
return block.getLocation();
}
}
return null;
}
public boolean isOwner(@NotNull Player player) {
return player.getUniqueId().equals(this.owner.getUniqueId());
}
public OfflinePlayer getOwner() {
return this.owner;
}
public Location getLocation() {
return this.location;
}
public long getTimestamp() {
return this.timestamp;
}
private static ConfigFile getHeadstonesData() {
return Headstones.getInstance().getDatabase();
}
}

View File

@@ -0,0 +1,58 @@
package tk.alex3025.headstones.utils;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class InventorySerializer {
public static @NotNull String serialize(ItemStack[] inventoryContents) throws IllegalStateException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
dataOutput.writeInt(inventoryContents.length);
for (ItemStack item : inventoryContents)
if (item != null)
dataOutput.writeObject(item.serializeAsBytes());
else
dataOutput.writeObject(null);
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
}
public static ItemStack @NotNull [] deserialize(String serializedInventory) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(serializedInventory));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack[] items = new ItemStack[dataInput.readInt()];
for (int i = 0; i < items.length; i++) {
byte[] stack = (byte[]) dataInput.readObject();
if (stack != null)
items[i] = ItemStack.deserializeBytes(stack);
else
items[i] = null;
}
dataInput.close();
return items;
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
}

View File

@@ -0,0 +1,68 @@
package tk.alex3025.headstones.utils;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import tk.alex3025.headstones.Headstones;
import java.util.Map;
public class Message {
private String rawMessage;
private final CommandSender sender;
private Map<String, String> placeholders;
private boolean prefixed = true;
public Message(CommandSender sender) {
this.sender = sender;
}
public Message(CommandSender sender, Map<String, String> placeholders) {
this.sender = sender;
this.placeholders = placeholders;
}
public Message text(String message) {
this.rawMessage = message;
return this;
}
public Message translation(String key) {
return this.text(Headstones.getInstance().getMessages().getString(key));
}
public Message prefixed(boolean prefixed) {
this.prefixed = prefixed;
return this;
}
public void send() {
if (this.rawMessage != null && !this.rawMessage.isEmpty()) {
// Format placeholders
if (this.placeholders != null)
for (Map.Entry<String, String> entry : this.placeholders.entrySet())
this.rawMessage = this.rawMessage.replace("%" + entry.getKey() + "%", entry.getValue());
if (this.prefixed)
Message.sendPrefixedMessage(this.sender, this.rawMessage);
else
Message.sendMessage(this.sender, this.rawMessage);
}
}
public static void sendMessage(@NotNull CommandSender sender, String message) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
}
public static void sendPrefixedMessage(CommandSender sender, String message) {
String prefix = Headstones.getInstance().getMessages().getString("prefix");
Message.sendMessage(sender, prefix + " " + message);
}
public static String getTranslation(String key) {
return Headstones.getInstance().getMessages().getString(key);
}
}

View File

@@ -0,0 +1,12 @@
# ===================================== #
# #
# Headstones v${project.version} - by alex3025 #
# PLUGIN CONFIGURATION #
# #
# ===================================== #
# The player head will be dropped on break?
drop-player-head: false
# The date format to use in the headstone info message.
date-format: 'dd/MM/yyyy HH:mm'

View File

@@ -0,0 +1,6 @@
# -------------------------- #
# Headstones Database File #
# /!\ DO NOT EDIT /!\ #
# -------------------------- #
headstones: {}

View File

@@ -0,0 +1,25 @@
# ===================================== #
# #
# Headstones v${project.version} - by alex3025 #
# MESSAGES CUSTOMIZATION #
# #
# ===================================== #
prefix: '&8[&bHeadstones&8]'
# You can set any message to an empty string to disable it.
# These messages won't have the prefix.
no-permissions: '&cYou don''t have the permission to use this command!'
cannot-break-others: '&cYou cannot break other players'' headstones!'
headstone-broken: '&aYou broke your headstone and got all your items back!'
# These messages will have the prefix.
unknown-subcommand: '&cUnknown subcommand!'
player-only: '&cThis command can be executed by players only!'
headstone-info: '&f%username%&7 died here on &f%datetime%&7'
# These messages will appear on the action bar.
inventory-full: '&cYour inventory is full!'
some-items-dropped: '&cSome items were dropped on the ground!'

View File

@@ -0,0 +1,13 @@
name: Headstones
description: Don't lose your precious items when you die!
authors: [ alex3025 ]
website: https://alex3025.tk
version: '${project.version}'
api-version: 1.18
main: tk.alex3025.headstones.Headstones
commands:
headstones:
aliases: [ hs, headstone ]
usage: /headstones