diff --git a/src/main/java/net/knarcraft/stargatecommand/StargateCommand.java b/src/main/java/net/knarcraft/stargatecommand/StargateCommand.java index 4598d3a..6f6dbe1 100644 --- a/src/main/java/net/knarcraft/stargatecommand/StargateCommand.java +++ b/src/main/java/net/knarcraft/stargatecommand/StargateCommand.java @@ -4,7 +4,10 @@ import net.TheDgtl.Stargate.api.StargateAPI; import net.TheDgtl.Stargate.config.ConfigurationOption; import net.knarcraft.stargatecommand.command.CommandStarGateCommand; import net.knarcraft.stargatecommand.command.StargateCommandTabCompleter; +import net.knarcraft.stargatecommand.formatting.Translator; +import net.knarcraft.stargatecommand.listener.StargateListener; import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicesManager; import org.bukkit.plugin.java.JavaPlugin; @@ -18,6 +21,7 @@ import java.util.List; @SuppressWarnings("unused") public class StargateCommand extends JavaPlugin { + private static StargateCommand instance; private List bannedConfigOptions; @Override @@ -26,6 +30,8 @@ public class StargateCommand extends JavaPlugin { if (bannedConfigOptions == null) { initializeBannedConfigOptions(); } + instance = this; + Translator.loadLanguages("en"); //Get the Stargate API ServicesManager servicesManager = this.getServer().getServicesManager(); RegisteredServiceProvider stargateProvider = servicesManager.getRegistration(StargateAPI.class); @@ -38,6 +44,8 @@ public class StargateCommand extends JavaPlugin { stargateCommand.setExecutor(new CommandStarGateCommand(stargateAPI, bannedConfigOptions)); stargateCommand.setTabCompleter(new StargateCommandTabCompleter(stargateAPI, bannedConfigOptions)); } + PluginManager pluginManager = getServer().getPluginManager(); + pluginManager.registerEvents(new StargateListener(), this); } else { throw new IllegalStateException("Unable to hook into Stargate. Make sure the Stargate plugin is installed " + "and enabled."); @@ -49,6 +57,15 @@ public class StargateCommand extends JavaPlugin { //Currently, nothing needs to be disabled } + /** + * Gets an instance of this plugin + * + * @return

An instance of this plugin

+ */ + public static StargateCommand getInstance() { + return instance; + } + /** * Initializes the list of banned configuration options * diff --git a/src/main/java/net/knarcraft/stargatecommand/command/CommandConfig.java b/src/main/java/net/knarcraft/stargatecommand/command/CommandConfig.java index b69e3ad..58a386d 100644 --- a/src/main/java/net/knarcraft/stargatecommand/command/CommandConfig.java +++ b/src/main/java/net/knarcraft/stargatecommand/command/CommandConfig.java @@ -3,6 +3,9 @@ package net.knarcraft.stargatecommand.command; import net.TheDgtl.Stargate.config.ConfigurationAPI; import net.TheDgtl.Stargate.config.ConfigurationOption; import net.TheDgtl.Stargate.config.OptionDataType; +import net.knarcraft.stargatecommand.formatting.StringFormatter; +import net.knarcraft.stargatecommand.formatting.TranslatableMessage; +import net.knarcraft.stargatecommand.formatting.Translator; import net.md_5.bungee.api.ChatColor; import org.apache.commons.lang.StringUtils; import org.bukkit.command.Command; @@ -14,6 +17,9 @@ import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.List; +import static net.knarcraft.stargatecommand.formatting.StringFormatter.getTranslatedErrorMessage; +import static net.knarcraft.stargatecommand.formatting.StringFormatter.getTranslatedInfoMessage; + /** * This command represents the config command for changing config values */ @@ -40,7 +46,7 @@ public class CommandConfig implements CommandExecutor { @NotNull String[] args) { if (commandSender instanceof Player player) { if (!player.hasPermission("stargate.command.config")) { - player.sendMessage("Permission Denied"); + player.sendMessage(getTranslatedErrorMessage(TranslatableMessage.PERMISSION_DENIED)); return true; } } @@ -53,7 +59,7 @@ public class CommandConfig implements CommandExecutor { return true; } } catch (IllegalArgumentException exception) { - commandSender.sendMessage("Invalid configuration option specified"); + commandSender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.INVALID_CONFIGURATION_OPTION)); return true; } if (args.length > 1) { @@ -83,7 +89,8 @@ public class CommandConfig implements CommandExecutor { try { ChatColor.of(value.toUpperCase()); } catch (IllegalArgumentException | NullPointerException ignored) { - commandSender.sendMessage(ChatColor.RED + "Invalid color given"); + commandSender.sendMessage(StringFormatter.replacePlaceholder(getTranslatedErrorMessage( + TranslatableMessage.INVALID_DATATYPE_GIVEN), "{datatype}", "color")); return; } } @@ -135,7 +142,8 @@ public class CommandConfig implements CommandExecutor { */ private boolean checkIfValueMatchesDatatype(OptionDataType dataType, String value, CommandSender commandSender) { if (!matchesOptionDataType(dataType, value)) { - commandSender.sendMessage(String.format("Invalid %s given", + commandSender.sendMessage(StringFormatter.replacePlaceholder(getTranslatedErrorMessage( + TranslatableMessage.INVALID_DATATYPE_GIVEN), "{datatype}", dataType.name().toLowerCase().replace('_', ' '))); return false; } else { @@ -162,7 +170,7 @@ public class CommandConfig implements CommandExecutor { private void saveAndReload(CommandSender commandSender) { configurationAPI.saveConfiguration(); configurationAPI.reload(); - commandSender.sendMessage("Config updated"); + commandSender.sendMessage(getTranslatedInfoMessage(TranslatableMessage.CONFIG_UPDATED)); } /** @@ -178,13 +186,13 @@ public class CommandConfig implements CommandExecutor { int intValue = Integer.parseInt(value); if ((selectedOption == ConfigurationOption.USE_COST || selectedOption == ConfigurationOption.CREATION_COST) && intValue < 0) { - commandSender.sendMessage(ChatColor.RED + "This config option cannot be negative."); + commandSender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.POSITIVE_NUMBER_REQUIRED)); return null; } return intValue; } catch (NumberFormatException exception) { - commandSender.sendMessage(ChatColor.RED + "Invalid number given"); + commandSender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.INVALID_NUMBER_GIVEN)); return null; } } @@ -202,13 +210,13 @@ public class CommandConfig implements CommandExecutor { double doubleValue = Double.parseDouble(value); if (selectedOption == ConfigurationOption.GATE_EXIT_SPEED_MULTIPLIER && doubleValue < 0) { - commandSender.sendMessage(ChatColor.RED + "This config option cannot be negative."); + commandSender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.POSITIVE_NUMBER_REQUIRED)); return null; } return doubleValue; } catch (NumberFormatException exception) { - commandSender.sendMessage(ChatColor.RED + "Invalid number given"); + commandSender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.INVALID_NUMBER_GIVEN)); return null; } } @@ -222,7 +230,8 @@ public class CommandConfig implements CommandExecutor { private void printConfigOptionValue(CommandSender sender, ConfigurationOption option) { Object value = configurationAPI.getConfigurationOptionValue(option); sender.sendMessage(getOptionDescription(option)); - sender.sendMessage(ChatColor.GREEN + "Current value: " + ChatColor.GOLD + value); + sender.sendMessage(StringFormatter.replacePlaceholder(Translator.getTranslatedMessage( + TranslatableMessage.CONFIG_OPTION_CURRENT_VALUE), "{value}", String.valueOf(value))); } /** @@ -231,7 +240,7 @@ public class CommandConfig implements CommandExecutor { * @param sender

The command sender to display the config list to

*/ private void displayConfigValues(CommandSender sender) { - sender.sendMessage(ChatColor.GREEN + "Stargate " + ChatColor.GOLD + "Config values:"); + sender.sendMessage(Translator.getTranslatedMessage(TranslatableMessage.CONFIG_VALUES_HEADER)); for (ConfigurationOption option : ConfigurationOption.values()) { if (!bannedConfigOptions.contains(option)) { @@ -252,8 +261,9 @@ public class CommandConfig implements CommandExecutor { if (option.getDataType() == OptionDataType.STRING_LIST) { stringValue = "[" + StringUtils.join((String[]) defaultValue, ",") + "]"; } - return ChatColor.GOLD + option.name() + ChatColor.WHITE + " - " + ChatColor.GREEN + option.getDescription() + - ChatColor.DARK_GRAY + " (Default: " + ChatColor.GRAY + stringValue + ChatColor.DARK_GRAY + ")"; + return StringFormatter.replacePlaceholders(Translator.getTranslatedMessage( + TranslatableMessage.CONFIG_OPTION_DESCRIPTION), new String[]{"{name}", "{description}", "{value}"}, + new String[]{option.name(), option.getDescription(), stringValue}); } } diff --git a/src/main/java/net/knarcraft/stargatecommand/command/CommandDial.java b/src/main/java/net/knarcraft/stargatecommand/command/CommandDial.java index 09220a2..702232f 100644 --- a/src/main/java/net/knarcraft/stargatecommand/command/CommandDial.java +++ b/src/main/java/net/knarcraft/stargatecommand/command/CommandDial.java @@ -5,6 +5,7 @@ import net.TheDgtl.Stargate.manager.PermissionManager; import net.TheDgtl.Stargate.network.Network; import net.TheDgtl.Stargate.network.RegistryAPI; import net.TheDgtl.Stargate.network.portal.RealPortal; +import net.knarcraft.stargatecommand.manager.OverrideManager; import net.knarcraft.stargatecommand.util.PortalFinderHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -74,6 +75,7 @@ public class CommandDial implements CommandExecutor { return true; } originPortal.overrideDestination(targetPortal); + OverrideManager.storeOverriddenDestination(originPortal); originPortal.open(player); player.sendMessage("Your Stargate has been prepared"); diff --git a/src/main/java/net/knarcraft/stargatecommand/formatting/StringFormatter.java b/src/main/java/net/knarcraft/stargatecommand/formatting/StringFormatter.java new file mode 100644 index 0000000..dc6ea3c --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/formatting/StringFormatter.java @@ -0,0 +1,107 @@ +package net.knarcraft.stargatecommand.formatting; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A formatter for formatting displayed messages + */ +public class StringFormatter { + + /** + * Replaces a placeholder in a string + * + * @param input

The input string to replace in

+ * @param placeholder

The placeholder to replace

+ * @param replacement

The replacement value

+ * @return

The input string with the placeholder replaced

+ */ + public static String replacePlaceholder(String input, String placeholder, String replacement) { + return input.replace(placeholder, replacement); + } + + /** + * Replaces placeholders in a string + * + * @param input

The input string to replace in

+ * @param placeholders

The placeholders to replace

+ * @param replacements

The replacement values

+ * @return

The input string with placeholders replaced

+ */ + public static String replacePlaceholders(String input, String[] placeholders, String[] replacements) { + for (int i = 0; i < Math.min(placeholders.length, replacements.length); i++) { + input = replacePlaceholder(input, placeholders[i], replacements[i]); + } + return input; + } + + /** + * Gets a translated and formatted info message + * + * @param translatableMessage

The translatable message to translate and format

+ * @return

The translated and formatted message

+ */ + public static String getTranslatedInfoMessage(TranslatableMessage translatableMessage) { + return formatInfoMessage(Translator.getTranslatedMessage(translatableMessage)); + } + + /** + * Gets a translated and formatted error message + * + * @param translatableMessage

The translatable message to translate and format

+ * @return

The translated and formatted message

+ */ + public static String getTranslatedErrorMessage(TranslatableMessage translatableMessage) { + return formatErrorMessage(Translator.getTranslatedMessage(translatableMessage)); + } + + /** + * Formats an information message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + public static String formatInfoMessage(String message) { + return ChatColor.DARK_GREEN + formatMessage(message); + } + + /** + * Formats an error message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + public static String formatErrorMessage(String message) { + return ChatColor.DARK_RED + formatMessage(message); + } + + /** + * Formats a message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + private static String formatMessage(String message) { + return Translator.getTranslatedMessage(TranslatableMessage.PREFIX) + " " + + ChatColor.RESET + message; + } + + /** + * Translates all found color codes to formatting in a string + * + * @param message

The string to search for color codes

+ * @return

The message with color codes translated

+ */ + public static String translateAllColorCodes(String message) { + message = ChatColor.translateAlternateColorCodes('&', message); + Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})"); + Matcher matcher = pattern.matcher(message); + while (matcher.find()) { + message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group())); + } + return message; + } + +} diff --git a/src/main/java/net/knarcraft/stargatecommand/formatting/TranslatableMessage.java b/src/main/java/net/knarcraft/stargatecommand/formatting/TranslatableMessage.java new file mode 100644 index 0000000..cef5973 --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/formatting/TranslatableMessage.java @@ -0,0 +1,58 @@ +package net.knarcraft.stargatecommand.formatting; + +/** + * An enum representing all translatable messages + */ +public enum TranslatableMessage { + + /** + * The prefix to display in messages + */ + PREFIX, + + /** + * The message displayed when a player is denied the usage of a command + */ + PERMISSION_DENIED, + + /** + * The message displayed when the user provides an invalid configuration option for the /sgc config command + */ + INVALID_CONFIGURATION_OPTION, + + /** + * The message displayed when the user provides an invalid value for the required datatype + */ + INVALID_DATATYPE_GIVEN, + + /** + * The message to display after a configuration value has been successfully updated + */ + CONFIG_UPDATED, + + /** + * The message to display if a negative number is provided, but a positive number is required + */ + POSITIVE_NUMBER_REQUIRED, + + /** + * The message to display if a number is expected, but a non-number is provided + */ + INVALID_NUMBER_GIVEN, + + /** + * The message to display when displaying the current value of a configuration option + */ + CONFIG_OPTION_CURRENT_VALUE, + + /** + * The header to display when showing all configuration options and their values + */ + CONFIG_VALUES_HEADER, + + /** + * The message to display when showing a full description of a configuration option + */ + CONFIG_OPTION_DESCRIPTION, + +} diff --git a/src/main/java/net/knarcraft/stargatecommand/formatting/Translator.java b/src/main/java/net/knarcraft/stargatecommand/formatting/Translator.java new file mode 100644 index 0000000..bf377e2 --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/formatting/Translator.java @@ -0,0 +1,118 @@ +package net.knarcraft.stargatecommand.formatting; + +import net.knarcraft.stargatecommand.StargateCommand; +import net.knarcraft.stargatecommand.util.FileHelper; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +/** + * A tool to get strings translated to the correct language + */ +public final class Translator { + + private static Map translatedMessages; + private static Map backupTranslatedMessages; + + private Translator() { + + } + + /** + * Loads the languages used by this translator + */ + public static void loadLanguages(String selectedLanguage) { + backupTranslatedMessages = loadTranslatedMessages("en"); + translatedMessages = loadCustomTranslatedMessages(selectedLanguage); + if (translatedMessages == null) { + translatedMessages = loadTranslatedMessages(selectedLanguage); + } + } + + /** + * Gets a translated version of the given translatable message + * + * @param translatableMessage

The message to translate

+ * @return

The translated message

+ */ + public static String getTranslatedMessage(TranslatableMessage translatableMessage) { + if (translatedMessages == null) { + return "Translated strings not loaded"; + } + String translatedMessage; + if (translatedMessages.containsKey(translatableMessage)) { + translatedMessage = translatedMessages.get(translatableMessage); + } else if (backupTranslatedMessages.containsKey(translatableMessage)) { + translatedMessage = backupTranslatedMessages.get(translatableMessage); + } else { + translatedMessage = translatableMessage.toString(); + } + return StringFormatter.translateAllColorCodes(translatedMessage); + } + + /** + * Loads all translated messages for the given language + * + * @param language

The language chosen by the user

+ * @return

A mapping of all strings for the given language

+ */ + public static Map loadTranslatedMessages(String language) { + try { + BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml"); + return loadTranslatableMessages(language, reader); + } catch (FileNotFoundException e) { + StargateCommand.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages"); + return null; + } + } + + /** + * Tries to load translated messages from a custom strings.yml file + * + * @param language

The selected language

+ * @return

The loaded translated strings, or null if no custom language file exists

+ */ + public static Map loadCustomTranslatedMessages(String language) { + File strings = new File(StargateCommand.getInstance().getDataFolder(), "strings.yml"); + if (!strings.exists()) { + StargateCommand.getInstance().getLogger().log(Level.FINEST, "Strings file not found"); + return null; + } + + try { + StargateCommand.getInstance().getLogger().log(Level.WARNING, "Loading custom strings..."); + return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings)))); + } catch (FileNotFoundException e) { + StargateCommand.getInstance().getLogger().log(Level.WARNING, "Unable to load custom messages"); + return null; + } + } + + /** + * Loads translatable messages from the given reader + * + * @param language

The selected language

+ * @param reader

The buffered reader to read from

+ * @return

The loaded translated strings

+ */ + private static Map loadTranslatableMessages(String language, BufferedReader reader) { + Map translatedMessages = new HashMap<>(); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(reader); + + for (TranslatableMessage message : TranslatableMessage.values()) { + String translated = configuration.getString(language + "." + message.toString()); + if (translated != null) { + translatedMessages.put(message, translated); + } + } + return translatedMessages; + } + +} \ No newline at end of file diff --git a/src/main/java/net/knarcraft/stargatecommand/listener/StargateListener.java b/src/main/java/net/knarcraft/stargatecommand/listener/StargateListener.java new file mode 100644 index 0000000..b6bd06f --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/listener/StargateListener.java @@ -0,0 +1,44 @@ +package net.knarcraft.stargatecommand.listener; + +import net.TheDgtl.Stargate.event.StargateCloseEvent; +import net.TheDgtl.Stargate.event.StargateDeactivateEvent; +import net.TheDgtl.Stargate.event.StargatePortalEvent; +import net.TheDgtl.Stargate.network.portal.Portal; +import net.knarcraft.stargatecommand.manager.OverrideManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +/** + * A listener for listening to Stargate events + */ +public class StargateListener implements Listener { + + @EventHandler + public void teleportListener(StargatePortalEvent event) { + removeOverriddenDestination(event.getPortal()); + } + + @EventHandler + public void deactivateListener(StargateDeactivateEvent event) { + removeOverriddenDestination(event.getPortal()); + } + + @EventHandler + public void closeListener(StargateCloseEvent event) { + removeOverriddenDestination(event.getPortal()); + } + + /** + * Removes the override for the given portal, if this plugin has overridden its destination + * + * @param portal

The portal to remove the override from

+ */ + private void removeOverriddenDestination(Portal portal) { + boolean isOverridden = OverrideManager.hasOverriddenDestination(portal); + if (isOverridden) { + portal.overrideDestination(null); + OverrideManager.removeOverriddenDestination(portal); + } + } + +} diff --git a/src/main/java/net/knarcraft/stargatecommand/manager/OverrideManager.java b/src/main/java/net/knarcraft/stargatecommand/manager/OverrideManager.java new file mode 100644 index 0000000..eeec0a0 --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/manager/OverrideManager.java @@ -0,0 +1,46 @@ +package net.knarcraft.stargatecommand.manager; + +import net.TheDgtl.Stargate.network.portal.Portal; + +import java.util.ArrayList; +import java.util.List; + +/** + * A manager for managing overridden destinations + */ +public final class OverrideManager { + + private static final List overriddenPortals = new ArrayList<>(); + + private OverrideManager() { + + } + + /** + * Stores an over-ridden destination + * + * @param portal

The portal whose destination was overridden

+ */ + public static void storeOverriddenDestination(Portal portal) { + overriddenPortals.add(portal); + } + + /** + * Removes an over-ridden destination for a portal + * + * @param portal

The portal to remove the overridden destination for

+ */ + public static void removeOverriddenDestination(Portal portal) { + overriddenPortals.remove(portal); + } + + /** + * Gets whether the given portal's destination is currently overridden + * + * @param portal

The portal with a possibly overridden destination

+ */ + public static boolean hasOverriddenDestination(Portal portal) { + return overriddenPortals.contains(portal); + } + +} \ No newline at end of file diff --git a/src/main/java/net/knarcraft/stargatecommand/util/FileHelper.java b/src/main/java/net/knarcraft/stargatecommand/util/FileHelper.java new file mode 100644 index 0000000..f31f00f --- /dev/null +++ b/src/main/java/net/knarcraft/stargatecommand/util/FileHelper.java @@ -0,0 +1,32 @@ +package net.knarcraft.stargatecommand.util; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * A helper class for dealing with files + */ +public final class FileHelper { + + private FileHelper() { + + } + + /** + * Gets a buffered reader for + * + * @return

A buffered read for reading the file

+ * @throws FileNotFoundException

If unable to get an input stream for the given file

+ */ + public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException { + InputStream inputStream = FileHelper.class.getResourceAsStream(file); + if (inputStream == null) { + throw new FileNotFoundException("Unable to read the given file"); + } + return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + } + +} diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml new file mode 100644 index 0000000..68ce171 --- /dev/null +++ b/src/main/resources/strings.yml @@ -0,0 +1,11 @@ +en: + PREFIX: "[StargateCommand]" + PERMISSION_DENIED: "Permission Denied" + INVALID_CONFIGURATION_OPTION: "Invalid configuration option specified" + INVALID_DATATYPE_GIVEN: "Invalid {datatype} given" + CONFIG_UPDATED: "Configuration updated" + POSITIVE_NUMBER_REQUIRED: "This config option cannot be negative" + INVALID_NUMBER_GIVEN: "Invalid number given" + CONFIG_OPTION_CURRENT_VALUE: "&aCurrent value: &6{value}" + CONFIG_VALUES_HEADER: "&aStargate &6Config values:" + CONFIG_OPTION_DESCRIPTION: "&6{name}&f - &a{description}&8 (Default: &7{value}&8)" \ No newline at end of file