Added language file to properly translate stat-keys into Minecraft text (#113, #79)

This commit is contained in:
Artemis-the-gr8 2022-08-15 01:01:59 +02:00
parent b57d8af335
commit 8924e02bba
9 changed files with 216 additions and 48 deletions

View File

@ -11,6 +11,7 @@ import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.listeners.JoinListener;
import com.gmail.artemis.the.gr8.playerstats.msg.MessageBuilder;
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatCalculator;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
@ -23,9 +24,11 @@ import org.jetbrains.annotations.NotNull;
public final class Main extends JavaPlugin {
private static Main instance;
private static BukkitAudiences adventure;
private static ConfigHandler config;
private static LanguageKeyHandler languageKeyHandler;
private static OfflinePlayerHandler offlinePlayerHandler;
private static EnumHandler enumHandler;
@ -71,6 +74,10 @@ public final class Main extends JavaPlugin {
this.getLogger().info("Disabled PlayerStats!");
}
public static @NotNull Main getInstance() {
return instance;
}
public static @NotNull BukkitAudiences getAdventure() throws IllegalStateException {
if (adventure == null) {
throw new IllegalStateException("Tried to access Adventure without PlayerStats being enabled!");
@ -87,11 +94,18 @@ public final class Main extends JavaPlugin {
public static @NotNull OfflinePlayerHandler getOfflinePlayerHandler() throws IllegalStateException {
if (offlinePlayerHandler == null) {
throw new IllegalStateException("PlayerStats does not seem to be fully loaded!");
throw new IllegalStateException("PlayerStats does not seem to be loaded!");
}
return offlinePlayerHandler;
}
public static @NotNull LanguageKeyHandler getLanguageKeyHandler() {
if (languageKeyHandler == null) {
languageKeyHandler = new LanguageKeyHandler();
}
return languageKeyHandler;
}
public static @NotNull EnumHandler getEnumHandler() {
if (enumHandler == null) {
enumHandler = new EnumHandler();
@ -121,10 +135,12 @@ public final class Main extends JavaPlugin {
}
private void initializeMainClasses() {
instance = this;
adventure = BukkitAudiences.create(this);
config = new ConfigHandler(this);
enumHandler = new EnumHandler();
languageKeyHandler = new LanguageKeyHandler();
offlinePlayerHandler = new OfflinePlayerHandler();
shareManager = new ShareManager(config);

View File

@ -6,6 +6,7 @@ import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.NumberFormatter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Statistic;
import org.jetbrains.annotations.Nullable;
/** Formats messages meant for usage outside PlayerStats.
@ -49,21 +50,24 @@ public interface ApiFormatter {
@return ________ [PlayerStats] ________ in rainbow colors*/
TextComponent getRainbowPluginPrefixAsTitle();
/** Gets the default top-stat-title for a Statistic of Type.Untyped.
@return Top [topStatSize] [stat-name]*/
TextComponent getTopStatTitle(int topStatSize, Statistic statistic);
/** Gets a formatted message that displays the name of this Statistic as it is
displayed by PlayerStats. If this Statistic is not of Type.Untyped,
include the name of the relevant sub-statistic (block, item or entity).
@param statistic the Statistic enum constant to display the name of
@param subStatName where necessary, the name of the Material or EntityType to include,
acquired by doing #toString() on the Material/EntityType in question
@return [stat-name] [sub-stat-name]*/
TextComponent getStatTitle(Statistic statistic, @Nullable String subStatName);
/** Gets the top-stat-title for a statistic that has a sub-statistic (block, item or entity).
/** Gets a formatted message that displays the name of this Statistic as it is
displayed by PlayerStats in a top-stat-message. If this Statistic is not of Type.Untyped,
include the name of the relevant sub-statistic (block, item or entity).
@param statistic the Statistic enum constant for this message
@param subStatName the name of the Material or EntityType of this statistic-lookup,
@param subStatName the name of the Material or EntityType to include,
acquired by doing #toString() on the Material/EntityType in question
@param topStatSize the size of the top-list this title is for
@return Top [topStatSize] [stat-name] [sub-stat-name] */
TextComponent getTopStatTitle(int topStatSize, Statistic statistic, String subStatName);
/** Gets the top-stat-title with the specified {@link Unit} in the title.
@return Top [topStatSize] [stat-name] [unit-name] */
TextComponent getTopStatTitle(int topStatSize, Statistic statistic, Unit unit);
TextComponent getTopStatTitle(int topStatSize, Statistic statistic, @Nullable String subStatName);
/** Formats the input into a single top-statistic line. The stat-number
is formatted into the most suitable {@link Unit} based on the provided Statistic.

View File

@ -6,17 +6,17 @@ import org.jetbrains.annotations.NotNull;
/** All the units PlayerStats can display statistics in, separated by Type.*/
public enum Unit {
NUMBER (Type.UNTYPED, "Times"),
KM (Type.DISTANCE, "KM"),
KM (Type.DISTANCE, "km"),
MILE (Type.DISTANCE, "Miles"),
BLOCK (Type.DISTANCE, "Blocks"),
CM (Type.DISTANCE, "CM"),
CM (Type.DISTANCE, "cm"),
HP (Type.DAMAGE, "HP"),
HEART (Type.DAMAGE, "Hearts"),
DAY (Type.TIME, "Days"),
HOUR (Type.TIME, "Hours"),
MINUTE (Type.TIME, "Minutes"),
SECOND (Type.TIME, "Seconds"),
TICK (Type.TIME, "Ticks");
DAY (Type.TIME, "days"),
HOUR (Type.TIME, "hours"),
MINUTE (Type.TIME, "minutes"),
SECOND (Type.TIME, "seconds"),
TICK (Type.TIME, "ticks");
private final Type type;
private final String label;

View File

@ -1,5 +1,6 @@
package com.gmail.artemis.the.gr8.playerstats.msg;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.api.ApiFormatter;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
@ -51,7 +52,7 @@ public final class MessageBuilder implements ApiFormatter {
componentFactory = factory;
formatter = new NumberFormatter();
languageKeyHandler = new LanguageKeyHandler();
languageKeyHandler = Main.getLanguageKeyHandler();
}
public static MessageBuilder defaultBuilder(ConfigHandler config) {
@ -201,8 +202,8 @@ public final class MessageBuilder implements ApiFormatter {
}
@Override
public TextComponent getTopStatTitle(int topListSize, Statistic statistic) {
return getTopStatTitleComponent(topListSize, statistic, null, null);
public TextComponent getStatTitle(Statistic statistic, @Nullable String subStatName) {
return getTopStatTitleComponent(0, statistic, subStatName, null);
}
@Override
@ -210,11 +211,6 @@ public final class MessageBuilder implements ApiFormatter {
return getTopStatTitleComponent(topListSize, statistic, subStatName, null);
}
@Override
public TextComponent getTopStatTitle(int topListSize, Statistic statistic, Unit unit) {
return getTopStatTitleComponent(topListSize, statistic, null, unit);
}
@Override
public TextComponent formatTopStatLine(int positionInTopList, String playerName, long statNumber, Statistic statistic) {
TextComponent statNumberComponent = getStatNumberComponent(statNumber, Target.TOP, statistic);
@ -289,7 +285,6 @@ public final class MessageBuilder implements ApiFormatter {
<br>- If both parameters are null, the formattedComponent will be returned as is.</br>*/
public BiFunction<Integer, CommandSender, TextComponent> formattedPlayerStatFunction(int stat, @NotNull RequestSettings request) {
TextComponent playerStat = formatPlayerStat(request.getPlayerName(), stat, request.getStatistic(), request.getSubStatEntryName());
return getFormattingFunction(playerStat, Target.PLAYER);
}
@ -400,14 +395,21 @@ public final class MessageBuilder implements ApiFormatter {
getStatUnitComponent(statistic, Target.TOP) :
getStatUnitComponent(unit, Target.TOP);
return Component.text()
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP))
.append(space())
.append(componentFactory.titleNumber(topListSize))
.append(space())
.append(getStatAndSubStatNameComponent(statistic, subStatName, Target.TOP)) //space is provided by statUnitComponent
.append(statUnit)
.build();
if (topListSize == 0) {
return Component.text()
.append(getStatAndSubStatNameComponent(statistic, subStatName, Target.TOP))
.append(statUnit) //space is provided by statUnitComponent
.build();
} else {
return Component.text()
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP))
.append(space())
.append(componentFactory.titleNumber(topListSize))
.append(space())
.append(getStatAndSubStatNameComponent(statistic, subStatName, Target.TOP))
.append(statUnit) //space is provided by statUnitComponent
.build();
}
}
private TextComponent getTopStatListComponent(LinkedHashMap<String, Integer> topStats, Statistic statistic) {
@ -460,6 +462,9 @@ public final class MessageBuilder implements ApiFormatter {
case BLOCK -> languageKeyHandler.getBlockKey(EnumHandler.getBlockEnum(subStatName));
case ITEM -> languageKeyHandler.getItemKey(EnumHandler.getItemEnum(subStatName));
};
if (subStatKey == null) {
subStatKey = StringUtils.prettify(subStatName);
}
return componentFactory.statAndSubStatNameTranslatable(statKey, subStatKey, target);
}

View File

@ -6,8 +6,7 @@ import net.kyori.adventure.text.*;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
/** A utility class for handling Adventure's Components.
Its main function is currently to help serialize Components into String.*/
/** A small utility class for turning PlayerStats' custom Components into String. */
public final class ComponentUtils {
/** Returns a LegacyComponentSerializer that is capable of serializing TranslatableComponents,
@ -29,6 +28,7 @@ public final class ComponentUtils {
TextComponent.Builder temp = Component.text();
trans.iterator(ComponentIteratorType.DEPTH_FIRST, ComponentIteratorFlag.INCLUDE_TRANSLATABLE_COMPONENT_ARGUMENTS)
.forEachRemaining(component -> {
//copy the style to the temp builder, because the translatable component that follows it has no style itself
if (component instanceof TextComponent text) {
if (!text.children().isEmpty()) {
text.iterator(ComponentIteratorType.DEPTH_FIRST).forEachRemaining(component1 -> {
@ -37,27 +37,31 @@ public final class ComponentUtils {
}
});
}
} else if (component instanceof TranslatableComponent translatable) {
if (translatable.key().contains("entity")) {
}
//isolate the translatable component with the entity inside
else if (component instanceof TranslatableComponent translatable) {
if (translatable.key().contains("entity.")) {
temp.append(Component.space())
.append(Component.text("(")
.append(Component.text(StringUtils.prettify(LanguageKeyHandler.convertToName(translatable.key()))))
.append(Component.text(
StringUtils.prettify(LanguageKeyHandler.convertToName(translatable.key()))))
.append(Component.text(")")));
totalPrettyName.append(
serializer.serialize(temp.build()));
} else if (!LanguageKeyHandler.isKeyForEntityKilledByArg(translatable.key())) {
}
else if (!LanguageKeyHandler.isKeyForEntityKilledByArg(translatable.key())) {
totalPrettyName.append(
StringUtils.prettify(
LanguageKeyHandler.convertToName(
translatable.key())));
LanguageKeyHandler.getStatKeyTranslation(
translatable.key()));
}
}
});
}
else if (trans.key().startsWith("stat")) {
return LanguageKeyHandler.getStatKeyTranslation(trans.key());
}
else {
return StringUtils.prettify(
LanguageKeyHandler.convertToName(
trans.key()));
return StringUtils.prettify(LanguageKeyHandler.convertToName(trans.key()));
}
return totalPrettyName.toString();
})

View File

@ -58,7 +58,7 @@ public final class EasterEggProvider {
}
}
case "0dc5336b-acd2-4dc3-a5e9-0aa9b8f113f7" -> {
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
if (sillyNumberIsBetween(sillyNumber, 0, 100)) {
playerName = "<gradient:#f73bdb:#fc8bec:#f73bdb>an UwU sister</gradient>";
}
}

View File

@ -1,13 +1,18 @@
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
@ -15,9 +20,31 @@ import java.util.HashMap;
public final class LanguageKeyHandler {
private static HashMap<Statistic, String> statNameKeys;
private static File languageKeyFile;
private static FileConfiguration languageKeys;
public LanguageKeyHandler() {
statNameKeys = generateStatNameKeys();
loadFile();
}
private static void loadFile() {
Main plugin = Main.getInstance();
languageKeyFile = new File(plugin.getDataFolder(), "language.yml");
if (!languageKeyFile.exists()) {
plugin.saveResource("language.yml", false);
}
languageKeys = YamlConfiguration.loadConfiguration(languageKeyFile);
}
public static void reloadFile() {
if (!languageKeyFile.exists()) {
loadFile();
} else {
languageKeys = YamlConfiguration.loadConfiguration(languageKeyFile);
MyLogger.logLowLevelMsg("Language file reloaded!");
}
}
/** Checks if a given Key is the language key "stat_type.minecraft.killed"
@ -88,6 +115,26 @@ public final class LanguageKeyHandler {
return key.replace(toReplace, "");
}
private static @Nullable String convertToNormalStatKey(String statKey) {
if (isKeyForKillEntity(statKey)) {
return "stat_type.minecraft.killed";
} else if (isKeyForEntityKilledBy(statKey)) {
return "stat_type.minecraft.killed_by";
} else if (isKeyForEntityKilledByArg(statKey)) {
return null;
} else {
return statKey;
}
}
public static String getStatKeyTranslation(String statKey) {
String realKey = convertToNormalStatKey(statKey);
if (realKey == null) {
return "";
}
return languageKeys.getString(realKey);
}
public String getStatKey(@NotNull Statistic statistic) {
if (statistic.getType() == Statistic.Type.UNTYPED) {
return "stat.minecraft." + statNameKeys.get(statistic);

View File

@ -6,6 +6,7 @@ import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatCalculator;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
@ -81,6 +82,7 @@ public final class ReloadThread extends Thread {
private void reloadEverything() {
MyLogger.setDebugLevel(config.getDebugLevel());
LanguageKeyHandler.reloadFile();
OutputManager.updateMessageBuilders();
OfflinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers());
ShareManager.updateSettings(config);

View File

@ -0,0 +1,90 @@
# ------------------------------------------------------------------------------------------------------ #
# PlayerStats Language File #
# ------------------------------------------------------------------------------------------------------ #
stat_type.minecraft.mined: "Times Mined"
stat_type.minecraft.crafted: "Times Crafted"
stat_type.minecraft.used: "Times Used"
stat_type.minecraft.broken: "Times Broken"
stat_type.minecraft.picked_up: "Picked Up"
stat_type.minecraft.dropped: "Dropped"
stat_type.minecraft.killed: "Times Killed"
stat_type.minecraft.killed_by: "Number of Times Killed By"
stat.minecraft.animals_bred: "Animals Bred"
stat.minecraft.aviate_one_cm: "Distance by Elytra"
stat.minecraft.clean_armor: "Armor Pieces Cleaned"
stat.minecraft.clean_banner: "Banners Cleaned"
stat.minecraft.clean_shulker_box: "Shulker Boxes Cleaned"
stat.minecraft.climb_one_cm: "Distance Climbed"
stat.minecraft.bell_ring: "Bells Rung"
stat.minecraft.target_hit: "Targets Hit"
stat.minecraft.boat_one_cm: "Distance by Boat"
stat.minecraft.crouch_one_cm: "Distance Crouched"
stat.minecraft.damage_dealt: "Damage Dealt"
stat.minecraft.damage_dealt_absorbed: "Damage Dealt (Absorbed)"
stat.minecraft.damage_dealt_resisted: "Damage Dealt (Resisted)"
stat.minecraft.damage_taken: "Damage Taken"
stat.minecraft.damage_blocked_by_shield: "Damage Blocked by Shield"
stat.minecraft.damage_absorbed: "Damage Absorbed"
stat.minecraft.damage_resisted: "Damage Resisted"
stat.minecraft.deaths: "Number of Deaths"
stat.minecraft.walk_under_water_one_cm: "Distance Walked under Water"
stat.minecraft.drop: "Items Dropped"
stat.minecraft.eat_cake_slice: "Cake Slices Eaten"
stat.minecraft.enchant_item: "Items Enchanted"
stat.minecraft.fall_one_cm: "Distance Fallen"
stat.minecraft.fill_cauldron: "Cauldrons Filled"
stat.minecraft.fish_caught: "Fish Caught"
stat.minecraft.fly_one_cm: "Distance Flown"
stat.minecraft.horse_one_cm: "Distance by Horse"
stat.minecraft.inspect_dispenser: "Dispensers Searched"
stat.minecraft.inspect_dropper: "Droppers Searched"
stat.minecraft.inspect_hopper: "Hoppers Searched"
stat.minecraft.interact_with_anvil: "Interactions with Anvil"
stat.minecraft.interact_with_beacon: "Interactions with Beacon"
stat.minecraft.interact_with_brewingstand: "Interactions with Brewing Stand"
stat.minecraft.interact_with_campfire: "Interactions with Campfire"
stat.minecraft.interact_with_cartography_table: "Interactions with Cartography Table"
stat.minecraft.interact_with_crafting_table: "Interactions with Crafting Table"
stat.minecraft.interact_with_furnace: "Interactions with Furnace"
stat.minecraft.interact_with_grindstone: "Interactions with Grindstone"
stat.minecraft.interact_with_lectern: "Interactions with Lectern"
stat.minecraft.interact_with_loom: "Interactions with Loom"
stat.minecraft.interact_with_blast_furnace: "Interactions with Blast Furnace"
stat.minecraft.interact_with_smithing_table: "Interactions with Smithing Table"
stat.minecraft.interact_with_smoker: "Interactions with Smoker"
stat.minecraft.interact_with_stonecutter: "Interactions with Stonecutter"
stat.minecraft.jump: "Jumps"
stat.minecraft.junk_fished: "Junk Fished"
stat.minecraft.leave_game: "Games Quit"
stat.minecraft.minecart_one_cm: "Distance by Minecart"
stat.minecraft.mob_kills: "Mob Kills"
stat.minecraft.open_barrel: "Barrels Opened"
stat.minecraft.open_chest: "Chests Opened"
stat.minecraft.open_enderchest: "Ender Chests Opened"
stat.minecraft.open_shulker_box: "Shulker Boxes Opened"
stat.minecraft.pig_one_cm: "Distance by Pig"
stat.minecraft.strider_one_cm: "Distance by Strider"
stat.minecraft.player_kills: "Player Kills"
stat.minecraft.play_noteblock: "Note Blocks Played"
stat.minecraft.play_time: "Time Played"
stat.minecraft.play_record: "Music Discs Played"
stat.minecraft.pot_flower: "Plants Potted"
stat.minecraft.raid_trigger: "Raids Triggered"
stat.minecraft.raid_win: "Raids Won"
stat.minecraft.ring_bell: "Bells Rung"
stat.minecraft.sleep_in_bed: "Times Slept in a Bed"
stat.minecraft.sneak_time: "Sneak Time"
stat.minecraft.sprint_one_cm: "Distance Sprinted"
stat.minecraft.walk_on_water_one_cm: "Distance Walked on Water"
stat.minecraft.swim_one_cm: "Distance Swum"
stat.minecraft.talked_to_villager: "Talked to Villagers"
stat.minecraft.time_since_rest: "Time Since Last Rest"
stat.minecraft.time_since_death: "Time Since Last Death"
stat.minecraft.total_world_time: "Time with World Open"
stat.minecraft.traded_with_villager: "Traded with Villagers"
stat.minecraft.treasure_fished: "Treasure Fished"
stat.minecraft.trigger_trapped_chest: "Trapped Chests Triggered"
stat.minecraft.tune_noteblock: "Note Blocks Tuned"
stat.minecraft.use_cauldron: "Water Taken from Cauldron"
stat.minecraft.walk_one_cm: "Distance Walked"