Skip to content

Commit

Permalink
Implement some suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Camotoy committed Apr 21, 2023
1 parent 6a2056b commit 6f35d82
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,7 @@ public final class NamedTextColor implements TextColor {
*/
public static final NamedTextColor WHITE = new NamedTextColor("white", WHITE_VALUE);

/**
* @since 4.14.0
*/
public static final List<NamedTextColor> VALUES = Collections.unmodifiableList(Arrays.asList(BLACK, DARK_BLUE, DARK_GREEN, DARK_AQUA, DARK_RED, DARK_PURPLE, GOLD, GRAY, DARK_GRAY, BLUE, GREEN, AQUA, RED, LIGHT_PURPLE, YELLOW, WHITE));
private static final List<NamedTextColor> VALUES = Collections.unmodifiableList(Arrays.asList(BLACK, DARK_BLUE, DARK_GREEN, DARK_AQUA, DARK_RED, DARK_PURPLE, GOLD, GRAY, DARK_GRAY, BLUE, GREEN, AQUA, RED, LIGHT_PURPLE, YELLOW, WHITE));
/**
* An index of name to color.
*
Expand Down Expand Up @@ -240,12 +237,16 @@ public final class NamedTextColor implements TextColor {
return (NamedTextColor) any;
}

return nearestColorTo(VALUES, any);
}

public static <C extends TextColor> @NotNull C nearestColorTo(final @NotNull List<C> values, final @NotNull TextColor any) {
requireNonNull(any, "color");

float matchedDistance = Float.MAX_VALUE;
NamedTextColor match = VALUES.get(0);
for (int i = 0, length = VALUES.size(); i < length; i++) {
final NamedTextColor potential = VALUES.get(i);
C match = values.get(0);
for (int i = 0, length = values.size(); i < length; i++) {
final C potential = values.get(i);
final float distance = distance(any.asHSV(), potential.asHSV());
if (distance < matchedDistance) {
match = potential;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.kyori.adventure.text.serializer.legacy;

import net.kyori.adventure.text.format.TextFormat;
import org.jetbrains.annotations.NotNull;

/**
* A pair containing a text format and its legacy character equivalent.
* @since 4.14.0
*/
public final class CharacterAndFormat {
private final char character;
private final @NotNull TextFormat format;

public CharacterAndFormat(final char character, final @NotNull TextFormat format) {
this.character = character;
this.format = format;
}

public char getCharacter() {
return character;
}

@NotNull
public TextFormat getFormat() {
return format;
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,62 @@
package net.kyori.adventure.text.serializer.legacy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextFormat;
import net.kyori.adventure.util.HSVLike;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static java.util.Objects.requireNonNull;

public abstract class LegacyCodeConverter {
protected static final char LEGACY_BUNGEE_HEX_CHAR = 'x';

protected final List<TextColor> colors;
protected final List<TextFormat> formats;
protected final String legacyChars;

protected final char character;
protected final char hexCharacter;
protected final boolean hexColours;
protected final boolean useTerriblyStupidHexFormat; // (╯°□°)╯︵ ┻━┻

protected LegacyCodeConverter(final char character, final char hexCharacter, final boolean hexColours, final boolean useTerriblyStupidHexFormat) {
protected LegacyCodeConverter(final List<CharacterAndFormat> characterFormats, final char character, final char hexCharacter, final boolean hexColours, final boolean useTerriblyStupidHexFormat) {
final List<TextColor> colors = new ArrayList<>();
final List<TextFormat> formats = new ArrayList<>(characterFormats.size());
final StringBuilder legacyChars = new StringBuilder(characterFormats.size());
for (int i = 0; i < characterFormats.size(); i++) {
CharacterAndFormat characterAndFormat = characterFormats.get(i);
legacyChars.append(characterAndFormat.getCharacter());
final TextFormat format = characterAndFormat.getFormat();
formats.add(format);
if (format instanceof TextColor) {
colors.add((TextColor) format);
}
}
this.colors = Collections.unmodifiableList(colors);
this.formats = Collections.unmodifiableList(formats);
this.legacyChars = legacyChars.toString();

this.character = character;
this.hexCharacter = hexCharacter;
this.hexColours = hexColours;
this.useTerriblyStupidHexFormat = useTerriblyStupidHexFormat;
}

@NotNull abstract List<TextFormat> getFormats();

@NotNull abstract String getAllLegacyChars();

@NotNull abstract List<? extends TextColor> getLegacyColors();

@NotNull abstract LegacyCodeConverterSupplier createSupplier();

/**
* Find the named colour nearest to the provided colour. Can be overridden to
*
* @param any colour to match
* @return nearest named colour. will always return a value
* @since 4.14.0
*/
protected @NotNull TextColor nearestColorTo(final @NotNull TextColor any) {
return NamedTextColor.nearestColorTo(this.colors, any);
}

private @Nullable FormatCodeType determineFormatType(final char legacy, final String input, final int pos) {
if (pos >= 14) {
// The BungeeCord RGB color format uses a repeating sequence of RGB values, each character formatted
Expand All @@ -47,7 +71,7 @@ protected LegacyCodeConverter(final char character, final char hexCharacter, fin
}
if (legacy == this.hexCharacter && input.length() - pos >= 6) {
return FormatCodeType.KYORI_HEX;
} else if (getAllLegacyChars().indexOf(legacy) != -1) {
} else if (legacyChars.indexOf(legacy) != -1) {
return FormatCodeType.MOJANG_LEGACY;
}
return null;
Expand All @@ -64,7 +88,7 @@ protected LegacyCodeConverter(final char character, final char hexCharacter, fin
return new DecodedFormat(foundFormat, parsed);
}
} else if (foundFormat == FormatCodeType.MOJANG_LEGACY) {
return new DecodedFormat(foundFormat, getFormats().get(getAllLegacyChars().indexOf(legacy)));
return new DecodedFormat(foundFormat, formats.get(legacyChars.indexOf(legacy)));
} else if (foundFormat == FormatCodeType.BUNGEECORD_UNUSUAL_HEX) {
final StringBuilder foundHex = new StringBuilder(6);
for (int i = pos - 1; i >= pos - 11; i -= 2) {
Expand Down Expand Up @@ -113,56 +137,8 @@ String toLegacyCode(TextFormat format) {
format = nearestColorTo(color);
}
}
final int index = getFormats().indexOf(format);
return Character.toString(getAllLegacyChars().charAt(index));
}

/**
* Find the named colour nearest to the provided colour.
*
* @param any colour to match
* @return nearest named colour. will always return a value
* @since 4.14.0
*/
public @NotNull TextColor nearestColorTo(final @NotNull TextColor any) {
if (any instanceof NamedTextColor) {
return any;
}

requireNonNull(any, "color");

float matchedDistance = Float.MAX_VALUE;
final List<? extends TextColor> values = getLegacyColors();
TextColor match = values.get(0);
for (int i = 0, length = values.size(); i < length; i++) {
final TextColor potential = values.get(i);
final float distance = distance(any.asHSV(), potential.asHSV());
if (distance < matchedDistance) {
match = potential;
matchedDistance = distance;
}
if (distance == 0) {
break; // same colour! whoo!
}
}
return match;
}

/**
* Returns a distance metric to the other colour.
*
* <p>This value is unitless and should only be used to compare with other text colours.</p>
*
* @param self the base colour
* @param other colour to compare to
* @return distance metric
*/
protected static float distance(final @NotNull HSVLike self, final @NotNull HSVLike other) {
// weight hue more heavily than saturation and brightness. kind of magic numbers, but is fine for our use case of downsampling to a set of colors
final float hueDistance = 3 * Math.min(Math.abs(self.h() - other.h()), 1f - Math.abs(self.h() - other.h()));
final float saturationDiff = self.s() - other.s();
final float valueDiff = self.v() - other.v();
return hueDistance * hueDistance + saturationDiff * saturationDiff + valueDiff * valueDiff;
final int index = formats.indexOf(format);
return Character.toString(legacyChars.charAt(index));
}

protected enum Reset implements TextFormat {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
Expand All @@ -14,56 +15,56 @@

final class LegacyCodeConverterImpl extends LegacyCodeConverter {

private static final List<TextFormat> FORMATS;
private static final List<CharacterAndFormat> FORMATS;
private static final String LEGACY_CHARS;

static {
// Enumeration order may change - manually

final Map<TextFormat, String> formats = new LinkedHashMap<>(16 + 5 + 1); // colours + decorations + reset

formats.put(NamedTextColor.BLACK, "0");
formats.put(NamedTextColor.DARK_BLUE, "1");
formats.put(NamedTextColor.DARK_GREEN, "2");
formats.put(NamedTextColor.DARK_AQUA, "3");
formats.put(NamedTextColor.DARK_RED, "4");
formats.put(NamedTextColor.DARK_PURPLE, "5");
formats.put(NamedTextColor.GOLD, "6");
formats.put(NamedTextColor.GRAY, "7");
formats.put(NamedTextColor.DARK_GRAY, "8");
formats.put(NamedTextColor.BLUE, "9");
formats.put(NamedTextColor.GREEN, "a");
formats.put(NamedTextColor.AQUA, "b");
formats.put(NamedTextColor.RED, "c");
formats.put(NamedTextColor.LIGHT_PURPLE, "d");
formats.put(NamedTextColor.YELLOW, "e");
formats.put(NamedTextColor.WHITE, "f");

formats.put(TextDecoration.OBFUSCATED, "k");
formats.put(TextDecoration.BOLD, "l");
formats.put(TextDecoration.STRIKETHROUGH, "m");
formats.put(TextDecoration.UNDERLINED, "n");
formats.put(TextDecoration.ITALIC, "o");

formats.put(LegacyCodeConverter.Reset.INSTANCE, "r");

FORMATS = Collections.unmodifiableList(new ArrayList<>(formats.keySet()));
LEGACY_CHARS = String.join("", formats.values());

// assert same length
if (FORMATS.size() != LEGACY_CHARS.length()) {
throw new IllegalStateException("FORMATS length differs from LEGACY_CHARS length");
final List<CharacterAndFormat> formats = new ArrayList<>(16 + 5 + 1); // colours + decorations + reset

formats.add(new CharacterAndFormat('0', NamedTextColor.BLACK));
formats.add(new CharacterAndFormat('1', NamedTextColor.DARK_BLUE));
formats.add(new CharacterAndFormat('2', NamedTextColor.DARK_GREEN));
formats.add(new CharacterAndFormat('3', NamedTextColor.DARK_AQUA));
formats.add(new CharacterAndFormat('4', NamedTextColor.DARK_RED));
formats.add(new CharacterAndFormat('5', NamedTextColor.DARK_PURPLE));
formats.add(new CharacterAndFormat('6', NamedTextColor.GOLD));
formats.add(new CharacterAndFormat('7', NamedTextColor.GRAY));
formats.add(new CharacterAndFormat('8', NamedTextColor.DARK_GRAY));
formats.add(new CharacterAndFormat('9', NamedTextColor.BLUE));
formats.add(new CharacterAndFormat('a', NamedTextColor.GREEN));
formats.add(new CharacterAndFormat('b', NamedTextColor.AQUA));
formats.add(new CharacterAndFormat('c', NamedTextColor.RED));
formats.add(new CharacterAndFormat('d', NamedTextColor.LIGHT_PURPLE));
formats.add(new CharacterAndFormat('e', NamedTextColor.YELLOW));
formats.add(new CharacterAndFormat('f', NamedTextColor.WHITE));

formats.add(new CharacterAndFormat('k', TextDecoration.OBFUSCATED));
formats.add(new CharacterAndFormat('l', TextDecoration.BOLD));
formats.add(new CharacterAndFormat('m', TextDecoration.STRIKETHROUGH));
formats.add(new CharacterAndFormat('n', TextDecoration.UNDERLINED));
formats.add(new CharacterAndFormat('o', TextDecoration.ITALIC));

formats.add(new CharacterAndFormat('r', LegacyCodeConverter.Reset.INSTANCE));

StringBuilder legacyChars = new StringBuilder(formats.size());
FORMATS = Collections.unmodifiableList(formats);
for (int i = 0; i < FORMATS.size(); i++) {
legacyChars.append(FORMATS.get(i).getCharacter());
}
LEGACY_CHARS = legacyChars.toString();
}

LegacyCodeConverterImpl(final char character, final char hexCharacter, final boolean hexColours, final boolean useTerriblyStupidHexFormat) {
super(character, hexCharacter, hexColours, useTerriblyStupidHexFormat);
super(FORMATS, character, hexCharacter, hexColours, useTerriblyStupidHexFormat);
}

static @Nullable LegacyFormat legacyFormat(final char character) {
final int index = LEGACY_CHARS.indexOf(character);
if (index != -1) {
final TextFormat format = FORMATS.get(index);
final CharacterAndFormat characterAndFormat = FORMATS.get(index);
final TextFormat format = characterAndFormat.getFormat();
if (format instanceof TextColor) {
return new LegacyFormat((TextColor) format);
} else if (format instanceof TextDecoration) {
Expand All @@ -75,26 +76,6 @@ final class LegacyCodeConverterImpl extends LegacyCodeConverter {
return null;
}

@Override
public @NotNull NamedTextColor nearestColorTo(final @NotNull TextColor color) {
return NamedTextColor.nearestTo(color);
}

@Override
@NotNull List<TextFormat> getFormats() {
return FORMATS;
}

@Override
@NotNull String getAllLegacyChars() {
return LEGACY_CHARS;
}

@Override
@NotNull List<? extends TextColor> getLegacyColors() {
return NamedTextColor.VALUES;
}

@Override
@NotNull LegacyCodeConverterSupplier createSupplier() {
return LegacyCodeConverterImpl::new;
Expand Down

0 comments on commit 6f35d82

Please sign in to comment.