rewrite legacy chat component reading, tests

This commit is contained in:
Bixilon 2023-02-03 00:16:56 +01:00
parent 01777d6daa
commit 9c833c0599
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
11 changed files with 287 additions and 131 deletions

View File

@ -17,18 +17,11 @@ import de.bixilon.kutil.cast.CastUtil.nullCast
import de.bixilon.kutil.json.JsonUtil.toJsonList import de.bixilon.kutil.json.JsonUtil.toJsonList
import de.bixilon.kutil.json.JsonUtil.toJsonObject import de.bixilon.kutil.json.JsonUtil.toJsonObject
import de.bixilon.kutil.primitive.BooleanUtil.toBoolean import de.bixilon.kutil.primitive.BooleanUtil.toBoolean
import de.bixilon.kutil.url.URLUtil.toURL
import de.bixilon.minosoft.data.language.translate.Translator import de.bixilon.minosoft.data.language.translate.Translator
import de.bixilon.minosoft.data.text.events.click.ClickEvent
import de.bixilon.minosoft.data.text.events.click.ClickEvents import de.bixilon.minosoft.data.text.events.click.ClickEvents
import de.bixilon.minosoft.data.text.events.click.OpenURLClickEvent
import de.bixilon.minosoft.data.text.events.hover.HoverEvents import de.bixilon.minosoft.data.text.events.hover.HoverEvents
import de.bixilon.minosoft.data.text.formatting.ChatCode.Companion.toColor import de.bixilon.minosoft.data.text.formatting.ChatCode.Companion.toColor
import de.bixilon.minosoft.data.text.formatting.ChatFormattingCode
import de.bixilon.minosoft.data.text.formatting.ChatFormattingCodes
import de.bixilon.minosoft.data.text.formatting.PostChatFormattingCodes
import de.bixilon.minosoft.data.text.formatting.PreChatFormattingCodes import de.bixilon.minosoft.data.text.formatting.PreChatFormattingCodes
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format import de.bixilon.minosoft.util.KUtil.format
@ -36,113 +29,20 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.get import de.bixilon.minosoft.util.nbt.tag.NBTUtil.get
import javafx.collections.ObservableList import javafx.collections.ObservableList
import javafx.scene.Node import javafx.scene.Node
import java.text.CharacterIterator
import java.text.StringCharacterIterator
class BaseComponent : ChatComponent { class BaseComponent : ChatComponent {
val parts: MutableList<ChatComponent> = mutableListOf() val parts: MutableList<ChatComponent> = mutableListOf()
constructor(parts: MutableList<ChatComponent>) {
this.parts += parts
}
constructor(vararg parts: Any?) { constructor(vararg parts: Any?) {
for (part in parts) { for (part in parts) {
this.parts += part.format() this.parts += part.format()
} }
} }
constructor(parent: TextComponent? = null, legacy: String = "", restrictedMode: Boolean = false) {
val currentText = StringBuilder()
var currentColor = parent?.color
var currentFormatting: MutableSet<ChatFormattingCode> = parent?.formatting?.toMutableSet() ?: mutableSetOf()
val iterator = StringCharacterIterator(legacy)
var char = iterator.first()
fun push() {
if (currentText.isEmpty()) {
return
}
val spaceSplit = currentText.split(' ')
var currentMessage = ""
fun push(clickEvent: ClickEvent?) {
if (currentMessage.isEmpty()) {
return
}
parts += TextComponent(message = currentMessage, color = currentColor, formatting = currentFormatting.toMutableSet(), clickEvent = clickEvent)
currentMessage = ""
}
for ((index, split) in spaceSplit.withIndex()) {
var clickEvent: ClickEvent? = null
if (split.isNotBlank()) {
for (protocol in URLProtocols.VALUES) {
if (!split.startsWith(protocol.prefix)) {
continue
}
if (protocol.restricted && restrictedMode) {
break
}
clickEvent = OpenURLClickEvent(split.toURL())
break
}
}
if (split.isNotEmpty()) {
if (clickEvent != null) {
// push previous
push(null)
currentMessage = split
push(clickEvent)
} else {
currentMessage += split
}
}
if (index != spaceSplit.size - 1) {
currentMessage += " "
}
}
push(null)
currentFormatting = mutableSetOf()
currentColor = null
currentText.clear()
}
while (char != CharacterIterator.DONE) {
if (char != ProtocolDefinition.TEXT_COMPONENT_FORMATTING_PREFIX) {
currentText.append(char)
char = iterator.next()
continue
}
val formattingChar = iterator.next()
ChatColors.VALUES.getOrNull(Character.digit(formattingChar, 16))?.let {
push()
currentColor = it.nullCast<RGBColor>()
} ?: ChatFormattingCodes.getChatFormattingCodeByChar(formattingChar)?.let {
push()
if (it == PostChatFormattingCodes.RESET) {
push()
} else {
currentFormatting.add(it)
}
} ?: let {
// ignore and ignore next char
char = iterator.next()
}
// check because of above
if (char == CharacterIterator.DONE) {
break
}
char = iterator.next()
}
push()
}
constructor(translator: Translator? = null, parent: TextComponent? = null, json: Map<String, Any>, restrictedMode: Boolean = false) { constructor(translator: Translator? = null, parent: TextComponent? = null, json: Map<String, Any>, restrictedMode: Boolean = false) {
var currentParent: TextComponent? = null var currentParent: TextComponent? = null
var currentText = "" var currentText = ""
@ -150,7 +50,7 @@ class BaseComponent : ChatComponent {
fun parseExtra() { fun parseExtra() {
json["extra"].toJsonList()?.let { json["extra"].toJsonList()?.let {
for (data in it) { for (data in it) {
parts += ChatComponent.of(data, translator, currentParent, restrictedMode) this += ChatComponent.of(data, translator, currentParent, restrictedMode)
} }
} }
} }
@ -186,7 +86,7 @@ class BaseComponent : ChatComponent {
hoverEvent = hoverEvent, hoverEvent = hoverEvent,
) )
if (currentText.isNotEmpty()) { if (currentText.isNotEmpty()) {
parts += textComponent this += textComponent
} }
currentParent = textComponent currentParent = textComponent
@ -199,7 +99,7 @@ class BaseComponent : ChatComponent {
with.add(part ?: continue) with.add(part ?: continue)
} }
} }
parts += translator?.translate(it.toResourceLocation(), currentParent, restrictedMode, *with.toTypedArray()) ?: ChatComponent.of(json["with"], translator, currentParent, restrictedMode) this += translator?.translate(it.toResourceLocation(), currentParent, restrictedMode, *with.toTypedArray()) ?: ChatComponent.of(json["with"], translator, currentParent, restrictedMode)
} }
} }
@ -295,8 +195,15 @@ class BaseComponent : ChatComponent {
return legacyText return legacyText
} }
operator fun plusAssign(component: ChatComponent) {
if (component.length == 0) {
return
}
parts += component
}
operator fun plusAssign(text: Any?) { operator fun plusAssign(text: Any?) {
parts += text.format() this += text.format()
} }
private fun <T> MutableSet<T>.addOrRemove(value: T, addOrRemove: Boolean?) { private fun <T> MutableSet<T>.addOrRemove(value: T, addOrRemove: Boolean?) {

View File

@ -133,7 +133,7 @@ interface ChatComponent {
} }
} }
return BaseComponent(parent, string, restrictedMode) return LegacyComponentReader.parse(parent, string, restrictedMode)
} }
fun String.chat(): ChatComponent { fun String.chat(): ChatComponent {

View File

@ -0,0 +1,148 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.text
import de.bixilon.kutil.url.URLUtil.toURL
import de.bixilon.minosoft.data.text.events.click.ClickEvent
import de.bixilon.minosoft.data.text.events.click.OpenFileClickEvent
import de.bixilon.minosoft.data.text.events.click.OpenURLClickEvent
import de.bixilon.minosoft.data.text.formatting.ChatFormattingCode
import de.bixilon.minosoft.data.text.formatting.ChatFormattingCodes
import de.bixilon.minosoft.data.text.formatting.PostChatFormattingCodes
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import java.io.File
import java.text.CharacterIterator
import java.text.StringCharacterIterator
private typealias PartList = MutableList<ChatComponent>
object LegacyComponentReader {
private fun PartList.push(sequence: SequenceBuilder, restricted: Boolean) {
if (sequence.text.isEmpty()) return
val split = sequence.text.split(' ')
val text = StringBuilder()
for ((index, part) in split.withIndex()) {
val event = getClickEvent(part, restricted)
if (event == null) {
text.append(part)
if (index < split.size - 1) {
text.append(" ") // space was lost in the split process
}
continue
}
if (text.isNotEmpty()) {
// an url follows, push the previous part
this += TextComponent(text, sequence.color, sequence.formatting.toMutableSet())
text.clear()
}
this += TextComponent(part, sequence.color, sequence.formatting.toMutableSet(), event)
}
if (text.isNotEmpty()) {
// data that was not pushed yet
this += TextComponent(text, sequence.color, sequence.formatting.toMutableSet())
}
sequence.reset() // clear it up again for next usage
}
private fun getClickEvent(link: String, restricted: Boolean): ClickEvent? {
for (protocol in URLProtocols.VALUES) {
if (!link.startsWith(protocol.prefix)) {
continue
}
if (protocol.restricted && restricted) {
break
}
return if (protocol == URLProtocols.FILE) OpenFileClickEvent(File(link.removePrefix(protocol.prefix))) else OpenURLClickEvent(link.toURL())
}
return null
}
fun parse(parent: TextComponent? = null, legacy: String = "", restricted: Boolean = false): ChatComponent {
val parts: PartList = mutableListOf()
val sequence = SequenceBuilder(color = parent?.color, formatting = parent?.formatting?.toMutableSet() ?: mutableSetOf())
val iterator = StringCharacterIterator(legacy)
var char: Char
while (true) {
char = iterator.getAndNext()
if (char == CharacterIterator.DONE) break
if (char != ProtocolDefinition.TEXT_COMPONENT_FORMATTING_PREFIX) {
sequence.text.append(char)
continue
}
val formattingChar = iterator.getAndNext()
val color = ChatColors.VALUES.getOrNull(Character.digit(formattingChar, 16))
if (color != null) {
parts.push(sequence, restricted) // try push previous, because this is a color change
sequence.color = color
continue
}
val formatting = ChatFormattingCodes.getChatFormattingCodeByChar(formattingChar)
if (formatting != null) {
parts.push(sequence, restricted) // try push previous, because this is a formatting change
if (formatting != PostChatFormattingCodes.RESET) {
// a reset means resetting, this is done by the previous push
sequence.formatting += formatting
}
continue
}
// ignore the next char, it is not valid formatting and should be hidden
}
parts.push(sequence, restricted)
return when {
parts.isEmpty() -> EmptyComponent
parts.size == 1 -> parts.first()
else -> BaseComponent(parts)
}
}
private fun StringCharacterIterator.getAndNext(): Char {
val char = current()
next()
return char
}
private data class SequenceBuilder(
var text: StringBuilder = StringBuilder(),
var color: RGBColor? = null,
var formatting: MutableSet<ChatFormattingCode> = mutableSetOf(),
) {
fun reset() {
text.clear()
color = null
formatting.clear()
}
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -36,6 +36,15 @@ class CopyToClipboardClickEvent(
dialog.show() dialog.show()
} }
override fun hashCode(): Int {
return text.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is CopyToClipboardClickEvent) return false
return other.text == text
}
companion object : ClickEventFactory<CopyToClipboardClickEvent> { companion object : ClickEventFactory<CopyToClipboardClickEvent> {
override val name = "copy_to_clipboard" override val name = "copy_to_clipboard"

View File

@ -48,6 +48,15 @@ class OpenFileClickEvent(
dialog.show() dialog.show()
} }
override fun hashCode(): Int {
return file.path.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is OpenFileClickEvent) return false
return other.file.path == file.path
}
companion object : ClickEventFactory<OpenFileClickEvent> { companion object : ClickEventFactory<OpenFileClickEvent> {
override val name: String = "open_file" override val name: String = "open_file"

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -46,6 +46,15 @@ class OpenURLClickEvent(
dialog.show() dialog.show()
} }
override fun hashCode(): Int {
return url.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is OpenURLClickEvent) return false
return other.url == url
}
companion object : ClickEventFactory<OpenURLClickEvent> { companion object : ClickEventFactory<OpenURLClickEvent> {
override val name: String = "open_url" override val name: String = "open_url"

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -37,6 +37,15 @@ class SendMessageClickEvent(
dialog.show() dialog.show()
} }
override fun hashCode(): Int {
return message.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is SendMessageClickEvent) return false
return other.message == message
}
companion object : ClickEventFactory<SendMessageClickEvent>, MultiNameFactory<SendMessageClickEvent> { companion object : ClickEventFactory<SendMessageClickEvent>, MultiNameFactory<SendMessageClickEvent> {
override val name: String = "send_message" override val name: String = "send_message"
override val aliases: Set<String> = setOf("run_command") override val aliases: Set<String> = setOf("run_command")

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -20,6 +20,15 @@ class SuggestChatClickEvent(
val message: String, val message: String,
) : ClickEvent { ) : ClickEvent {
override fun hashCode(): Int {
return message.hashCode()
}
override fun equals(other: Any?): Boolean {
if (other !is SuggestChatClickEvent) return false
return other.message == message
}
companion object : ClickEventFactory<SuggestChatClickEvent>, MultiNameFactory<SuggestChatClickEvent> { companion object : ClickEventFactory<SuggestChatClickEvent>, MultiNameFactory<SuggestChatClickEvent> {
override val name: String = "suggest_chat" override val name: String = "suggest_chat"
override val aliases: Set<String> = setOf("suggest_command") override val aliases: Set<String> = setOf("suggest_command")

View File

@ -28,6 +28,7 @@ import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.json.Jackson import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes
import java.nio.charset.StandardCharsets
open class InByteBuffer : de.bixilon.kutil.buffer.bytes.`in`.InByteBuffer { open class InByteBuffer : de.bixilon.kutil.buffer.bytes.`in`.InByteBuffer {
@ -44,14 +45,11 @@ open class InByteBuffer : de.bixilon.kutil.buffer.bytes.`in`.InByteBuffer {
return readByte() / 32.0 return readByte() / 32.0
} }
// TODO kutil 1.19.2 override fun readString(length: Int): String {
/*
override fun readString(length: Int = readVarInt()): String {
val string = String(readByteArray(length), StandardCharsets.UTF_8) val string = String(readByteArray(length), StandardCharsets.UTF_8)
check(string.length <= ProtocolDefinition.STRING_MAX_LENGTH) { "String max string length exceeded ${string.length} > ${ProtocolDefinition.STRING_MAX_LENGTH}" } check(string.length <= ProtocolDefinition.STRING_MAX_LENGTH) { "String max string length exceeded ${string.length} > ${ProtocolDefinition.STRING_MAX_LENGTH}" }
return string return string
} }
*/
fun readJson(): Map<String, Any> { fun readJson(): Map<String, Any> {
return Jackson.MAPPER.readValue(readString(), Jackson.JSON_MAP_TYPE) return Jackson.MAPPER.readValue(readString(), Jackson.JSON_MAP_TYPE)

View File

@ -18,6 +18,8 @@ import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.registries.identified.Namespaces import de.bixilon.minosoft.data.registries.identified.Namespaces
import de.bixilon.minosoft.data.registries.identified.ResourceLocation import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.protocol.ProtocolUtil.encodeNetwork
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.json.Jackson import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes import de.bixilon.minosoft.util.nbt.tag.NBTTagTypes
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.nbtType import de.bixilon.minosoft.util.nbt.tag.NBTUtil.nbtType
@ -36,8 +38,6 @@ open class OutByteBuffer : de.bixilon.kutil.buffer.bytes.out.OutByteBuffer {
} }
// TODO kutil 1.19.2
/*
override fun writeString(string: String) { override fun writeString(string: String) {
check(string.length <= ProtocolDefinition.STRING_MAX_LENGTH) { "String max string length exceeded ${string.length} > ${ProtocolDefinition.STRING_MAX_LENGTH}" } check(string.length <= ProtocolDefinition.STRING_MAX_LENGTH) { "String max string length exceeded ${string.length} > ${ProtocolDefinition.STRING_MAX_LENGTH}" }
val bytes = string.encodeNetwork() val bytes = string.encodeNetwork()
@ -45,8 +45,6 @@ open class OutByteBuffer : de.bixilon.kutil.buffer.bytes.out.OutByteBuffer {
writeBareByteArray(bytes) writeBareByteArray(bytes)
} }
*/
protected fun writeNBTTagType(type: NBTTagTypes) { protected fun writeNBTTagType(type: NBTTagTypes) {
writeByte(type.ordinal) writeByte(type.ordinal)
} }

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger * Copyright (C) 2020-2023 Moritz Zwerger
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -13,7 +13,10 @@
package de.bixilon.minosoft.data.text package de.bixilon.minosoft.data.text
import de.bixilon.kutil.url.URLUtil.toURL
import de.bixilon.minosoft.data.text.ChatComponent.Companion.chat import de.bixilon.minosoft.data.text.ChatComponent.Companion.chat
import de.bixilon.minosoft.data.text.events.click.OpenFileClickEvent
import de.bixilon.minosoft.data.text.events.click.OpenURLClickEvent
import de.bixilon.minosoft.data.text.formatting.color.ChatColors import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.asColor import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.asColor
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -30,28 +33,28 @@ internal class ChatComponentTest {
@Test @Test
fun testSimpleText() { fun testSimpleText() {
val expected = BaseComponent(parts = arrayOf(TextComponent("Test"))) val expected = TextComponent("Test")
val actual = "Test".chat() val actual = "Test".chat()
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun testSimpleColor() { fun testSimpleColor() {
val expected = BaseComponent(parts = arrayOf(TextComponent("Test").color(ChatColors.RED))) val expected = TextComponent("Test").color(ChatColors.RED)
val actual = "§cTest".chat() val actual = "§cTest".chat()
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun testSimpleColoredFormatting() { fun testSimpleColoredFormatting() {
val expected = BaseComponent(parts = arrayOf(TextComponent("Test").color(ChatColors.RED).strikethrough())) val expected = TextComponent("Test").color(ChatColors.RED).strikethrough()
val actual = "§c§mTest".chat() val actual = "§c§mTest".chat()
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun testSwappedSimpleColoredFormatting() { fun testSwappedSimpleColoredFormatting() {
val expected = BaseComponent(parts = arrayOf(TextComponent("Test").color(ChatColors.RED).strikethrough())) val expected = TextComponent("Test").color(ChatColors.RED).strikethrough()
val actual = "§m§cTest".chat() val actual = "§m§cTest".chat()
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -66,6 +69,47 @@ internal class ChatComponentTest {
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test
fun url1() {
val expected = BaseComponent(
TextComponent("Test").color(ChatColors.RED),
TextComponent("https://bixilon.de").color(ChatColors.GREEN).clickEvent(OpenURLClickEvent("https://bixilon.de".toURL())),
)
val actual = "§cTest§ahttps://bixilon.de".chat()
assertEquals(expected, actual)
}
@Test
fun url2() {
val expected = BaseComponent(
TextComponent("Test ").color(ChatColors.RED),
TextComponent("file:/home/moritz").color(ChatColors.GREEN).clickEvent(OpenFileClickEvent("/home/moritz")),
)
val actual = ChatComponent.of("§cTest §afile:/home/moritz")
assertEquals(expected, actual)
}
@Test
fun url3() {
val expected = BaseComponent(
TextComponent("Hi, please take care of: "),
TextComponent("https://bixilon.de/technoblade").clickEvent(OpenURLClickEvent("https://bixilon.de/technoblade".toURL())),
)
val actual = ChatComponent.of("Hi, please take care of: https://bixilon.de/technoblade")
assertEquals(expected, actual)
}
@Test
fun restrictedMode() {
val expected = BaseComponent(
TextComponent("Test ").color(ChatColors.RED),
TextComponent("file:/home/moritz").color(ChatColors.GREEN),
)
val actual = ChatComponent.of("§cTest §afile:/home/moritz", restrictedMode = true)
assertEquals(expected, actual)
}
@Test @Test
fun test2FormattedTexts() { fun test2FormattedTexts() {
val expected = BaseComponent( val expected = BaseComponent(
@ -137,11 +181,7 @@ internal class ChatComponentTest {
@Test @Test
fun testInvalidJson() { fun testInvalidJson() {
val expected = BaseComponent( val expected = TextComponent("""{text":"Test"}""")
parts = arrayOf(
TextComponent("""{text":"Test"}"""),
)
)
val actual = """{text":"Test"}""".chat() val actual = """{text":"Test"}""".chat()
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -169,4 +209,24 @@ internal class ChatComponentTest {
val text = ChatComponent.of("dummy§anext") val text = ChatComponent.of("dummy§anext")
assertEquals(text.getJson(), listOf(mapOf("text" to "dummy"), mapOf("text" to "next", "color" to "green"))) assertEquals(text.getJson(), listOf(mapOf("text" to "dummy"), mapOf("text" to "next", "color" to "green")))
} }
@Test
fun hypixelMotd() {
val string = " §aHypixel Network §c[1.8-1.19]\n §c§lLUNAR MAPS §7§l§ §6§lCOSMETICS §7| §d§lSKYBLOCK 0.17.3"
val component = ChatComponent.of(string)
val expected = BaseComponent(
" ",
TextComponent("Hypixel Network ").color(ChatColors.GREEN),
TextComponent("[1.8-1.19]\n ").color(ChatColors.RED),
TextComponent("LUNAR MAPS ").color(ChatColors.RED).bold(),
TextComponent("COSMETICS ").color(ChatColors.GOLD).bold(),
TextComponent("| ").color(ChatColors.GRAY),
TextComponent("SKYBLOCK 0.17.3").color(ChatColors.LIGHT_PURPLE).bold(),
)
assertEquals(" Hypixel Network [1.8-1.19]\n LUNAR MAPS COSMETICS | SKYBLOCK 0.17.3", component.message)
assertEquals(expected, component)
}
} }