mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 19:05:02 -04:00
rewrite legacy chat component reading, tests
This commit is contained in:
parent
01777d6daa
commit
9c833c0599
@ -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?) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user