From c7b4554a6ca7d36dd13b05b09815eb01f7183129 Mon Sep 17 00:00:00 2001 From: Jikoo Date: Tue, 24 Mar 2020 21:01:59 -0400 Subject: [PATCH] It's been over 3 years :) The common module was designed to prevent the internal modules depending on the core plugin. With the introduction of localization, this overcomplication became ever more exacerbated. Probably will play around a bit more to remove freshly introduced static abuse before release. Closes #61 --- api/pom.xml | 17 -- assembly/pom.xml | 2 +- common/pom.xml | 74 -------- internal/v1_14_R1/pom.xml | 4 +- .../internal/v1_14_R1/AnySilentContainer.java | 5 +- .../internal/v1_14_R1/PlayerDataManager.java | 46 ++++- internal/v1_15_R1/pom.xml | 4 +- .../internal/v1_15_R1/AnySilentContainer.java | 3 +- .../internal/v1_15_R1/PlayerDataManager.java | 47 ++++- internal/v1_8_R3/pom.xml | 4 +- .../internal/v1_8_R3/PlayerDataManager.java | 74 +++++--- plugin/pom.xml | 6 +- .../main/java/com/lishid/openinv/OpenInv.java | 74 ++++++-- .../commands/ContainerSettingCommand.java | 16 +- .../openinv/commands/OpenInvCommand.java | 35 ++-- .../commands/SearchContainerCommand.java | 26 ++- .../commands/SearchEnchantCommand.java | 42 +++-- .../openinv/commands/SearchInvCommand.java | 22 ++- .../openinv/internal/IPlayerDataManager.java | 11 +- .../openinv/listeners/PlayerListener.java | 18 +- .../java/com/lishid/openinv/util/Cache.java | 0 .../lishid/openinv/util/ConfigUpdater.java | 14 ++ .../lishid/openinv/util/InternalAccessor.java | 0 .../lishid/openinv/util/LanguageManager.java | 165 ++++++++++++++++++ .../com/lishid/openinv/util/Permissions.java | 0 plugin/src/main/resources/config.yml | 6 +- plugin/src/main/resources/en_us.yml | 28 +++ plugin/src/main/resources/plugin.yml | 4 +- pom.xml | 1 - 29 files changed, 530 insertions(+), 218 deletions(-) delete mode 100644 common/pom.xml rename {common => plugin}/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java (85%) rename {common => plugin}/src/main/java/com/lishid/openinv/util/Cache.java (100%) rename {common => plugin}/src/main/java/com/lishid/openinv/util/InternalAccessor.java (100%) create mode 100644 plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java rename {common => plugin}/src/main/java/com/lishid/openinv/util/Permissions.java (100%) create mode 100644 plugin/src/main/resources/en_us.yml diff --git a/api/pom.xml b/api/pom.xml index bcaa09b..8659df8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -43,23 +43,6 @@ - - org.apache.maven.plugins - maven-shade-plugin - 3.0.0 - - true - - - - package - - shade - - - - - maven-compiler-plugin 3.8.1 diff --git a/assembly/pom.xml b/assembly/pom.xml index 26dc354..c470621 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -34,7 +34,7 @@ maven-assembly-plugin - 3.0.0 + 3.2.0 reactor-uberjar diff --git a/common/pom.xml b/common/pom.xml deleted file mode 100644 index bc57138..0000000 --- a/common/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - 4.0.0 - - - com.lishid - openinvparent - 4.0.9-SNAPSHOT - - - openinvcommon - OpenInvCommon - - - - com.lishid - openinvapi - 4.0.9-SNAPSHOT - - - org.spigotmc - spigot-api - 1.8.8-R0.1-SNAPSHOT - provided - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.0.0 - - true - - - - package - - shade - - - - - - - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - - - diff --git a/internal/v1_14_R1/pom.xml b/internal/v1_14_R1/pom.xml index 61c3922..9a7fc9b 100644 --- a/internal/v1_14_R1/pom.xml +++ b/internal/v1_14_R1/pom.xml @@ -37,7 +37,7 @@ com.lishid - openinvcommon + openinvplugincore 4.0.9-SNAPSHOT @@ -47,7 +47,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.0.0 + 3.2.2 true diff --git a/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/AnySilentContainer.java b/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/AnySilentContainer.java index d172fa7..dfa759e 100644 --- a/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/AnySilentContainer.java +++ b/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/AnySilentContainer.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal.v1_14_R1; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import java.lang.reflect.Field; import net.minecraft.server.v1_14_R1.Block; @@ -191,7 +192,7 @@ public class AnySilentContainer implements IAnySilentContainer { InventoryEnderChest enderChest = player.getEnderChest(); enderChest.a((TileEntityEnderChest) tile); player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> { - Containers containers; + Containers containers; int rows = enderChest.getSize() / 9; switch (rows) { case 1: @@ -300,7 +301,7 @@ public class AnySilentContainer implements IAnySilentContainer { if (tile instanceof TileEntityLootable) { TileEntityLootable lootable = (TileEntityLootable) tile; if (lootable.lootTable != null) { - player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true); + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } diff --git a/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/PlayerDataManager.java b/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/PlayerDataManager.java index 882e721..5d6c9d7 100644 --- a/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/PlayerDataManager.java +++ b/internal/v1_14_R1/src/main/java/com/lishid/openinv/internal/v1_14_R1/PlayerDataManager.java @@ -16,16 +16,19 @@ package com.lishid.openinv.internal.v1_14_R1; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.mojang.authlib.GameProfile; import net.minecraft.server.v1_14_R1.ChatComponentText; +import net.minecraft.server.v1_14_R1.ChatMessageType; import net.minecraft.server.v1_14_R1.Container; import net.minecraft.server.v1_14_R1.Containers; import net.minecraft.server.v1_14_R1.DimensionManager; import net.minecraft.server.v1_14_R1.EntityHuman; import net.minecraft.server.v1_14_R1.EntityPlayer; import net.minecraft.server.v1_14_R1.MinecraftServer; +import net.minecraft.server.v1_14_R1.PacketPlayOutChat; import net.minecraft.server.v1_14_R1.PacketPlayOutOpenWindow; import net.minecraft.server.v1_14_R1.PlayerInteractManager; import net.minecraft.server.v1_14_R1.PlayerInventory; @@ -42,9 +45,11 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class PlayerDataManager implements IPlayerDataManager { + @NotNull public static EntityPlayer getHandle(final Player player) { if (player instanceof CraftPlayer) { return ((CraftPlayer) player).getHandle(); @@ -88,26 +93,37 @@ public class PlayerDataManager implements IPlayerDataManager { return target; } - @Override + @Nullable + @Override public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { EntityPlayer nmsPlayer = getHandle(player); - if (nmsPlayer == null || nmsPlayer.playerConnection == null) { + if (nmsPlayer.playerConnection == null) { return null; } String title; if (inventory instanceof SpecialEnderChest) { HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner(); - title = (owner.getName() != null ? owner.getName() : owner.getUniqueId().toString()) + "'s Ender Chest"; + title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest"); + if (title == null) { + title = "%player%'s Ender Chest"; + } + //noinspection ConstantConditions - owner name can be null if loaded by UUID + title = title.replace("%player%", owner.getName() != null ? owner.getName() : owner.getUniqueId().toString()); } else if (inventory instanceof SpecialPlayerInventory) { EntityHuman owner = ((PlayerInventory) inventory).player; - title = (owner.getName() != null ? owner.getName() : owner.getUniqueID().toString()) + "'s Inventory"; + title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player"); + if (title == null) { + title = "%player%'s Inventory"; + } + title = title.replace("%player%", owner.getName() != null ? owner.getName() : owner.getUniqueID().toString()); } else { return player.openInventory(inventory.getBukkitInventory()); } + String finalTitle = title; Container container = new CraftContainer(new InventoryView() { @Override public @NotNull Inventory getTopInventory() { @@ -127,7 +143,7 @@ public class PlayerDataManager implements IPlayerDataManager { } @Override public @NotNull String getTitle() { - return title; + return finalTitle; } }, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override @@ -167,4 +183,24 @@ public class PlayerDataManager implements IPlayerDataManager { } + @Override + public void sendSystemMessage(@NotNull Player player, @NotNull String message) { + int newline = message.indexOf('\n'); + if (newline != -1) { + // No newlines in action bar chat. + message = message.substring(0, newline); + } + + if (message.isEmpty()) { + return; + } + + EntityPlayer nmsPlayer = getHandle(player); + + // For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text. + if (nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO)); + } + } + } diff --git a/internal/v1_15_R1/pom.xml b/internal/v1_15_R1/pom.xml index 0351ef6..4aef51f 100644 --- a/internal/v1_15_R1/pom.xml +++ b/internal/v1_15_R1/pom.xml @@ -37,7 +37,7 @@ com.lishid - openinvcommon + openinvplugincore 4.0.9-SNAPSHOT @@ -47,7 +47,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.0.0 + 3.2.2 true diff --git a/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/AnySilentContainer.java b/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/AnySilentContainer.java index a43637c..e4b58ed 100644 --- a/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/AnySilentContainer.java +++ b/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/AnySilentContainer.java @@ -16,6 +16,7 @@ package com.lishid.openinv.internal.v1_15_R1; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IAnySilentContainer; import java.lang.reflect.Field; import net.minecraft.server.v1_15_R1.Block; @@ -300,7 +301,7 @@ public class AnySilentContainer implements IAnySilentContainer { if (tile instanceof TileEntityLootable) { TileEntityLootable lootable = (TileEntityLootable) tile; if (lootable.lootTable != null) { - player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true); + OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated"); return false; } } diff --git a/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/PlayerDataManager.java b/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/PlayerDataManager.java index 8da163b..25e6deb 100644 --- a/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/PlayerDataManager.java +++ b/internal/v1_15_R1/src/main/java/com/lishid/openinv/internal/v1_15_R1/PlayerDataManager.java @@ -16,16 +16,19 @@ package com.lishid.openinv.internal.v1_15_R1; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.mojang.authlib.GameProfile; import net.minecraft.server.v1_15_R1.ChatComponentText; +import net.minecraft.server.v1_15_R1.ChatMessageType; import net.minecraft.server.v1_15_R1.Container; import net.minecraft.server.v1_15_R1.Containers; import net.minecraft.server.v1_15_R1.DimensionManager; import net.minecraft.server.v1_15_R1.EntityHuman; import net.minecraft.server.v1_15_R1.EntityPlayer; import net.minecraft.server.v1_15_R1.MinecraftServer; +import net.minecraft.server.v1_15_R1.PacketPlayOutChat; import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow; import net.minecraft.server.v1_15_R1.PlayerInteractManager; import net.minecraft.server.v1_15_R1.PlayerInventory; @@ -42,9 +45,11 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class PlayerDataManager implements IPlayerDataManager { + @NotNull public static EntityPlayer getHandle(final Player player) { if (player instanceof CraftPlayer) { return ((CraftPlayer) player).getHandle(); @@ -65,6 +70,7 @@ public class PlayerDataManager implements IPlayerDataManager { return nmsPlayer; } + @Nullable @Override public Player loadPlayer(@NotNull final OfflinePlayer offline) { // Ensure player has data @@ -88,26 +94,37 @@ public class PlayerDataManager implements IPlayerDataManager { return target; } - @Override + @Nullable + @Override public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { EntityPlayer nmsPlayer = getHandle(player); - if (nmsPlayer == null || nmsPlayer.playerConnection == null) { + if (nmsPlayer.playerConnection == null) { return null; } String title; if (inventory instanceof SpecialEnderChest) { HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner(); - title = (owner.getName() != null ? owner.getName() : owner.getUniqueId().toString()) + "'s Ender Chest"; + title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest"); + if (title == null) { + title = "%player%'s Ender Chest"; + } + //noinspection ConstantConditions - owner name can be null if loaded by UUID + title = title.replace("%player%", owner.getName() != null ? owner.getName() : owner.getUniqueId().toString()); } else if (inventory instanceof SpecialPlayerInventory) { EntityHuman owner = ((PlayerInventory) inventory).player; - title = (owner.getName() != null ? owner.getName() : owner.getUniqueID().toString()) + "'s Inventory"; + title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player"); + if (title == null) { + title = "%player%'s Inventory"; + } + title = title.replace("%player%", owner.getName() != null ? owner.getName() : owner.getUniqueID().toString()); } else { return player.openInventory(inventory.getBukkitInventory()); } + String finalTitle = title; Container container = new CraftContainer(new InventoryView() { @Override public @NotNull Inventory getTopInventory() { @@ -127,7 +144,7 @@ public class PlayerDataManager implements IPlayerDataManager { } @Override public @NotNull String getTitle() { - return title; + return finalTitle; } }, nmsPlayer, nmsPlayer.nextContainerCounter()) { @Override @@ -167,4 +184,24 @@ public class PlayerDataManager implements IPlayerDataManager { } + @Override + public void sendSystemMessage(@NotNull Player player, @NotNull String message) { + int newline = message.indexOf('\n'); + if (newline != -1) { + // No newlines in action bar chat. + message = message.substring(0, newline); + } + + if (message.isEmpty()) { + return; + } + + EntityPlayer nmsPlayer = getHandle(player); + + // For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text. + if (nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO)); + } + } + } diff --git a/internal/v1_8_R3/pom.xml b/internal/v1_8_R3/pom.xml index a4f9086..c4e587c 100644 --- a/internal/v1_8_R3/pom.xml +++ b/internal/v1_8_R3/pom.xml @@ -36,7 +36,7 @@ com.lishid - openinvcommon + openinvplugincore 4.0.9-SNAPSHOT @@ -46,7 +46,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.0.0 + 3.2.2 true diff --git a/internal/v1_8_R3/src/main/java/com/lishid/openinv/internal/v1_8_R3/PlayerDataManager.java b/internal/v1_8_R3/src/main/java/com/lishid/openinv/internal/v1_8_R3/PlayerDataManager.java index a14df88..7153515 100644 --- a/internal/v1_8_R3/src/main/java/com/lishid/openinv/internal/v1_8_R3/PlayerDataManager.java +++ b/internal/v1_8_R3/src/main/java/com/lishid/openinv/internal/v1_8_R3/PlayerDataManager.java @@ -19,8 +19,10 @@ package com.lishid.openinv.internal.v1_8_R3; import com.lishid.openinv.internal.IPlayerDataManager; import com.lishid.openinv.internal.ISpecialInventory; import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_8_R3.ChatComponentText; import net.minecraft.server.v1_8_R3.EntityPlayer; import net.minecraft.server.v1_8_R3.MinecraftServer; +import net.minecraft.server.v1_8_R3.PacketPlayOutChat; import net.minecraft.server.v1_8_R3.PlayerInteractManager; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -30,9 +32,32 @@ import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryView; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class PlayerDataManager implements IPlayerDataManager { + @NotNull + public static EntityPlayer getHandle(Player player) { + if (player instanceof CraftPlayer) { + return ((CraftPlayer) player).getHandle(); + } + + Server server = player.getServer(); + EntityPlayer nmsPlayer = null; + + if (server instanceof CraftServer) { + nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName()); + } + + if (nmsPlayer == null) { + // Could use reflection to examine fields, but it's honestly not worth the bother. + throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); + } + + return nmsPlayer; + } + + @Nullable @Override public Player loadPlayer(@NotNull OfflinePlayer offline) { // Ensure the player has data @@ -56,29 +81,36 @@ public class PlayerDataManager implements IPlayerDataManager { return target; } - public static EntityPlayer getHandle(Player player) { - if (player instanceof CraftPlayer) { - return ((CraftPlayer) player).getHandle(); - } - - Server server = player.getServer(); - EntityPlayer nmsPlayer = null; - - if (server instanceof CraftServer) { - nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName()); - } - - if (nmsPlayer == null) { - // Could use reflection to examine fields, but it's honestly not worth the bother. - throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation"); - } - - return nmsPlayer; - } - - @Override + @Nullable + @Override public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) { return player.openInventory(inventory.getBukkitInventory()); } + @Override + public void sendSystemMessage(@NotNull Player player, @NotNull String message) { + int newline = message.indexOf('\n'); + if (newline != -1) { + // No newlines in action bar chat. + message = message.substring(0, newline); + } + + if (message.isEmpty()) { + return; + } + + EntityPlayer nmsPlayer = getHandle(player); + + // For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text. + if (nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), (byte) 2)); + } + } + + @NotNull + @Override + public String getLocale(Player player) { + return getHandle(player).locale; + } + } diff --git a/plugin/pom.xml b/plugin/pom.xml index dbfa04f..e695d91 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -30,13 +30,13 @@ com.lishid - openinvcommon + openinvapi 4.0.9-SNAPSHOT org.spigotmc spigot-api - 1.8.8-R0.1-SNAPSHOT + 1.15.2-R0.1-SNAPSHOT provided @@ -52,7 +52,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.0.0 + 3.2.2 true diff --git a/plugin/src/main/java/com/lishid/openinv/OpenInv.java b/plugin/src/main/java/com/lishid/openinv/OpenInv.java index 78a7f54..151ad67 100644 --- a/plugin/src/main/java/com/lishid/openinv/OpenInv.java +++ b/plugin/src/main/java/com/lishid/openinv/OpenInv.java @@ -35,6 +35,7 @@ import com.lishid.openinv.listeners.PluginListener; import com.lishid.openinv.util.Cache; import com.lishid.openinv.util.ConfigUpdater; import com.lishid.openinv.util.InternalAccessor; +import com.lishid.openinv.util.LanguageManager; import com.lishid.openinv.util.Permissions; import java.util.HashMap; import java.util.Iterator; @@ -45,6 +46,7 @@ import java.util.concurrent.Future; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.entity.HumanEntity; @@ -107,6 +109,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { }); private InternalAccessor accessor; + private LanguageManager languageManager; /** * Evicts all viewers lacking cross-world permissions from a Player's inventory. @@ -128,8 +131,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { HumanEntity human = iterator.next(); // If player has permission or is in the same world, allow continued access // Just in case, also allow null worlds. - if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld() == null - || human.getWorld().equals(player.getWorld())) { + if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) { continue; } human.closeInventory(); @@ -140,8 +142,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv { Iterator iterator = this.enderChests.get(key).getBukkitInventory().getViewers().iterator(); while (iterator.hasNext()) { HumanEntity human = iterator.next(); - if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld() == null - || human.getWorld().equals(player.getWorld())) { + if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) { continue; } human.closeInventory(); @@ -284,6 +285,43 @@ public class OpenInv extends JavaPlugin implements IOpenInv { return this.accessor.getPlayerDataManager().openInventory(player, inventory); } + public void sendMessage(@NotNull CommandSender sender, @NotNull String key) { + String message = this.languageManager.getValue(key, getLocale(sender)); + + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); + } + } + + public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) { + String message = this.languageManager.getValue(key, getLocale(sender), replacements); + + if (message != null && !message.isEmpty()) { + sender.sendMessage(message); + } + } + + public void sendSystemMessage(@NotNull Player player, @NotNull String key) { + String message = this.languageManager.getValue(key, getLocale(player)); + + if (message != null) { + this.accessor.getPlayerDataManager().sendSystemMessage(player, message); + } + } + + public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) { + return this.languageManager.getValue(key, getLocale(sender)); + } + + @Nullable + private String getLocale(@NotNull CommandSender sender) { + if (sender instanceof Player) { + return this.accessor.getPlayerDataManager().getLocale((Player) sender); + } else { + return this.getConfig().getString("settings.locale", "en_us"); + } + } + @Override public boolean notifyAnyChest() { return this.getConfig().getBoolean("notify.any-chest", true); @@ -317,6 +355,8 @@ public class OpenInv extends JavaPlugin implements IOpenInv { this.accessor = new InternalAccessor(this); + this.languageManager = new LanguageManager(this, "en_us"); + // Version check if (this.accessor.isSupported()) { // Update existing configuration. May require internal access. @@ -332,16 +372,16 @@ public class OpenInv extends JavaPlugin implements IOpenInv { // Register commands to their executors OpenInvCommand openInv = new OpenInvCommand(this); - this.getCommand("openinv").setExecutor(openInv); - this.getCommand("openender").setExecutor(openInv); + this.setCommandExecutor("openinv", openInv); + this.setCommandExecutor("openender", openInv); + this.setCommandExecutor("searchcontainer", new SearchContainerCommand(this)); SearchInvCommand searchInv = new SearchInvCommand(this); - this.getCommand("searchcontainer").setExecutor(new SearchContainerCommand()); - this.getCommand("searchinv").setExecutor(searchInv); - this.getCommand("searchender").setExecutor(searchInv); - this.getCommand("searchenchant").setExecutor(new SearchEnchantCommand(this)); + this.setCommandExecutor("searchinv", searchInv); + this.setCommandExecutor("searchender", searchInv); + this.setCommandExecutor("searchenchant", new SearchEnchantCommand(this)); ContainerSettingCommand settingCommand = new ContainerSettingCommand(this); - this.getCommand("silentcontainer").setExecutor(settingCommand); - this.getCommand("anycontainer").setExecutor(settingCommand); + this.setCommandExecutor("silentcontainer", settingCommand); + this.setCommandExecutor("anycontainer", settingCommand); } else { this.getLogger().info("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported."); @@ -351,12 +391,18 @@ public class OpenInv extends JavaPlugin implements IOpenInv { } + private void setCommandExecutor(String commandName, CommandExecutor executor) { + PluginCommand command = this.getCommand(commandName); + if (command != null) { + command.setExecutor(executor); + } + } + @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!this.accessor.isSupported()) { sender.sendMessage("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported."); sender.sendMessage("If this version is a recent release, check for an update."); - sender.sendMessage("If this is an older version, ensure that you've downloaded the legacy support version."); return true; } return false; diff --git a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java index eb14e8b..09ed34c 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/ContainerSettingCommand.java @@ -22,12 +22,12 @@ import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Function; -import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; public class ContainerSettingCommand implements TabExecutor { @@ -38,15 +38,14 @@ public class ContainerSettingCommand implements TabExecutor { } @Override - public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "You can't use this from the console."); + plugin.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } Player player = (Player) sender; boolean any = command.getName().startsWith("any"); - String commandName = any ? "AnyContainer" : "SilentContainer"; Function getSetting = any ? plugin::getPlayerAnyChestStatus : plugin::getPlayerSilentChestStatus; BiConsumer setSetting = any ? plugin::setPlayerAnyChestStatus : plugin::setPlayerSilentChestStatus; @@ -66,13 +65,18 @@ public class ContainerSettingCommand implements TabExecutor { setSetting.accept(player, !getSetting.apply(player)); } - sender.sendMessage(commandName + " is now " + (getSetting.apply(player) ? "ON" : "OFF") + "."); + String onOff = plugin.getLocalizedMessage(player, getSetting.apply(player) ? "messages.info.on" : "messages.info.off"); + if (onOff == null) { + onOff = String.valueOf(getSetting.apply(player)); + } + + plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff); return true; } @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!command.testPermissionSilent(sender) || args.length != 1) { return Collections.emptyList(); } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java index 8c20be9..f8246c5 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/OpenInvCommand.java @@ -23,13 +23,13 @@ import com.lishid.openinv.util.TabCompleter; import java.util.Collections; import java.util.HashMap; import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; public class OpenInvCommand implements TabExecutor { @@ -42,9 +42,9 @@ public class OpenInvCommand implements TabExecutor { } @Override - public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { + public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, @NotNull final String[] args) { if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "You can't use this from the console."); + plugin.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } @@ -79,7 +79,7 @@ public class OpenInvCommand implements TabExecutor { final OfflinePlayer offlinePlayer = OpenInvCommand.this.plugin.matchPlayer(name); if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) { - player.sendMessage(ChatColor.RED + "Player not found!"); + plugin.sendMessage(player, "messages.error.invalidPlayer"); return; } @@ -100,49 +100,48 @@ public class OpenInvCommand implements TabExecutor { } private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) { - - Player onlineTarget; boolean online = target.isOnline(); if (!online) { // Try loading the player's data onlineTarget = this.plugin.loadPlayer(target); - - if (onlineTarget == null) { - player.sendMessage(ChatColor.RED + "Player not found!"); - return; - } } else { onlineTarget = target.getPlayer(); } + if (onlineTarget == null) { + plugin.sendMessage(player, "messages.error.invalidPlayer"); + return; + } + // Permissions checks if (onlineTarget.equals(player)) { // Inventory: Additional permission required to open own inventory if (openinv && !Permissions.OPENSELF.hasPermission(player)) { - player.sendMessage(ChatColor.RED + "You're not allowed to open your own inventory!"); + plugin.sendMessage(player, "messages.error.permissionOpenSelf"); return; } } else { // Enderchest: Additional permission required to open others' ender chests if (!openinv && !Permissions.ENDERCHEST_ALL.hasPermission(player)) { - player.sendMessage(ChatColor.RED + "You do not have permission to access other players' ender chests."); + plugin.sendMessage(player, "messages.error.permissionEnderAll"); return; } // Protected check if (!Permissions.OVERRIDE.hasPermission(player) && Permissions.EXEMPT.hasPermission(onlineTarget)) { - player.sendMessage(ChatColor.RED + onlineTarget.getDisplayName() + "'s inventory is protected!"); + plugin.sendMessage(player, "messages.error.permissionExempt", + "%target%", onlineTarget.getDisplayName()); return; } // Crossworld check if (!Permissions.CROSSWORLD.hasPermission(player) && !onlineTarget.getWorld().equals(player.getWorld())) { - player.sendMessage( - ChatColor.RED + onlineTarget.getDisplayName() + " is not in your world!"); + plugin.sendMessage(player, "messages.error.permissionCrossWorld", + "%target%", onlineTarget.getDisplayName()); return; } } @@ -155,7 +154,7 @@ public class OpenInvCommand implements TabExecutor { try { inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online); } catch (Exception e) { - player.sendMessage(ChatColor.RED + "An error occurred creating " + onlineTarget.getDisplayName() + "'s inventory!"); + plugin.sendMessage(player, "messages.error.commandException"); e.printStackTrace(); return; } @@ -165,7 +164,7 @@ public class OpenInvCommand implements TabExecutor { } @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!command.testPermissionSilent(sender) || args.length != 1) { return Collections.emptyList(); } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java index d95d3d3..7242ee9 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchContainerCommand.java @@ -16,10 +16,10 @@ package com.lishid.openinv.commands; +import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; import java.util.Collections; import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; @@ -29,16 +29,23 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; /** * Command for searching containers in a radius of chunks. */ public class SearchContainerCommand implements TabExecutor { + private final OpenInv plugin; + + public SearchContainerCommand(OpenInv plugin) { + this.plugin = plugin; + } + @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!(sender instanceof Player)) { - sender.sendMessage(ChatColor.RED + "You can't use this from the console."); + plugin.sendMessage(sender, "messages.error.consoleUnsupported"); return true; } @@ -47,10 +54,10 @@ public class SearchContainerCommand implements TabExecutor { return false; } - Material material = Material.getMaterial(args[0]); + Material material = Material.getMaterial(args[0].toUpperCase()); if (material == null) { - sender.sendMessage(ChatColor.RED + "Unknown item: \"" + args[0] + "\""); + plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args[0]); return false; } @@ -95,15 +102,18 @@ public class SearchContainerCommand implements TabExecutor { if (locations.length() > 0) { locations.delete(locations.length() - 2, locations.length()); } else { - sender.sendMessage("No containers found with " + material.toString()); + plugin.sendMessage(sender, "messages.info.container.noMatches", + "%target%", material.name()); + return true; } - sender.sendMessage("Containers holding item " + material.toString() + ": " + locations.toString()); + plugin.sendMessage(sender, "messages.info.container.matches", + "%target%", material.name(), "%detail%", locations.toString()); return true; } @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { return Collections.emptyList(); } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java index c755185..9e4206d 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchEnchantCommand.java @@ -21,6 +21,7 @@ import com.lishid.openinv.util.TabCompleter; import java.util.Collections; import java.util.List; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -29,6 +30,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; /** * Command adding the ability to search online players' inventories for enchantments of a specific @@ -45,7 +47,7 @@ public class SearchEnchantCommand implements TabExecutor { } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (args.length == 0) { return false; } @@ -54,14 +56,28 @@ public class SearchEnchantCommand implements TabExecutor { int level = 0; for (String argument : args) { - Enchantment localEnchant = Enchantment.getByName(argument.toUpperCase()); - if (localEnchant != null) { - enchant = localEnchant; - continue; - } try { level = Integer.parseInt(argument); + continue; } catch (NumberFormatException ignored) {} + + argument = argument.toLowerCase(); + int colon = argument.indexOf(':'); + NamespacedKey key; + try { + if (colon > -1 && colon < argument.length() - 1) { + key = new NamespacedKey(argument.substring(0, colon), argument.substring(colon + 1)); + } else { + key = NamespacedKey.minecraft(argument); + } + } catch (IllegalArgumentException ignored) { + continue; + } + + Enchantment localEnchant = Enchantment.getByKey(key); + if (localEnchant != null) { + enchant = localEnchant; + } } // Arguments not set correctly @@ -97,12 +113,14 @@ public class SearchEnchantCommand implements TabExecutor { // Matches found, delete trailing comma and space players.delete(players.length() - 2, players.length()); } else { - sender.sendMessage("No players found with " + (enchant == null ? "any enchant" : enchant.getName()) - + " of level " + level + " or higher."); + plugin.sendMessage(sender, "messages.info.player.noMatches", + "%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level); return true; } - sender.sendMessage("Players: " + players.toString()); + plugin.sendMessage(sender, "messages.info.player.matches", + "%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level, + "%detail%", players.toString()); return true; } @@ -120,7 +138,7 @@ public class SearchEnchantCommand implements TabExecutor { continue; } ItemMeta meta = item.getItemMeta(); - if (!meta.hasEnchants()) { + if (meta == null || !meta.hasEnchants()) { continue; } for (int enchLevel : meta.getEnchants().values()) { @@ -134,13 +152,13 @@ public class SearchEnchantCommand implements TabExecutor { } @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (!command.testPermissionSilent(sender) || args.length < 1 || args.length > 2) { return Collections.emptyList(); } if (args.length == 1) { - return TabCompleter.completeObject(args[0], Enchantment::getName, Enchantment.values()); + return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Enchantment.values()); } else { return TabCompleter.completeInteger(args[1]); } diff --git a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java index c92b064..86d20d2 100644 --- a/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java +++ b/plugin/src/main/java/com/lishid/openinv/commands/SearchInvCommand.java @@ -20,13 +20,13 @@ import com.lishid.openinv.OpenInv; import com.lishid.openinv.util.TabCompleter; import java.util.Collections; import java.util.List; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; public class SearchInvCommand implements TabExecutor { @@ -37,32 +37,33 @@ public class SearchInvCommand implements TabExecutor { } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { Material material = null; int count = 1; if (args.length >= 1) { - material = Material.getMaterial(args[0]); + material = Material.getMaterial(args[0].toUpperCase()); } if (args.length >= 2) { try { count = Integer.parseInt(args[1]); } catch (NumberFormatException ex) { - sender.sendMessage(ChatColor.RED + "'" + args[1] + "' is not a number!"); + plugin.sendMessage(sender, "messages.error.invalidNumber", "%target%", args[1]); return false; } } if (material == null) { - sender.sendMessage(ChatColor.RED + "Unknown item: \"" + args[0] + "\""); + plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args[0]); return false; } StringBuilder players = new StringBuilder(); + boolean searchInv = command.getName().equals("searchinv"); for (Player player : plugin.getServer().getOnlinePlayers()) { - Inventory inventory = command.getName().equals("searchinv") ? player.getInventory() : player.getEnderChest(); + Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest(); if (inventory.contains(material, count)) { players.append(player.getName()).append(", "); } @@ -72,15 +73,18 @@ public class SearchInvCommand implements TabExecutor { if (players.length() > 0) { players.delete(players.length() - 2, players.length()); } else { - sender.sendMessage("No players found with " + material.toString()); + plugin.sendMessage(sender, "messages.info.player.noMatches", + "%target%", material.name()); + return true; } - sender.sendMessage("Players with the item " + material.toString() + ": " + players.toString()); + plugin.sendMessage(sender, "messages.info.player.matches", + "%target%", material.name(), "%detail%", players.toString()); return true; } @Override - public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) { return Collections.emptyList(); } diff --git a/common/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java similarity index 85% rename from common/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java rename to plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java index 752ebb8..a801490 100644 --- a/common/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java +++ b/plugin/src/main/java/com/lishid/openinv/internal/IPlayerDataManager.java @@ -32,7 +32,8 @@ public interface IPlayerDataManager { * @param offline the OfflinePlayer * @return the Player loaded */ - @Nullable Player loadPlayer(@NotNull OfflinePlayer offline); + @Nullable + Player loadPlayer(@NotNull OfflinePlayer offline); /** * Opens an ISpecialInventory for a Player. @@ -42,6 +43,14 @@ public interface IPlayerDataManager { *` * @return the InventoryView opened */ + @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory); + void sendSystemMessage(@NotNull Player player, @NotNull String message); + + @NotNull + default String getLocale(Player player) { + return player.getLocale(); + } + } diff --git a/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java b/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java index 390a65c..7bbead2 100644 --- a/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java +++ b/plugin/src/main/java/com/lishid/openinv/listeners/PlayerListener.java @@ -55,7 +55,7 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking() - || event.useInteractedBlock() == Result.DENY + || event.useInteractedBlock() == Result.DENY || event.getClickedBlock() == null || !plugin.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) { return; } @@ -71,13 +71,15 @@ public class PlayerListener implements Listener { boolean silent = Permissions.SILENT.hasPermission(player) && plugin.getPlayerSilentChestStatus(player); // If anycontainer or silentcontainer is active - if ((any || silent) && plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) { - if (silent && plugin.notifySilentChest() && needsAny && plugin.notifyAnyChest()) { - player.sendMessage("You are opening a blocked container silently."); - } else if (silent && plugin.notifySilentChest()) { - player.sendMessage("You are opening a container silently."); - } else if (needsAny && plugin.notifyAnyChest()) { - player.sendMessage("You are opening a blocked container."); + if (any || silent) { + if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) { + if (silent && plugin.notifySilentChest() && needsAny && plugin.notifyAnyChest()) { + plugin.sendSystemMessage(player, "messages.info.containerBlockedSilent"); + } else if (needsAny && plugin.notifyAnyChest()) { + plugin.sendSystemMessage(player, "messages.info.containerBlocked"); + } else if (silent && plugin.notifySilentChest()) { + plugin.sendSystemMessage(player, "messages.info.containerSilent"); + } } event.setCancelled(true); } diff --git a/common/src/main/java/com/lishid/openinv/util/Cache.java b/plugin/src/main/java/com/lishid/openinv/util/Cache.java similarity index 100% rename from common/src/main/java/com/lishid/openinv/util/Cache.java rename to plugin/src/main/java/com/lishid/openinv/util/Cache.java diff --git a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java index c07beaa..082427b 100644 --- a/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java +++ b/plugin/src/main/java/com/lishid/openinv/util/ConfigUpdater.java @@ -59,6 +59,9 @@ public class ConfigUpdater { if (version < 3) { updateConfig2To3(); } + if (version < 4) { + updateConfig3To4(); + } new BukkitRunnable() { @Override @@ -71,6 +74,17 @@ public class ConfigUpdater { }.runTaskAsynchronously(plugin); } + private void updateConfig3To4() { + new BukkitRunnable() { + @Override + public void run() { + plugin.getConfig().set("notify", null); + plugin.getConfig().set("settings.locale", "en_US"); + plugin.getConfig().set("config-version", 4); + } + }.runTask(plugin); + } + private void updateConfig2To3() { new BukkitRunnable() { @Override diff --git a/common/src/main/java/com/lishid/openinv/util/InternalAccessor.java b/plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java similarity index 100% rename from common/src/main/java/com/lishid/openinv/util/InternalAccessor.java rename to plugin/src/main/java/com/lishid/openinv/util/InternalAccessor.java diff --git a/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java b/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java new file mode 100644 index 0000000..d0a51a5 --- /dev/null +++ b/plugin/src/main/java/com/lishid/openinv/util/LanguageManager.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011-2020 Jikoo. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lishid.openinv.util; + +import com.lishid.openinv.OpenInv; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A simple language manager supporting both custom and bundled languages. + * + * @author Jikoo + */ +public class LanguageManager { + + private final OpenInv plugin; + private final String defaultLocale; + private Map locales; + + public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) { + this.plugin = plugin; + this.defaultLocale = defaultLocale; + this.locales = new HashMap<>(); + getOrLoadLocale(defaultLocale); + } + + private YamlConfiguration getOrLoadLocale(@NotNull String locale) { + YamlConfiguration loaded = locales.get(locale); + if (loaded != null) { + return loaded; + } + + InputStream resourceStream = plugin.getResource(locale + ".yml"); + YamlConfiguration localeConfigDefaults; + if (resourceStream == null) { + localeConfigDefaults = new YamlConfiguration(); + } else { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) { + localeConfigDefaults = YamlConfiguration.loadConfiguration(reader); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to load resource " + locale + ".yml", e); + localeConfigDefaults = new YamlConfiguration(); + } + } + + File file = new File(plugin.getDataFolder(), locale + ".yml"); + YamlConfiguration localeConfig; + + if (!file.exists()) { + localeConfig = localeConfigDefaults; + try { + localeConfigDefaults.save(file); + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e); + } + } else { + localeConfig = YamlConfiguration.loadConfiguration(file); + + // Add new language keys + List newKeys = new ArrayList<>(); + for (String key : localeConfigDefaults.getKeys(true)) { + if (localeConfigDefaults.isConfigurationSection(key)) { + continue; + } + + if (localeConfig.isSet(key)) { + continue; + } + + localeConfig.set(key, localeConfigDefaults.get(key)); + newKeys.add(key); + } + + if (!newKeys.isEmpty()) { + plugin.getLogger().info("[LanguageManager] Added new language keys: " + String.join(", ", newKeys)); + } + } + + if (!locale.equals(defaultLocale)) { + localeConfigDefaults = locales.get(defaultLocale); + + // Check for missing keys + List newKeys = new ArrayList<>(); + for (String key : localeConfigDefaults.getKeys(true)) { + if (localeConfigDefaults.isConfigurationSection(key)) { + continue; + } + + if (localeConfig.isSet(key)) { + continue; + } + + newKeys.add(key); + } + + if (!newKeys.isEmpty()) { + plugin.getLogger().info("[LanguageManager] Missing translations from " + locale + ".yml: " + String.join(", ", newKeys)); + } + + // Fall through to default locale + localeConfig.setDefaults(localeConfigDefaults); + } + + locales.put(locale, localeConfig); + return localeConfig; + } + + @Nullable + public String getValue(@NotNull String key, @Nullable String locale) { + String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key); + if (value == null || value.isEmpty()) { + return null; + } + + value = ChatColor.translateAlternateColorCodes('&', value); + + return value; + } + + @Nullable + public String getValue(@NotNull String key, @Nullable String locale, @NotNull String... replacements) { + if (replacements.length % 2 != 0) { + plugin.getLogger().log(Level.WARNING, "[LanguageManager] Replacement data is uneven", new Exception()); + } + + String value = getValue(key, locale); + + if (value == null) { + return null; + } + + for (int i = 0; i < replacements.length; i += 2) { + value = value.replace(replacements[i], replacements[i + 1]); + } + + return value; + } + +} diff --git a/common/src/main/java/com/lishid/openinv/util/Permissions.java b/plugin/src/main/java/com/lishid/openinv/util/Permissions.java similarity index 100% rename from common/src/main/java/com/lishid/openinv/util/Permissions.java rename to plugin/src/main/java/com/lishid/openinv/util/Permissions.java diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 35f924c..d8bc7bb 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -1,6 +1,4 @@ -config-version: 3 -notify: - any-chest: true - silent-chest: true +config-version: 4 settings: disable-saving: false + locale: 'en_us' diff --git a/plugin/src/main/resources/en_us.yml b/plugin/src/main/resources/en_us.yml new file mode 100644 index 0000000..8ea3b42 --- /dev/null +++ b/plugin/src/main/resources/en_us.yml @@ -0,0 +1,28 @@ +messages: + error: + consoleUnsupported: 'You cannot use this command from console.' + lootNotGenerated: '&cLoot not generated! Please disable &b/silentcontainer&c.' + invalidMaterial: '&cInvalid material: "%target%"' + invalidNumber: '&cInvalid number: "%target%"' + invalidPlayer: '&cPlayer not found!' + permissionOpenSelf: '&cYou''re not allowed to open your own inventory.' + permissionEnderAll: '&cYou''re not allowed to access other players'' ender chests.' + permissionExempt: '&c%target%''s inventory is protected.' + permissionCrossWorld: '&c%target% is not in your world.' + commandException: '&cAn error occurred. Please check console for details.' + info: + containerBlocked: 'You are opening a blocked container.' + containerBlockedSilent: 'You are opening a blocked container silently.' + containerSilent: 'You are opening a container silently.' + settingState: '%setting%: %state%' + player: + noMatches: 'No players found with %target%.' + matches: 'Players holding %target%: %detail%' + container: + noMatches: 'No containers found with %target%.' + matches: 'Containers holding %target%: %detail%' + on: 'on' + off: 'off' +container: + player: '%player%''s Inventory' + enderchest: '%Player''s Ender Chest' diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 598e14f..61606b4 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -5,7 +5,7 @@ author: lishid authors: [Jikoo, ShadowRanger] description: > This plugin allows you to open a player's inventory as a chest and interact with it in real time. -api-version: "1.13" +api-version: "1.14" permissions: OpenInv.any.default: @@ -24,7 +24,7 @@ permissions: OpenInv.silent: true OpenInv.anychest: true OpenInv.searchenchant: true - OpenInv.searchcontainer + OpenInv.searchcontainer: true commands: openinv: diff --git a/pom.xml b/pom.xml index 959b6c3..9e45336 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,6 @@ api - common plugin internal assembly