Uses KnarLib to get rid of duplicate code

This commit is contained in:
Kristian Knarvik 2022-11-07 02:17:07 +01:00
parent 5b02a094e8
commit c03e06b132
10 changed files with 104 additions and 380 deletions

26
pom.xml
View File

@ -13,7 +13,7 @@
<description>A plugin for displaying citizens on the dynmap map</description> <description>A plugin for displaying citizens on the dynmap map</description>
<properties> <properties>
<java.version>17</java.version> <java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@ -24,8 +24,8 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>17</source> <source>${java.version}</source>
<target>17</target> <target>${java.version}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@ -40,6 +40,20 @@
</goals> </goals>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>net.knarcraft:knarlib</artifact>
<includes>
<include>net/knarcraft/knarlib/**</include>
</includes>
</filter>
<filter>
<excludes>
<exclude>*.MF</exclude>
<exclude>*.yml</exclude>
</excludes>
</filter>
</filters>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -136,6 +150,12 @@
<version>1.7.3</version> <version>1.7.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.mcmonkey</groupId> <groupId>org.mcmonkey</groupId>
<artifactId>sentinel</artifactId> <artifactId>sentinel</artifactId>

View File

@ -1,6 +1,6 @@
package net.knarcraft.dynmapcitizens; package net.knarcraft.dynmapcitizens;
import net.knarcraft.dynmapcitizens.formatting.Translator; import net.knarcraft.dynmapcitizens.formatting.DynmapCitizensTranslatableMessage;
import net.knarcraft.dynmapcitizens.handler.VaultHandler; import net.knarcraft.dynmapcitizens.handler.VaultHandler;
import net.knarcraft.dynmapcitizens.handler.trait.BlacksmithHandler; import net.knarcraft.dynmapcitizens.handler.trait.BlacksmithHandler;
import net.knarcraft.dynmapcitizens.handler.trait.CitizensTraitHandler; import net.knarcraft.dynmapcitizens.handler.trait.CitizensTraitHandler;
@ -8,6 +8,9 @@ import net.knarcraft.dynmapcitizens.handler.trait.MinstrelHandler;
import net.knarcraft.dynmapcitizens.handler.trait.SentinelHandler; import net.knarcraft.dynmapcitizens.handler.trait.SentinelHandler;
import net.knarcraft.dynmapcitizens.handler.trait.quests.QuestsHandler; import net.knarcraft.dynmapcitizens.handler.trait.quests.QuestsHandler;
import net.knarcraft.dynmapcitizens.settings.GlobalSettings; import net.knarcraft.dynmapcitizens.settings.GlobalSettings;
import net.knarcraft.knarlib.KnarLib;
import net.knarcraft.knarlib.formatting.TranslatableTimeUnit;
import net.knarcraft.knarlib.formatting.Translator;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
@ -31,6 +34,8 @@ public final class DynmapCitizens extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
DynmapCitizens.instance = this; DynmapCitizens.instance = this;
KnarLib.setPlugin(this);
//Initialize quest and dynmap APIs //Initialize quest and dynmap APIs
PluginManager pluginManager = Bukkit.getPluginManager(); PluginManager pluginManager = Bukkit.getPluginManager();
Plugin dynmapPlugin = pluginManager.getPlugin("dynmap"); Plugin dynmapPlugin = pluginManager.getPlugin("dynmap");
@ -51,6 +56,8 @@ public final class DynmapCitizens extends JavaPlugin {
this.globalSettings.load(configuration); this.globalSettings.load(configuration);
//Load all messages //Load all messages
Translator.registerMessageCategory(TranslatableTimeUnit.UNIT_SECOND);
Translator.registerMessageCategory(DynmapCitizensTranslatableMessage.SENTINEL_DESCRIPTION);
Translator.loadLanguages("en"); Translator.loadLanguages("en");
//Initialize all enabled traits //Initialize all enabled traits

View File

@ -0,0 +1,35 @@
package net.knarcraft.dynmapcitizens.formatting;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
/**
* An enum describing all of DynmapCitizens' translatable messages
*/
public enum DynmapCitizensTranslatableMessage implements TranslatableMessage {
SENTINEL_DESCRIPTION,
SENTINEL_DETAILS,
QUESTS_PLANNER_DESCRIPTION,
QUESTS_PLANNER_COOL_DOWN,
QUESTS_PLANNER_UNREPEATABLE,
QUESTS_PLANNER_FROM,
QUESTS_PLANNER_UNTIL,
QUEST_PLANNER_REPEAT,
QUESTS_REQUIREMENTS_FORMAT,
QUESTS_REQUIREMENTS_QUEST_POINTS,
QUESTS_REQUIREMENTS_EXP,
QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT,
QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM,
QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT,
QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM,
QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT,
QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM,
QUESTS_REQUIREMENTS_MC_MMO_SKILL,
QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT,
QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM;
@Override
public TranslatableMessage[] getAllMessages() {
return DynmapCitizensTranslatableMessage.values();
}
}

View File

@ -1,114 +0,0 @@
package net.knarcraft.dynmapcitizens.formatting;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A formatter for formatting displayed messages
*/
public final class StringFormatter {
private final static String pluginName = DynmapCitizens.getInstance().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, TranslatableMessage 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, TranslatableMessage 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;
}
/**
* 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>
*/
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;
}
}

View File

@ -1,7 +0,0 @@
package net.knarcraft.dynmapcitizens.formatting;
public enum TranslatableMessage {
SENTINEL_DETAILS
}

View File

@ -1,120 +0,0 @@
package net.knarcraft.dynmapcitizens.formatting;
import net.knarcraft.dynmapcitizens.DynmapCitizens;
import net.knarcraft.dynmapcitizens.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<TranslatableMessage, String> translatedMessages;
private static Map<TranslatableMessage, String> 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 <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 StringFormatter.translateAllColorCodes(translatedMessage);
}
/**
* 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) {
DynmapCitizens.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 <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) {
DynmapCitizens instance = DynmapCitizens.getInstance();
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 message : TranslatableMessage.values()) {
String translated = configuration.getString(language + "." + message.toString());
if (translated != null) {
translatedMessages.put(message, translated);
}
}
return translatedMessages;
}
}

View File

@ -2,7 +2,11 @@ package net.knarcraft.dynmapcitizens.handler.trait.quests;
import me.blackvein.quests.quests.IQuest; import me.blackvein.quests.quests.IQuest;
import me.blackvein.quests.quests.Planner; import me.blackvein.quests.quests.Planner;
import net.knarcraft.dynmapcitizens.util.TimeFormatter; import net.knarcraft.knarlib.formatting.TimeFormatter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/** /**
* A class to generate a string containing all information about a quest's planner info * A class to generate a string containing all information about a quest's planner info
@ -42,13 +46,13 @@ public class QuestPlannerInfoGenerator {
//Quest only becomes available after the start date //Quest only becomes available after the start date
if (planner.hasStart()) { if (planner.hasStart()) {
plannerInfo.append("<li>Quest available from "); plannerInfo.append("<li>Quest available from ");
plannerInfo.append(TimeFormatter.formatTimestamp(planner.getStartInMillis())).append("</li>"); plannerInfo.append(formatTimestamp(planner.getStartInMillis())).append("</li>");
} }
//Quest is only available until the end date //Quest is only available until the end date
if (planner.hasEnd()) { if (planner.hasEnd()) {
plannerInfo.append("<li>Quest available until "); plannerInfo.append("<li>Quest available until ");
plannerInfo.append(TimeFormatter.formatTimestamp(planner.getEndInMillis())).append("</li>"); plannerInfo.append(formatTimestamp(planner.getEndInMillis())).append("</li>");
} }
//Quest availability repeats //Quest availability repeats
@ -61,4 +65,16 @@ public class QuestPlannerInfoGenerator {
return plannerInfo.toString(); return plannerInfo.toString();
} }
/**
* Gets a datetime string for the given timestamp
*
* @param timestamp <p>A timestamp in milliseconds</p>
* @return <p>A datetime string</p>
*/
private String formatTimestamp(long timestamp) {
DateFormat format = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
Date date = new Date(timestamp);
return format.format(date);
}
} }

View File

@ -1,32 +0,0 @@
package net.knarcraft.dynmapcitizens.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 <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 = 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));
}
}

View File

@ -1,98 +0,0 @@
package net.knarcraft.dynmapcitizens.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static net.knarcraft.dynmapcitizens.formatting.StringFormatter.replacePlaceholder;
/**
* A helper class for time formatting
*/
public class TimeFormatter {
/**
* Gets a datetime string for the given timestamp
*
* @param timestamp <p>A timestamp in milliseconds</p>
* @return <p>A datetime string</p>
*/
public static String formatTimestamp(long timestamp) {
DateFormat format = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
Date date = new Date(timestamp);
return format.format(date);
}
/**
* 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(long duration) {
if (duration == 0) {
return "immediately";
} else {
double minute = 60;
double hour = minute * 60;
double day = hour * 24;
double week = day * 7;
double month = day * 30;
double year = day * 365;
double decade = year * 10;
Map<Double, String[]> timeUnits = new HashMap<>();
timeUnits.put(decade, new String[]{"decade", "decades"});
timeUnits.put(year, new String[]{"year", "years"});
timeUnits.put(month, new String[]{"month", "months"});
timeUnits.put(week, new String[]{"week", "weeks"});
timeUnits.put(day, new String[]{"day", "days"});
timeUnits.put(hour, new String[]{"hour", "hours"});
timeUnits.put(minute, new String[]{"minute", "minutes"});
timeUnits.put(1D, new String[]{"second", "seconds"});
List<Double> sortedUnits = new ArrayList<>(timeUnits.keySet());
Collections.sort(sortedUnits);
Collections.reverse(sortedUnits);
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, "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, String translatableMessage, boolean castToInt) {
String durationFormat = "{duration} {unit}";
durationFormat = replacePlaceholder(durationFormat, "{unit}", translatableMessage);
return replacePlaceholder(durationFormat, "{duration}", castToInt ? String.valueOf((int) duration) :
String.valueOf(duration));
}
}

View File

@ -49,7 +49,24 @@ en:
QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM: "<li>{questName}</li>" QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM: "<li>{questName}</li>"
QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT: "<li>Required items:<ul>{requiredItems}</ul></li>" QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT: "<li>Required items:<ul>{requiredItems}</ul></li>"
QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM: "<li>{itemName}</li>" QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM: "<li>{itemName}</li>"
QUESTS_REQUIREMENTS_MCMMO_SKILL: "<li>Requires mcMMO skill {skill} at level {level}</li>" QUESTS_REQUIREMENTS_MC_MMO_SKILL: "<li>Requires mcMMO skill {skill} at level {level}</li>"
QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT: "<li>Required permissions:<ul>{permissions}</ul></li>" QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT: "<li>Required permissions:<ul>{permissions}</ul></li>"
QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM: "<li>{permission}</li>" QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM: "<li>{permission}</li>"
DURATION_FORMAT: "in {time} {unit}"
UNIT_NOW: "imminently"
UNIT_SECOND: "second"
UNIT_SECONDS: "seconds"
UNIT_MINUTE: "minute"
UNIT_MINUTES: "minutes"
UNIT_HOUR: "hour"
UNIT_HOURS: "hours"
UNIT_DAY: "day"
UNIT_DAYS: "days"
UNIT_WEEK: "week"
UNIT_WEEKS: "weeks"
UNIT_MONTH: "month"
UNIT_MONTHS: "months"
UNIT_YEAR: "year"
UNIT_YEARS: "years"
UNIT_DECADE: "decade"
UNIT_DECADES: "decades"