Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions module/minecraft/minecraft-chat/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ dependencies {
compileOnly(project(":common-env"))
compileOnly(project(":common-platform-api"))
compileOnly(project(":common-util"))
testImplementation("net.md-5:bungeecord-chat:1.21-R0.4")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import net.md_5.bungee.api.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.util.Optional;
Expand Down Expand Up @@ -46,34 +47,25 @@ public static String translate(String in) {
return ChatColor.translateAlternateColorCodes('&', in);
}
StringBuilder builder = new StringBuilder();
char[] chars = in.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (i + 1 < chars.length && chars[i] == '&' && chars[i + 1] == '{') {
ChatColor chatColor = null;
char[] match = new char[0];
for (int j = i + 2; j < chars.length && chars[j] != '}'; j++) {
match = arrayAppend(match, chars[j]);
}
if (match.length == 11 && (match[3] == ',' || match[3] == '-') && (match[7] == ',' || match[7] == '-')) {
chatColor = ChatColor.of(new Color(toInt(match, 0, 3), toInt(match, 4, 7), toInt(match, 8, 11)));
} else if (match.length == 7 && match[0] == '#') {
try {
chatColor = ChatColor.of(toString(match));
} catch (IllegalArgumentException ignored) {
}
} else {
Optional<StandardColors> knownColor = StandardColors.match(toString(match));
for (int i = 0; i < in.length(); i++) {
if (i + 1 < in.length() && in.charAt(i) == '&' && in.charAt(i + 1) == '{') {
int end = in.indexOf('}', i + 2);
if (end >= 0) {
String expression = in.substring(i + 2, end).trim();
Optional<StandardColors> knownColor = StandardColors.match(expression);
Integer color = parseColor(expression);
if (knownColor.isPresent()) {
chatColor = knownColor.get().toChatColor();
builder.append(knownColor.get().toChatColor());
i = end;
continue;
} else if (color != null) {
builder.append(ChatColor.of(new Color(color)));
i = end;
continue;
}
}
if (chatColor != null) {
builder.append(chatColor);
i += match.length + 2;
}
} else {
builder.append(chars[i]);
}
builder.append(in.charAt(i));
}
String colorString = builder.toString();
// 1.20.4 不再支持该写法,该模块无法判断版本,因此全部替换为白色
Expand All @@ -86,26 +78,42 @@ public static String getColorCode(int color) {
return ChatColor.of(new Color(color)).toString();
}

private static char[] arrayAppend(char[] chars, char in) {
char[] newChars = new char[chars.length + 1];
System.arraycopy(chars, 0, newChars, 0, chars.length);
newChars[chars.length] = in;
return newChars;
}

private static String toString(char[] chars) {
StringBuilder builder = new StringBuilder();
for (char c : chars) {
builder.append(c);
@Nullable
static Integer parseColor(String source) {
String value = source.trim();
if (value.matches("#[0-9a-fA-F]{6}")) {
return Integer.parseInt(value.substring(1), 16);
}
return builder.toString();
}

private static int toInt(char[] chars, int start, int end) {
StringBuilder builder = new StringBuilder();
for (int i = start; i < end; i++) {
builder.append(chars[i]);
Character separator = null;
if (value.indexOf(',') >= 0) {
separator = ',';
} else if (value.indexOf('-') >= 0) {
separator = '-';
}
if (separator != null) {
String[] parts = value.split("\\" + separator, -1);
if (parts.length != 3) {
return null;
}
int color = 0;
for (String part : parts) {
int component;
try {
component = Integer.parseInt(part.trim());
} catch (NumberFormatException ignored) {
return null;
}
if (component < 0 || component > 255) {
return null;
}
color = color << 8 | component;
}
return color;
}
Optional<StandardColors> knownColor = StandardColors.match(value);
if (knownColor.isPresent() && knownColor.get().toChatColor().getColor() != null) {
return knownColor.get().toChatColor().getColor().getRGB() & 0xFFFFFF;
}
return Integer.parseInt(builder.toString());
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package taboolib.module.chat

import net.md_5.bungee.api.ChatColor
import taboolib.common.platform.function.warning
import taboolib.common.util.orNull
import taboolib.common.util.t
import kotlin.math.ceil

Expand Down Expand Up @@ -53,23 +52,7 @@ fun List<String>.uncolored() = map { it.uncolored() }
* 获取颜色
*/
fun String.parseToHexColor(): Int {
// HEX: #ffffff
if (startsWith('#')) {
return substring(1).toIntOrNull(16) ?: 0
}
// RGB: 255,255,255
if (contains(',')) {
return split(',').map { it.toIntOrNull() ?: 0 }.let { (r, g, b) -> (r shl 16) or (g shl 8) or b }
}
// RGB: 255-255-255
if (contains('-')) {
return split('-').map { it.toIntOrNull() ?: 0 }.let { (r, g, b) -> (r shl 16) or (g shl 8) or b }
}
// NAMED: white
val knownColor = StandardColors.match(this)
if (knownColor.orNull()?.chatColor?.color != null) {
return knownColor.get().chatColor.color.rgb
}
HexColor.parseColor(this)?.let { return it }
warning(
"""
$this 不是一个颜色。
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package taboolib.module.chat

import net.md_5.bungee.api.ChatColor
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import java.awt.Color

class ColorParsingTest {

@Test
fun `strict color parser preserves black and leading zero colors`() {
assertEquals(0x000000, HexColor.parseColor("#000000"))
assertEquals(0x000001, HexColor.parseColor("#000001"))
assertEquals(0x00ff0a, HexColor.parseColor("#00ff0a"))
assertEquals(0xffffff, HexColor.parseColor("#FFFFFF"))
}

@Test
fun `strict color parser rejects malformed hex and rgb`() {
listOf("#fff", "#00000", "#0000000", "#gggggg", "#", "##000000").forEach {
assertNull(HexColor.parseColor(it), it)
}
listOf("1,2", "1,2,3,4", "255,x,255", "256,0,0", "-1,0,0", "1--2-3", "1,,3").forEach {
assertNull(HexColor.parseColor(it), it)
}
}

@Test
fun `strict color parser accepts variable width rgb components`() {
assertEquals(0x000000, HexColor.parseColor("0,0,0"))
assertEquals(0x0114ff, HexColor.parseColor("1,20,255"))
assertEquals(0xffffff, HexColor.parseColor("255-255-255"))
assertEquals(0x0114ff, HexColor.parseColor("1 - 20 - 255"))
}

@Test
fun `hex translation preserves invalid expressions and parses valid rgb`() {
assertEquals("&{999,0,0}x", HexColor.translate("&{999,0,0}x"))
assertEquals("&{abc,def,ghi}x", HexColor.translate("&{abc,def,ghi}x"))
assertEquals("&{1,2", HexColor.translate("&{1,2"))
assertEquals("${ChatColor.of(Color(1, 20, 255))}x", HexColor.translate("&{1,20,255}x"))
assertEquals("${ChatColor.BLUE}x", HexColor.translate("&{BLUE}x"))
assertEquals("${ChatColor.WHITE}x", HexColor.translate("&{RESET}x"))
}
}
4 changes: 4 additions & 0 deletions module/minecraft/minecraft-i18n/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ dependencies {
compileOnly(project(":common-util"))
compileOnly(project(":module:minecraft:minecraft-chat"))
compileOnly(project(":module:basic:basic-configuration"))
testImplementation(project(":common-platform-api"))
testImplementation(project(":common-util"))
testImplementation(project(":module:minecraft:minecraft-chat"))
testImplementation(project(":module:basic:basic-configuration"))
}
Loading
Loading