Initial commit
This commit is contained in:
commit
8f0c028bb5
113
.gitignore
vendored
Normal file
113
.gitignore
vendored
Normal file
@ -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/
|
74
pom.xml
Normal file
74
pom.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>KnarLib</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>KnarLib</name>
|
||||
|
||||
<properties>
|
||||
<java.version>16</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.19.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
31
src/main/java/net/knarcraft/knarlib/KnarLib.java
Normal file
31
src/main/java/net/knarcraft/knarlib/KnarLib.java
Normal file
@ -0,0 +1,31 @@
|
||||
package net.knarcraft.knarlib;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* KnarLib's main class
|
||||
*/
|
||||
public final class KnarLib {
|
||||
|
||||
private static JavaPlugin plugin;
|
||||
|
||||
/**
|
||||
* Gets a plugin instance for use with the bukkit scheduler or similar
|
||||
*
|
||||
* @return <p>A plugin instance</p>
|
||||
*/
|
||||
public static JavaPlugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an instance of the plugin used with KnarLib
|
||||
*
|
||||
* @param plugin <p>The plugin instance to use</p>
|
||||
*/
|
||||
public static void setPlugin(final JavaPlugin plugin) {
|
||||
KnarLib.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package net.knarcraft.knarlib.formatting;
|
||||
|
||||
import net.knarcraft.knarlib.KnarLib;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* A formatter for formatting displayed messages
|
||||
*/
|
||||
public final class StringFormatter {
|
||||
|
||||
private final static String pluginName = KnarLib.getPlugin().getDescription().getName();
|
||||
|
||||
private StringFormatter() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message signifying a successful action
|
||||
*
|
||||
* @param sender <p>The command sender to display the message to</p>
|
||||
* @param message <p>The translatable message to display</p>
|
||||
*/
|
||||
public static void displaySuccessMessage(CommandSender sender, TranslatableTimeUnit message) {
|
||||
sender.sendMessage(ChatColor.GREEN + getFormattedMessage(Translator.getTranslatedMessage(message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message signifying a successful action
|
||||
*
|
||||
* @param sender <p>The command sender to display the message to</p>
|
||||
* @param message <p>The raw message to display</p>
|
||||
*/
|
||||
public static void displaySuccessMessage(CommandSender sender, String message) {
|
||||
sender.sendMessage(ChatColor.GREEN + getFormattedMessage(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a message signifying an unsuccessful action
|
||||
*
|
||||
* @param sender <p>The command sender to display the message to</p>
|
||||
* @param message <p>The translatable message to display</p>
|
||||
*/
|
||||
public static void displayErrorMessage(CommandSender sender, TranslatableTimeUnit message) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + getFormattedMessage(Translator.getTranslatedMessage(message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the formatted version of any chat message
|
||||
*
|
||||
* @param message <p>The message to format</p>
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
private static String getFormattedMessage(String message) {
|
||||
return "[" + pluginName + "] " + ChatColor.RESET + translateColors(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates & color codes to proper colors
|
||||
*
|
||||
* @param input <p>The input string to translate colors for</p>
|
||||
* @return <p>The input with color codes translated</p>
|
||||
*/
|
||||
private static String translateColors(String input) {
|
||||
return ChatColor.translateAlternateColorCodes('&', input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a placeholder in a string
|
||||
*
|
||||
* @param input <p>The input string to replace in</p>
|
||||
* @param placeholder <p>The placeholder to replace</p>
|
||||
* @param replacement <p>The replacement value</p>
|
||||
* @return <p>The input string with the placeholder replaced</p>
|
||||
*/
|
||||
public static String replacePlaceholder(String input, String placeholder, String replacement) {
|
||||
return input.replace(placeholder, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces placeholders in a string
|
||||
*
|
||||
* @param input <p>The input string to replace in</p>
|
||||
* @param placeholders <p>The placeholders to replace</p>
|
||||
* @param replacements <p>The replacement values</p>
|
||||
* @return <p>The input string with placeholders replaced</p>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package net.knarcraft.knarlib.formatting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.knarcraft.knarlib.formatting.StringFormatter.replacePlaceholder;
|
||||
|
||||
/**
|
||||
* A utility for formatting a string specifying an amount of time
|
||||
*/
|
||||
public final class TimeFormatter {
|
||||
|
||||
private static Map<Double, TranslatableTimeUnit[]> timeUnits;
|
||||
private static List<Double> sortedUnits;
|
||||
|
||||
private TimeFormatter() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string used for displaying this sign's duration
|
||||
*
|
||||
* @return <p>The string used for displaying this sign's duration</p>
|
||||
*/
|
||||
public static String getDurationString(int duration) {
|
||||
if (duration == 0) {
|
||||
return Translator.getTranslatedMessage(TranslatableTimeUnit.UNIT_NOW);
|
||||
} else {
|
||||
if (sortedUnits == null) {
|
||||
initializeUnits();
|
||||
}
|
||||
for (Double unit : sortedUnits) {
|
||||
if (duration / unit >= 1) {
|
||||
double units = round(duration / unit);
|
||||
return formatDurationString(units, timeUnits.get(unit)[units == 1 ? 0 : 1],
|
||||
(units * 10) % 10 == 0);
|
||||
}
|
||||
}
|
||||
return formatDurationString(duration, TranslatableTimeUnit.UNIT_SECONDS, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a number to its last two digits
|
||||
*
|
||||
* @param number <p>The number to round</p>
|
||||
* @return <p>The rounded number</p>
|
||||
*/
|
||||
private static double round(double number) {
|
||||
return Math.round(number * 100.0) / 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a duration string
|
||||
*
|
||||
* @param duration <p>The duration to display</p>
|
||||
* @param translatableMessage <p>The time unit to display</p>
|
||||
* @param castToInt <p>Whether to cast the duration to an int</p>
|
||||
* @return <p>The formatted duration string</p>
|
||||
*/
|
||||
private static String formatDurationString(double duration, TranslatableTimeUnit translatableMessage, boolean castToInt) {
|
||||
String durationFormat = Translator.getTranslatedMessage(TranslatableTimeUnit.DURATION_FORMAT);
|
||||
durationFormat = replacePlaceholder(durationFormat, "{unit}",
|
||||
Translator.getTranslatedMessage(translatableMessage));
|
||||
return replacePlaceholder(durationFormat, "{time}", castToInt ? String.valueOf((int) duration) :
|
||||
String.valueOf(duration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the mapping of available time units for formatting permission sign duration
|
||||
*/
|
||||
private static void initializeUnits() {
|
||||
double minute = 60;
|
||||
double hour = 60 * minute;
|
||||
double day = 24 * hour;
|
||||
double week = 7 * day;
|
||||
double month = 4 * week;
|
||||
double year = 365 * day;
|
||||
double decade = 10 * year;
|
||||
|
||||
timeUnits = new HashMap<>();
|
||||
timeUnits.put(decade, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_DECADE, TranslatableTimeUnit.UNIT_DECADES});
|
||||
timeUnits.put(year, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_YEAR, TranslatableTimeUnit.UNIT_YEARS});
|
||||
timeUnits.put(month, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_MONTH, TranslatableTimeUnit.UNIT_MONTHS});
|
||||
timeUnits.put(week, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_WEEK, TranslatableTimeUnit.UNIT_WEEKS});
|
||||
timeUnits.put(day, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_DAY, TranslatableTimeUnit.UNIT_DAYS});
|
||||
timeUnits.put(hour, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_HOUR, TranslatableTimeUnit.UNIT_HOURS});
|
||||
timeUnits.put(minute, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_MINUTE, TranslatableTimeUnit.UNIT_MINUTES});
|
||||
timeUnits.put(1D, new TranslatableTimeUnit[]{TranslatableTimeUnit.UNIT_SECOND, TranslatableTimeUnit.UNIT_SECONDS});
|
||||
|
||||
sortedUnits = new ArrayList<>(timeUnits.keySet());
|
||||
Collections.sort(sortedUnits);
|
||||
Collections.reverse(sortedUnits);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package net.knarcraft.knarlib.formatting;
|
||||
|
||||
/**
|
||||
* A message which can be translated
|
||||
*/
|
||||
public interface TranslatableMessage {
|
||||
|
||||
/**
|
||||
* Gets the name of this translatable message
|
||||
*
|
||||
* <p>This is automatically overridden by enums. This should not be overridden manually!</p>
|
||||
*
|
||||
* @return <p>The name of this translatable message</p>
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Gets all translatable messages
|
||||
*
|
||||
* <p>This should return Enum.values() for the class. This is basically a workaround to get all enum values.</p>
|
||||
*
|
||||
* @return <p>All translatable messages</p>
|
||||
*/
|
||||
TranslatableMessage[] getAllMessages();
|
||||
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package net.knarcraft.knarlib.formatting;
|
||||
|
||||
/**
|
||||
* An enum containing all translatable time units
|
||||
*
|
||||
* <p>These time units must have a translatable message to use the time formatter</p>
|
||||
*/
|
||||
public enum TranslatableTimeUnit implements TranslatableMessage {
|
||||
|
||||
/**
|
||||
* The format for displaying the exact duration of a blacksmith's cool-down or delay
|
||||
*/
|
||||
DURATION_FORMAT,
|
||||
|
||||
/**
|
||||
* The text to display for 0 seconds
|
||||
*/
|
||||
UNIT_NOW,
|
||||
|
||||
/**
|
||||
* The text to display for 1 second
|
||||
*/
|
||||
UNIT_SECOND,
|
||||
|
||||
/**
|
||||
* The text to display for a number of seconds
|
||||
*/
|
||||
UNIT_SECONDS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 minute
|
||||
*/
|
||||
UNIT_MINUTE,
|
||||
|
||||
/**
|
||||
* The text to display for a number of minutes
|
||||
*/
|
||||
UNIT_MINUTES,
|
||||
|
||||
/**
|
||||
* The text to display for 1 hour
|
||||
*/
|
||||
UNIT_HOUR,
|
||||
|
||||
/**
|
||||
* The text to display for a number of hours
|
||||
*/
|
||||
UNIT_HOURS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 day
|
||||
*/
|
||||
UNIT_DAY,
|
||||
|
||||
/**
|
||||
* The text to display for a number of days
|
||||
*/
|
||||
UNIT_DAYS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 week
|
||||
*/
|
||||
UNIT_WEEK,
|
||||
|
||||
/**
|
||||
* The text to display for a number of weeks
|
||||
*/
|
||||
UNIT_WEEKS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 month
|
||||
*/
|
||||
UNIT_MONTH,
|
||||
|
||||
/**
|
||||
* The text to display for a number of months
|
||||
*/
|
||||
UNIT_MONTHS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 year
|
||||
*/
|
||||
UNIT_YEAR,
|
||||
|
||||
/**
|
||||
* The text to display for a number of years
|
||||
*/
|
||||
UNIT_YEARS,
|
||||
|
||||
/**
|
||||
* The text to display for 1 decade
|
||||
*/
|
||||
UNIT_DECADE,
|
||||
|
||||
/**
|
||||
* The text to display for a number of decades
|
||||
*/
|
||||
UNIT_DECADES,
|
||||
;
|
||||
|
||||
@Override
|
||||
public TranslatableMessage[] getAllMessages() {
|
||||
return TranslatableTimeUnit.values();
|
||||
}
|
||||
|
||||
}
|
139
src/main/java/net/knarcraft/knarlib/formatting/Translator.java
Normal file
139
src/main/java/net/knarcraft/knarlib/formatting/Translator.java
Normal file
@ -0,0 +1,139 @@
|
||||
package net.knarcraft.knarlib.formatting;
|
||||
|
||||
import net.knarcraft.knarlib.KnarLib;
|
||||
import net.knarcraft.knarlib.property.ColorConversion;
|
||||
import net.knarcraft.knarlib.util.ColorHelper;
|
||||
import net.knarcraft.knarlib.util.FileHelper;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
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.List;
|
||||
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 List<TranslatableMessage> messageCategories;
|
||||
private static Map<TranslatableMessage, String> translatedMessages;
|
||||
private static Map<TranslatableMessage, String> backupTranslatedMessages;
|
||||
|
||||
private Translator() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all translatable messages in the given message category
|
||||
*
|
||||
* <p>This should be run for one enum of every class extending TranslatableMessage. This allows the translator to
|
||||
* look for translations for any enum in the category.</p>
|
||||
*
|
||||
* @param translatableMessage <p>A translatable message in the category to register</p>
|
||||
*/
|
||||
public static void registerMessageCategory(TranslatableMessage translatableMessage) {
|
||||
messageCategories.add(translatableMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <p>The message to translate</p>
|
||||
* @return <p>The translated message</p>
|
||||
*/
|
||||
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 ColorHelper.translateColorCodes(translatedMessage, ColorConversion.RGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all translated messages for the given language
|
||||
*
|
||||
* @param language <p>The language chosen by the user</p>
|
||||
* @return <p>A mapping of all strings for the given language</p>
|
||||
*/
|
||||
public static Map<TranslatableMessage, String> loadTranslatedMessages(String language) {
|
||||
try {
|
||||
BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml");
|
||||
return loadTranslatableMessages(language, reader);
|
||||
} catch (FileNotFoundException e) {
|
||||
KnarLib.getPlugin().getLogger().log(Level.SEVERE, "Unable to load translated messages");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load translated messages from a custom strings.yml file
|
||||
*
|
||||
* @param language <p>The selected language</p>
|
||||
* @return <p>The loaded translated strings, or null if no custom language file exists</p>
|
||||
*/
|
||||
public static Map<TranslatableMessage, String> loadCustomTranslatedMessages(String language) {
|
||||
JavaPlugin instance = KnarLib.getPlugin();
|
||||
|
||||
File strings = new File(instance.getDataFolder(), "strings.yml");
|
||||
if (!strings.exists()) {
|
||||
instance.getLogger().log(Level.FINEST, "Strings file not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
instance.getLogger().log(Level.INFO, "Loading custom strings...");
|
||||
return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings))));
|
||||
} catch (FileNotFoundException e) {
|
||||
instance.getLogger().log(Level.WARNING, "Unable to load custom messages");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads translatable messages from the given reader
|
||||
*
|
||||
* @param language <p>The selected language</p>
|
||||
* @param reader <p>The buffered reader to read from</p>
|
||||
* @return <p>The loaded translated strings</p>
|
||||
*/
|
||||
private static Map<TranslatableMessage, String> loadTranslatableMessages(String language, BufferedReader reader) {
|
||||
Map<TranslatableMessage, String> translatedMessages = new HashMap<>();
|
||||
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(reader);
|
||||
|
||||
for (TranslatableMessage translatableMessageCategories : messageCategories) {
|
||||
for (TranslatableMessage translatableMessage : translatableMessageCategories.getAllMessages()) {
|
||||
String translated = configuration.getString(language + "." + translatableMessage.name());
|
||||
if (translated != null) {
|
||||
translatedMessages.put(translatableMessage, translated);
|
||||
}
|
||||
}
|
||||
}
|
||||
return translatedMessages;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.knarcraft.knarlib.property;
|
||||
|
||||
/**
|
||||
* An enum representing the different types of color conversions available
|
||||
*/
|
||||
public enum ColorConversion {
|
||||
|
||||
/**
|
||||
* No conversion of colors
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Ampersand color codes are converted into colors
|
||||
*/
|
||||
NORMAL,
|
||||
|
||||
/**
|
||||
* Ampersand color codes, and hexadecimal color codes are converted into colors
|
||||
*/
|
||||
RGB
|
||||
|
||||
}
|
89
src/main/java/net/knarcraft/knarlib/util/ColorHelper.java
Normal file
89
src/main/java/net/knarcraft/knarlib/util/ColorHelper.java
Normal file
@ -0,0 +1,89 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import net.knarcraft.knarlib.property.ColorConversion;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Color;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A helper class for dealing with colors
|
||||
*/
|
||||
public final class ColorHelper {
|
||||
|
||||
private static boolean requireAmpersandInHexColors = false;
|
||||
|
||||
private ColorHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts the given color
|
||||
*
|
||||
* @param color <p>The color to invert</p>
|
||||
* @return <p>The inverted color</p>
|
||||
*/
|
||||
public static Color invert(Color color) {
|
||||
return color.setRed(255 - color.getRed()).setGreen(255 - color.getGreen()).setBlue(255 - color.getBlue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the chat color corresponding to the given color
|
||||
*
|
||||
* @param color <p>The color to convert into a chat color</p>
|
||||
* @return <p>The resulting chat color</p>
|
||||
*/
|
||||
public static ChatColor fromColor(Color color) {
|
||||
return ChatColor.of(String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to require ampersand in hex color
|
||||
*
|
||||
* <p>If set to true, &#f35336 will be treated as a color code, but #f35336 won't. By default, this is set to false,
|
||||
* meaning that both are treated as color codes.</p>
|
||||
*
|
||||
* @param requireAmpersandInHexColors <p>True if hex colors should require an ampersand</p>
|
||||
*/
|
||||
public static void setRequireAmpersandInHexColors(boolean requireAmpersandInHexColors) {
|
||||
ColorHelper.requireAmpersandInHexColors = requireAmpersandInHexColors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates color codes according to the given color conversion setting
|
||||
*
|
||||
* @param message <p>The message to translate color codes for</p>
|
||||
* @param colorConversion <p>The type of color conversion to apply</p>
|
||||
* @return <p>The string with color codes applied</p>
|
||||
*/
|
||||
public static String translateColorCodes(String message, ColorConversion colorConversion) {
|
||||
return switch (colorConversion) {
|
||||
case NONE -> message;
|
||||
case NORMAL -> ChatColor.translateAlternateColorCodes('&', message);
|
||||
case RGB -> translateAllColorCodes(message);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates all found color codes to formatting in a string
|
||||
*
|
||||
* @param message <p>The string to search for color codes</p>
|
||||
* @return <p>The message with color codes translated</p>
|
||||
*/
|
||||
private static String translateAllColorCodes(String message) {
|
||||
message = ChatColor.translateAlternateColorCodes('&', message);
|
||||
Pattern pattern;
|
||||
if (requireAmpersandInHexColors) {
|
||||
pattern = Pattern.compile("(&#[a-fA-F0-9]{6})");
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
141
src/main/java/net/knarcraft/knarlib/util/FileHelper.java
Normal file
141
src/main/java/net/knarcraft/knarlib/util/FileHelper.java
Normal file
@ -0,0 +1,141 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A helper class for dealing with files
|
||||
*/
|
||||
public final class FileHelper {
|
||||
|
||||
private FileHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffered reader for
|
||||
*
|
||||
* @return <p>A buffered read for reading the file</p>
|
||||
* @throws FileNotFoundException <p>If unable to get an input stream for the given file</p>
|
||||
*/
|
||||
public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException {
|
||||
InputStream inputStream = getInputStreamForInternalFile(file);
|
||||
if (inputStream == null) {
|
||||
throw new FileNotFoundException("Unable to read the given file");
|
||||
}
|
||||
return getBufferedReaderFromInputStream(inputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an input stream from a string pointing to an internal file
|
||||
*
|
||||
* <p>This is used for getting an input stream for reading a file contained within the compiled .jar file. The file
|
||||
* should be in the resources directory, and the file path should start with a forward slash ("/") character.</p>
|
||||
*
|
||||
* @param file <p>The file to read</p>
|
||||
* @return <p>An input stream for the file</p>
|
||||
*/
|
||||
public static InputStream getInputStreamForInternalFile(String file) {
|
||||
return FileHelper.class.getResourceAsStream(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffered reader from a string pointing to a file
|
||||
*
|
||||
* @param file <p>The file to read</p>
|
||||
* @return <p>A buffered reader reading the file</p>
|
||||
* @throws FileNotFoundException <p>If the given file does not exist</p>
|
||||
*/
|
||||
public static BufferedReader getBufferedReaderFromString(String file) throws FileNotFoundException {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
return getBufferedReaderFromInputStream(fileInputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffered reader given an input stream
|
||||
*
|
||||
* @param inputStream <p>The input stream to read</p>
|
||||
* @return <p>A buffered reader reading the input stream</p>
|
||||
*/
|
||||
public static BufferedReader getBufferedReaderFromInputStream(InputStream inputStream) {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
|
||||
return new BufferedReader(inputStreamReader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffered writer from a string pointing to a file
|
||||
*
|
||||
* @param file <p>The file to write to</p>
|
||||
* @return <p>A buffered writer writing to the file</p>
|
||||
* @throws FileNotFoundException <p>If the file does not exist</p>
|
||||
*/
|
||||
public static BufferedWriter getBufferedWriterFromString(String file) throws FileNotFoundException {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8);
|
||||
return new BufferedWriter(outputStreamWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads key/value pairs from an input stream
|
||||
*
|
||||
* @param bufferedReader <p>The buffered reader to read</p>
|
||||
* @return <p>A map containing the read pairs</p>
|
||||
* @throws IOException <p>If unable to read from the stream</p>
|
||||
*/
|
||||
public static Map<String, String> readKeyValuePairs(BufferedReader bufferedReader, String separator, boolean translateColorCodes) throws IOException {
|
||||
Map<String, String> readPairs = new HashMap<>();
|
||||
|
||||
String line = bufferedReader.readLine();
|
||||
boolean firstLine = true;
|
||||
while (line != null) {
|
||||
//Strip UTF BOM from the first line
|
||||
if (firstLine) {
|
||||
line = removeUTF8BOM(line);
|
||||
firstLine = false;
|
||||
}
|
||||
//Split at first separator
|
||||
int separatorIndex = line.indexOf(separator);
|
||||
if (separatorIndex == -1) {
|
||||
line = bufferedReader.readLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
//Read the line
|
||||
String key = line.substring(0, separatorIndex);
|
||||
String value = ChatColor.translateAlternateColorCodes('&', line.substring(separatorIndex + 1));
|
||||
readPairs.put(key, value);
|
||||
|
||||
line = bufferedReader.readLine();
|
||||
}
|
||||
bufferedReader.close();
|
||||
|
||||
return readPairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the UTF-8 Byte Order Mark if present
|
||||
*
|
||||
* @param string <p>The string to remove the BOM from</p>
|
||||
* @return <p>A string guaranteed without a BOM</p>
|
||||
*/
|
||||
private static String removeUTF8BOM(String string) {
|
||||
String UTF8_BOM = "\uFEFF";
|
||||
if (string.startsWith(UTF8_BOM)) {
|
||||
string = string.substring(1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper class for getting string lists required for auto-completion
|
||||
*/
|
||||
public final class TabCompletionHelper {
|
||||
|
||||
private TabCompletionHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds tab complete values that contain the typed text
|
||||
*
|
||||
* @param values <p>The values to filter</p>
|
||||
* @param typedText <p>The text the player has started typing</p>
|
||||
* @return <p>The given string values that contain the player's typed text</p>
|
||||
*/
|
||||
public static List<String> filterMatchingContains(List<String> values, String typedText) {
|
||||
List<String> configValues = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
if (value.toLowerCase().contains(typedText.toLowerCase())) {
|
||||
configValues.add(value);
|
||||
}
|
||||
}
|
||||
return configValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds tab complete values that match the start of the typed text
|
||||
*
|
||||
* @param values <p>The values to filter</p>
|
||||
* @param typedText <p>The text the player has started typing</p>
|
||||
* @return <p>The given string values that start with the player's typed text</p>
|
||||
*/
|
||||
public static List<String> filterMatchingStartsWith(List<String> values, String typedText) {
|
||||
List<String> configValues = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
if (value.toLowerCase().startsWith(typedText.toLowerCase())) {
|
||||
configValues.add(value);
|
||||
}
|
||||
}
|
||||
return configValues;
|
||||
}
|
||||
|
||||
}
|
102
src/main/java/net/knarcraft/knarlib/util/UpdateChecker.java
Normal file
102
src/main/java/net/knarcraft/knarlib/util/UpdateChecker.java
Normal file
@ -0,0 +1,102 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* The update checker is responsible for looking for new updates
|
||||
*/
|
||||
public final class UpdateChecker {
|
||||
|
||||
private final static String updateNotice = "A new update is available: %s (You are still on %s)";
|
||||
|
||||
private UpdateChecker() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a new update available, and alerts the user if necessary
|
||||
*
|
||||
* @param plugin <p>The plugin to check for updates for</p>
|
||||
* @param apiResourceURL <p>The spigot URL to check for updates. Example: https://api.spigotmc.org/legacy/update.php?resource={resourceId}</p>
|
||||
* @param getVersionSupplier <p>The supplier used to get the current plugin version</p>
|
||||
* @param setVersionMethod <p>A method to call with the new version as an argument. Can be used to alert admins about an available update or similar.</p>
|
||||
*/
|
||||
public static void checkForUpdate(Plugin plugin, String apiResourceURL, Supplier<String> getVersionSupplier,
|
||||
Consumer<String> setVersionMethod) {
|
||||
BukkitScheduler scheduler = plugin.getServer().getScheduler();
|
||||
scheduler.runTaskAsynchronously(plugin, () -> UpdateChecker.queryAPI(plugin, apiResourceURL, getVersionSupplier,
|
||||
setVersionMethod));
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the spigot API to check for a newer version, and prints to the console if found
|
||||
*
|
||||
* @param plugin <p>The plugin to check for updates for</p>
|
||||
* @param APIResourceURL <p>The spigot URL to check for updates</p>
|
||||
* @param getVersionMethod <p>The supplier used to get the current plugin version</p>
|
||||
* @param setVersionMethod <p>A method to call with the new version as an argument. Can be used to alert admins about an available update or similar.</p>
|
||||
*/
|
||||
private static void queryAPI(Plugin plugin, String APIResourceURL, Supplier<String> getVersionMethod,
|
||||
Consumer<String> setVersionMethod) {
|
||||
try {
|
||||
InputStream inputStream = new URL(APIResourceURL).openStream();
|
||||
BufferedReader reader = FileHelper.getBufferedReaderFromInputStream(inputStream);
|
||||
//There should only be one line of output
|
||||
String newVersion = reader.readLine();
|
||||
reader.close();
|
||||
|
||||
String oldVersion = getVersionMethod.get();
|
||||
//If there is a newer version, notify the user
|
||||
if (isVersionHigher(oldVersion, newVersion)) {
|
||||
plugin.getLogger().log(Level.INFO, getUpdateAvailableString(newVersion, oldVersion));
|
||||
if (setVersionMethod != null) {
|
||||
setVersionMethod.accept(newVersion);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Unable to get newest version.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string to display to a user to alert about a new update
|
||||
*
|
||||
* @param newVersion <p>The new available plugin version</p>
|
||||
* @param oldVersion <p>The old (current) plugin version</p>
|
||||
* @return <p>The string to display</p>
|
||||
*/
|
||||
public static String getUpdateAvailableString(String newVersion, String oldVersion) {
|
||||
return String.format(updateNotice, newVersion, oldVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether one version number is higher than another
|
||||
*
|
||||
* @param oldVersion <p>The old version to check</p>
|
||||
* @param newVersion <p>The new version to check</p>
|
||||
* @return <p>True if the new version is higher than the old one</p>
|
||||
*/
|
||||
public static boolean isVersionHigher(String oldVersion, String newVersion) {
|
||||
String[] oldVersionParts = oldVersion.split("\\.");
|
||||
String[] newVersionParts = newVersion.split("\\.");
|
||||
int versionLength = Math.max(oldVersionParts.length, newVersionParts.length);
|
||||
for (int i = 0; i < versionLength; i++) {
|
||||
int oldVersionNumber = oldVersionParts.length > i ? Integer.parseInt(oldVersionParts[i]) : 0;
|
||||
int newVersionNumber = newVersionParts.length > i ? Integer.parseInt(newVersionParts[i]) : 0;
|
||||
if (newVersionNumber != oldVersionNumber) {
|
||||
return newVersionNumber > oldVersionNumber;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user