Skip to content

Commit

Permalink
Add support for hex color codes in locale
Browse files Browse the repository at this point in the history
  • Loading branch information
nossr50 committed Apr 13, 2024
1 parent 4d98d25 commit c0952a2
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Changelog.txt
@@ -1,4 +1,8 @@
Version 2.2.006
Added support for hex color codes in the locale file, uses the format &#RRGGBB (see notes)
Fixed a bug where sometimes the locale name of a skill would get lowercased
Fixed en_US locale string 'Commands.Skill.Leaderboard' not being colored properly
Fixed skill commands incorrectly telling you to use their locale name, this isn't currently possible
Updated outdated wiki URLs in commands to point to the new wiki
Removed the msg about skills being migrated to a new system when using /mmoinfo command
Added new config custom_item_support.yml
Expand All @@ -7,6 +11,10 @@ Version 2.2.006
Added new locale entry 'Anvil.Salvage.Reject.CustomModelData'

NOTES:
Hex Color support in locale files is here!
The hex color code format for the locale files is &#RRGGBB
An example entry applying yellow as a hex color code would look like this:
Axes.SkillName=&#FFFF00Axes
Let me know in detail what kind of support you'd like to see in mcMMO regarding custom items, I'm open to suggestions.
This update adds a new config file to allow server owners to disable repair or salvage on items with custom models,
This prevention mechanism is not enabled by default, change the settings in custom_item_support.yml if you want to enable it.
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Expand Up @@ -259,6 +259,12 @@
</repositories>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
Expand Down
Expand Up @@ -106,7 +106,7 @@ protected List<String> statsDisplay(Player player, float skillValue, boolean has

@Override
protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>();
final List<Component> textComponents = new ArrayList<>();

TextComponentFactory.getSubSkillTextComponents(player, textComponents, PrimarySkillType.AXES);

Expand Down
18 changes: 8 additions & 10 deletions src/main/java/com/gmail/nossr50/commands/skills/SkillCommand.java
Expand Up @@ -31,7 +31,6 @@

public abstract class SkillCommand implements TabExecutor {
protected PrimarySkillType skill;
private final String skillName;

protected DecimalFormat percent = new DecimalFormat("##0.00%");
protected DecimalFormat decimal = new DecimalFormat("##0.00");
Expand All @@ -40,7 +39,6 @@ public abstract class SkillCommand implements TabExecutor {

public SkillCommand(PrimarySkillType skill) {
this.skill = skill;
skillName = mcMMO.p.getSkillTools().getLocalizedSkillName(skill);
skillGuideCommand = new SkillGuideCommand(skill);
}

Expand Down Expand Up @@ -76,7 +74,8 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
permissionsCheck(player);
dataCalculations(player, skillValue);

sendSkillCommandHeader(player, mcMMOPlayer, (int) skillValue);
sendSkillCommandHeader(mcMMO.p.getSkillTools().getLocalizedSkillName(skill),
player, mcMMOPlayer, (int) skillValue);

//Make JSON text components
List<Component> subskillTextComponents = getTextComponents(player);
Expand Down Expand Up @@ -139,15 +138,14 @@ private void getStatMessages(Player player, boolean isLucky, boolean hasEnduranc
}
}

player.sendMessage(LocaleLoader.getString("Guides.Available", skillName, skillName.toLowerCase(Locale.ENGLISH)));
final String skillName = mcMMO.p.getSkillTools().getLocalizedSkillName(skill);
player.sendMessage(LocaleLoader.getString("Guides.Available",
skillName,
skillName.toLowerCase(Locale.ENGLISH)));
}

private void sendSkillCommandHeader(Player player, McMMOPlayer mcMMOPlayer, int skillValue) {
ChatColor hd1 = ChatColor.DARK_AQUA;
ChatColor c1 = ChatColor.GOLD;
ChatColor c2 = ChatColor.RED;


private void sendSkillCommandHeader(String skillName, Player player, McMMOPlayer mcMMOPlayer, int skillValue) {
// send header
player.sendMessage(LocaleLoader.getString("Skills.Overhaul.Header", skillName));

if(!SkillTools.isChildSkill(skill))
Expand Down
67 changes: 64 additions & 3 deletions src/main/java/com/gmail/nossr50/locale/LocaleLoader.java
Expand Up @@ -16,6 +16,8 @@
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class LocaleLoader {
private static final String BUNDLE_ROOT = "com.gmail.nossr50.locale.locale";
Expand All @@ -24,6 +26,9 @@ public final class LocaleLoader {
private static ResourceBundle bundle = null;
private static ResourceBundle filesystemBundle = null;
private static ResourceBundle enBundle = null;
// Matches the pattern &#RRGGBB
private static final Pattern hexPattern = Pattern.compile("&#([A-Fa-f0-9]{6})");
private static final Pattern minecraftHexPattern = Pattern.compile("§x(§[A-Fa-f0-9])(§[A-Fa-f0-9])(§[A-Fa-f0-9])(§[A-Fa-f0-9])(§[A-Fa-f0-9])(§[A-Fa-f0-9])");

private LocaleLoader() {}

Expand All @@ -48,8 +53,6 @@ public static String getString(String key, Object... messageArguments) {
return formatString(rawMessage, messageArguments);
}

//TODO: Remove this hacky crap with something better later

/**
* Gets the appropriate TextComponent representation of a formatted string from the Locale files.
*
Expand Down Expand Up @@ -258,9 +261,14 @@ private static String getLocaleHelpText() {
@NotNull
private static String getExamples() {
return """
This.Is.An.Example.Put.Locale.Keys.Here.One=&aExample text using hex color codes
This.Is.An.Example.Put.Locale.Keys.Here.One=&aExample text using simplified minecraft color codes
This.Is.An.Example.Put.Locale.Keys.Here.Two=[[DARK_AQUA]]Example text using our own color codes
This.Is.An.Example.Put.Locale.Keys.Here.Three=Example text with no colors
This.Is.An.Example.Put.Locale.Keys.Here.Four=&#FF0000Example text with red color hex code
This.Is.An.Example.Put.Locale.Keys.Here.Five=&#00FF00Example text with green color hex code
This.Is.An.Example.Put.Locale.Keys.Here.Six=&#0000FFExample text with blue color hex code
This.Is.An.Example.Put.Locale.Keys.Here.Seven=&#FFFF00Example text with yellow color hex code
This.Is.An.Example.Put.Locale.Keys.Here.Eight=&lExample text with bold using simplified minecraft color codes
""";
}

Expand Down Expand Up @@ -304,6 +312,10 @@ private static String getLocaleHelpTextWithoutExamples() {
}

public static String addColors(String input) {
// First check for hex color codes and insert them
input = translateHexColorCodes(input);

// Then check for our own color codes
input = input.replaceAll("\\Q[[BLACK]]\\E", ChatColor.BLACK.toString());
input = input.replaceAll("\\Q[[DARK_BLUE]]\\E", ChatColor.DARK_BLUE.toString());
input = input.replaceAll("\\Q[[DARK_GREEN]]\\E", ChatColor.DARK_GREEN.toString());
Expand All @@ -327,6 +339,7 @@ public static String addColors(String input) {
input = input.replaceAll("\\Q[[MAGIC]]\\E", ChatColor.MAGIC.toString());
input = input.replaceAll("\\Q[[RESET]]\\E", ChatColor.RESET.toString());

// Then check for the typical color codes
input = input.replaceAll("\\Q&0\\E", ChatColor.BLACK.toString());
input = input.replaceAll("\\Q&1\\E", ChatColor.DARK_BLUE.toString());
input = input.replaceAll("\\Q&2\\E", ChatColor.DARK_GREEN.toString());
Expand All @@ -352,4 +365,52 @@ public static String addColors(String input) {

return input;
}

/**
* Translates hex color codes to the appropriate Minecraft color codes.
* <p>
* Hex color codes are in the format of &#RRGGBB
* Minecraft color codes are in the format of §x§R§R§G§G§B§B
* Where R, G, and B are the red, green, and blue values respectively.
* The §x is a special character that tells Minecraft to use the following color codes as hex values.
* The §R§R is the red value, the §G§G is the green value, and the §B§B is the blue value.
* Example: §x§R§R§G§G§B§B is the equivalent of the hex color code &#RRGGBB
* </p>
* @param messageWithHex The message with hex color codes to translate
* @return The message with the hex color codes translated to Minecraft color codes
*/
public static String translateHexColorCodes(String messageWithHex) {
if(messageWithHex == null) {
return null;
}

final Matcher matcher = hexPattern.matcher(messageWithHex);
final StringBuilder buffer = new StringBuilder(messageWithHex.length() + 4 * 8);
while (matcher.find()) {
String group = matcher.group(1);
String hexEquivalent = "§x" +
"§" + group.charAt(0) + "§" + group.charAt(1) +
"§" + group.charAt(2) + "§" + group.charAt(3) +
"§" + group.charAt(4) + "§" + group.charAt(5);
matcher.appendReplacement(buffer, hexEquivalent);
}
return matcher.appendTail(buffer).toString();
}

// Method to reverse the transformation from Minecraft color codes to hex codes
public static String reverseTranslateHexColorCodes(String minecraftColorString) {
// Matches the Minecraft color pattern: §x§R§R§G§G§B§B
Matcher matcher = minecraftHexPattern.matcher(minecraftColorString);
StringBuilder buffer = new StringBuilder();

while (matcher.find()) {
String hexColor = "#" +
matcher.group(1).substring(1) + matcher.group(2).substring(1) +
matcher.group(3).substring(1) + matcher.group(4).substring(1) +
matcher.group(5).substring(1) + matcher.group(6).substring(1);
matcher.appendReplacement(buffer, "&" + hexColor);
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
Expand Up @@ -20,6 +20,7 @@
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.text.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;

import java.util.ArrayList;
Expand All @@ -42,8 +43,8 @@ private static void registerSkillCommands() {
command.setDescription(LocaleLoader.getString("Commands.Description.Skill", StringUtils.getCapitalized(localizedName)));
command.setPermission("mcmmo.commands." + commandName);
command.setPermissionMessage(permissionsMessage);
command.setUsage(LocaleLoader.getString("Commands.Usage.0", localizedName));
command.setUsage(command.getUsage() + "\n" + LocaleLoader.getString("Commands.Usage.2", localizedName, "?", "[" + LocaleLoader.getString("Commands.Usage.Page") + "]"));
command.setUsage(LocaleLoader.getString("Commands.Usage.0", commandName));
command.setUsage(command.getUsage() + "\n" + LocaleLoader.getString("Commands.Usage.2", commandName, "?", "[" + LocaleLoader.getString("Commands.Usage.Page") + "]"));

switch (skill) {
case ACROBATICS:
Expand Down
Expand Up @@ -4,6 +4,7 @@
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.player.PlayerLevelUtils;
import com.gmail.nossr50.util.text.StringUtils;
import org.bukkit.boss.BarColor;
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/com/gmail/nossr50/util/skills/SkillTools.java
Expand Up @@ -349,8 +349,7 @@ public static boolean isChildSkill(PrimarySkillType primarySkillType) {
* @return the localized name for a {@link PrimarySkillType}
*/
public String getLocalizedSkillName(PrimarySkillType primarySkillType) {
//TODO: Replace with current impl
return StringUtils.getCapitalized(LocaleLoader.getString(StringUtils.getCapitalized(primarySkillType.toString()) + ".SkillName"));
return LocaleLoader.getString(StringUtils.getCapitalized(primarySkillType.toString()) + ".SkillName");
}

public boolean doesPlayerHaveSkillPermission(Player player, PrimarySkillType primarySkillType) {
Expand Down
Expand Up @@ -236,7 +236,7 @@ private static ClickEvent getUrlClickEvent(String url) {

private static Component getSubSkillTextComponent(Player player, SubSkillType subSkillType) {
//Get skill name
String skillName = subSkillType.getLocaleName();
final String skillName = subSkillType.getLocaleName();

boolean skillUnlocked = RankUtils.hasUnlockedSubskill(player, subSkillType);

Expand Down Expand Up @@ -290,6 +290,11 @@ private static TextComponent.Builder initNewSkillTextComponent(Player player, St
return textComponent;
}

private static TextComponent.Builder detectLegacyColors(String msg) {
// TODO: Impl
return null;
}

private static Component getSubSkillHoverComponent(Player player, AbstractSubSkill abstractSubSkill) {
return getSubSkillHoverEventJSON(abstractSubSkill, player);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/locale/locale_en_US.properties
Expand Up @@ -784,7 +784,7 @@ Commands.XPBar.Reset=&6XP Bar settings for mcMMO have been reset.
Commands.XPBar.SettingChanged=&6XP Bar setting for &a{0}&6 is now set to &a{1}
Commands.Skill.Invalid=That is not a valid skillname!
Commands.Skill.ChildSkill=Child skills are not valid for this command!
Commands.Skill.Leaderboard=--mcMMO &9{0}&e Leaderboard--
Commands.Skill.Leaderboard=-&e-mcMMO &9{0}&e Leaderboard--
Commands.SkillInfo=&a- View detailed information about a skill
Commands.Stats=&a- View your mcMMO stats
Commands.ToggleAbility=&a- Toggle ability activation with right click
Expand Down
77 changes: 77 additions & 0 deletions src/test/java/com/gmail/nossr50/locale/LocaleLoaderTest.java
@@ -0,0 +1,77 @@
package com.gmail.nossr50.locale;

import org.bukkit.ChatColor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class LocaleLoaderTest {

@BeforeEach
void setUp() {
}

@AfterEach
void tearDown() {
}

@ParameterizedTest
@ValueSource(strings = {"§cTest", "[[RED]]Test"})
void addColorsShouldAddColorRed(String testString) {
// When
final String result = LocaleLoader.addColors(testString);

// Then
assertThat(result).isEqualTo(ChatColor.RED + "Test");
}

// hex colors test
@Test
void translateHexColorCodesShouldAddRed() {
// Given
final String testString = "&#FF0000Test";

// When
final String result = LocaleLoader.translateHexColorCodes(testString);

// Then
final String expectedResult = "§x§F§F§0§0§0§0Test";
assertThat(result).isEqualTo(expectedResult);
}

@Test
void reverseTranslateHexColorCodesShouldRemoveRed() {
// Given
final String testString = "§x§F§F§0§0§0§0Test";

// When
final String result = LocaleLoader.reverseTranslateHexColorCodes(testString);

// Then
final String expectedResult = "&#FF0000Test";
assertThat(result).isEqualTo(expectedResult);
}

@ParameterizedTest
@ValueSource(strings = {"&#FF0000Te&#0000FFst", "&#FF0000Te[[RED]]st", "[[BLUE]]Te[[RED]]st", "§9Te§cst"})
void addColorsShouldAddRedAndBlue(String testString) {
// When
final String result = LocaleLoader.addColors(testString);

// TODO: Hacky, clean this up sometime in the future
// Then
// All legal representations of the same string
final List<String> expectedResults = List.of("§x§F§F§0§0§0§0Te§x§0§0§0§0§F§Fst",
"§x§F§F§0§0§0§0Te§x§0§0§0§0§F§Fst",
"§x§F§F§0§0§0§0Te§cst",
"§9Te§cst");
assertThat(expectedResults).contains(result);
}
}

0 comments on commit c0952a2

Please sign in to comment.