commit 4564d11aa1de832c6759d00e07b03604078c6562 Author: Molzonas Date: Thu Aug 21 06:34:48 2025 +0200 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7a579b0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + fr.molzonas + PainfulLoss + 1.0 + jar + + PainfulLoss + + + 21 + UTF-8 + + + + clean package + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.3 + + + package + + shade + + + + + + + + src/main/resources + true + + + + + + + spigotmc-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.20.6-R0.1-SNAPSHOT + provided + + + org.projectlombok + lombok + 1.18.38 + + + org.jetbrains + annotations + 26.0.2 + compile + + + diff --git a/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java b/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java new file mode 100644 index 0000000..63d4d0c --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java @@ -0,0 +1,100 @@ +package fr.molzonas.painfulloss; + +import fr.molzonas.painfulloss.enums.PropertiesEnum; +import fr.molzonas.painfulloss.listeners.DeathListener; +import fr.molzonas.painfulloss.utils.Message; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public final class PainfulLoss extends JavaPlugin { + public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; + public static final String DEFAULT_LOCALE_TAG = "en-US"; + @Getter private static PainfulLoss instance = null; + + @Override + public void onEnable() { + PainfulLoss.instance = this; + Message.init(DEFAULT_LOCALE); + configurationInit(); + langInit(); + registerListeners(); + Bukkit.getLogger().info("[PainfulLoss] Plugin has been enabled!"); + } + + @Override + public void onDisable() { + Bukkit.getLogger().info("[PainfulLoss] Plugin has been disabled!"); + } + + private void configurationInit() { + saveDefaultConfig(); + saveResource("lang/messages_en.properties", false); + saveResource("lang/messages_fr.properties", false); + } + + private void langInit() { + Locale locale = Locale.forLanguageTag(PropertiesEnum.LOCALE.getValue()); + Message.init(locale); + } + + private void registerListeners() { + Bukkit.getPluginManager().registerEvents(new DeathListener(), this); + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, Command command, @NotNull String label, String[] args) { + return switch (command.getName()) { + case "painfulloss" -> defaultCommand(sender, args); + case "itesten" -> commandLang(sender, Locale.ENGLISH); + case "itestfr" -> commandLang(sender, Locale.FRENCH); + default -> false; + }; + } + + private boolean commandLang(CommandSender sender, Locale locale) { + if (sender instanceof Player p) { + p.sendMessage(Message.of("death.summary", locale, p.getName(), 12345)); + p.sendMessage(Message.of("death.topitem", locale, "Netherite Chestplate", 7890)); + } else { + var text = Message.of("death.summary.first", locale, "Console", 42); + sender.sendMessage(text); + } + return true; + } + + private boolean defaultCommand(@NotNull CommandSender sender, String[] args) { + if (args.length > 0) { + return switch (args[0]) { + case "help" -> commandHelp(sender); + case "reload" -> commandReload(sender); + default -> commandUnknown(sender); + }; + } + return true; + } + + private boolean commandHelp(CommandSender sender) { + sender.sendMessage("&f&lGit gud"); + return true; + } + + private boolean commandReload(CommandSender sender) { + reloadConfig(); + Message.clearCache(); + langInit(); + sender.sendMessage(Message.of("plugin.reload")); + return true; + } + + private boolean commandUnknown(CommandSender sender) { + sender.sendMessage(Message.of("plugin.unknown")); + return true; + } +} diff --git a/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java b/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java new file mode 100644 index 0000000..68f2258 --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java @@ -0,0 +1,22 @@ +package fr.molzonas.painfulloss.enums; + +import fr.molzonas.painfulloss.PainfulLoss; +import lombok.Getter; + +public enum PropertiesEnum { + LOCALE("locale", PainfulLoss.DEFAULT_LOCALE_TAG), + DEBUG("debug", "false"); + + @Getter private final String path; + @Getter private String fallback = null; + + PropertiesEnum(String path, String fallback) { + this.path = path; + this.fallback = fallback; + } + + public String getValue() { + String value = PainfulLoss.getInstance().getConfig().getString(this.getPath()); + return value == null ? fallback : value; + } +} diff --git a/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java b/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java new file mode 100644 index 0000000..d7435f2 --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java @@ -0,0 +1,34 @@ +package fr.molzonas.painfulloss.listeners; + +import fr.molzonas.painfulloss.utils.Message; +import org.bukkit.GameRule; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +public class DeathListener implements Listener { + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getEntity(); + + if (Boolean.TRUE.equals(player.getWorld().getGameRuleValue(GameRule.KEEP_INVENTORY))) return; + + List lostItems = new ArrayList<>(); + for (ItemStack is : event.getDrops()) { + if (is != null && is.getType() != Material.AIR) lostItems.add(is.clone()); + } + + if (lostItems.isEmpty()) return; + + int cnt = lostItems.stream().mapToInt(ItemStack::getAmount).sum(); + + player.sendMessage(Message.of("death.summary", player.getName(), cnt)); + } +} diff --git a/src/main/java/fr/molzonas/painfulloss/utils/Message.java b/src/main/java/fr/molzonas/painfulloss/utils/Message.java new file mode 100644 index 0000000..708858d --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/utils/Message.java @@ -0,0 +1,110 @@ +package fr.molzonas.painfulloss.utils; + +import fr.molzonas.painfulloss.PainfulLoss; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class Message { + private static final String DIRECTORY = "lang"; + private static final String BUNDLE_NAME = "messages"; + private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); + private static Locale currentLocale = Locale.ENGLISH; + + + private Message() {} + + public static void init(Locale locale) { + if (locale == null) locale = PainfulLoss.DEFAULT_LOCALE; + Bukkit.getLogger().info("[Painfullost] Locale used : " + locale.getDisplayName()); + Message.currentLocale = locale; + clearCache(); + } + + public static String of(String key, Object... args) { + return of(key, currentLocale, args); + } + + public static String of(CommandSender sender, String key, Object... args) { + return of(key, senderLocale(sender), args); + } + + public static void send(CommandSender sender, String key, Object... args) { + sender.sendMessage(of(sender, key, args)); + } + + public static String of(String key, Locale locale, Object... args) { + Locale loc = (locale != null) ? locale : currentLocale; + ResourceBundle bundle = getBundle(loc); + String pattern = bundle.containsKey(key) ? bundle.getString(key) : ResourceBundle.getBundle(BUNDLE_NAME, loc).getString(key); + MessageFormat mf = new MessageFormat(pattern, loc); + String formatted = mf.format(args == null ? new Object[0] : args); + return ChatColor.translateAlternateColorCodes('&', formatted); + } + + public static Locale senderLocale(CommandSender sender) { + if (sender instanceof Player p) { + String r = p.getLocale(); + if (!r.isEmpty()) { + return fromMinecraftLocale(r); + } + } + return PainfulLoss.DEFAULT_LOCALE; + } + + public static void clearCache() { + CACHE.clear(); + ResourceBundle.clearCache(Message.class.getClassLoader()); + } + + private static Locale fromMinecraftLocale(String mc) { + String tag = mc.replace('_', '-'); + try { + return Locale.forLanguageTag(tag); + } catch (Exception ignored) { + return PainfulLoss.DEFAULT_LOCALE; + } + } + + private static ResourceBundle getBundle(Locale loc) { + return CACHE.computeIfAbsent(loc, l -> + ResourceBundle.getBundle(BUNDLE_NAME, l, new Utf8Control())); + } + + private static class Utf8Control extends ResourceBundle.Control { + @Override + public ResourceBundle newBundle(String baseName, Locale locale, String format, + ClassLoader loader, boolean reload) throws IOException { + String bundleName = toBundleName(baseName, locale); + String resourceName = toResourceName(bundleName, "properties"); + File file = new File(PainfulLoss.getInstance().getDataFolder(), DIRECTORY + File.separator + resourceName); + + try (FileInputStream fis = new FileInputStream(file)) { + try (InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) { + Properties prop = new Properties(); + prop.load(isr); + return new PropertiesResourceBundle(prop); + } + } + } + } + + private static class PropertiesResourceBundle extends ResourceBundle { + private final Properties props; + PropertiesResourceBundle(Properties props) { this.props = props; } + @Override protected Object handleGetObject(@NotNull String key) { return props.getProperty(key); } + @NotNull + @Override public Enumeration getKeys() { + return Collections.enumeration(props.stringPropertyNames()); + } + @Override public boolean containsKey(@NotNull String key) { return props.containsKey(key); } + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..530e0fd --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,5 @@ +# Available : en-US, fr-FR +locale: en-US + +# Dev option, you won't have to enable it, trust me +debug: false \ No newline at end of file diff --git a/src/main/resources/lang/messages_en.properties b/src/main/resources/lang/messages_en.properties new file mode 100644 index 0000000..f4cd121 --- /dev/null +++ b/src/main/resources/lang/messages_en.properties @@ -0,0 +1,4 @@ +death.summary=&c{0} lost &e{1}&c. +death.topitem=&7Most valuable: &6{0} &7(&e{1}&7) +plugin.reload=&aPainfulLoss has successfully reloaded. +plugin.unknown=&4Unknown command. Please use &c/painfullost help &4to get some help. \ No newline at end of file diff --git a/src/main/resources/lang/messages_fr.properties b/src/main/resources/lang/messages_fr.properties new file mode 100644 index 0000000..763e44c --- /dev/null +++ b/src/main/resources/lang/messages_fr.properties @@ -0,0 +1,4 @@ +death.summary=&c{0} a perdu &e{1}&c. +death.topitem=&7Objet le plus cher : &6{0} &7(&e{1}&7) +plugin.reload=&aPainfulLoss a été rechargé avec succès. +plugin.unknown=&4Commande inconnue. Merci d'utiliser &c/painfulloss help &4pour obtenir de l'aide. \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..36eebd8 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,15 @@ +name: PainfulLoss +version: '1.0' +main: fr.molzonas.painfulloss.PainfulLoss +api-version: '1.20' +author: Molzonas +commands: + itestfr: + description: Test i18n + usage: /itestfr + itesten: + description: Test i18n + usage: /itesten + painfulloss: + description: Base command for Painfulloss + usage: /painfulloss \ No newline at end of file