From 5de8f982748b99dd0f96bd4c63b45b5a88aadf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MC=7E=E8=9B=9F=E9=BE=99?= <1610105206@qq.com> Date: Wed, 14 Aug 2024 18:47:27 +0800 Subject: [PATCH] Experimental | Try to fix DynamicOps --- .../cn/fd/ratziel/module/item/nms/NMS12005.kt | 171 +++++++++++++----- .../cn/fd/ratziel/module/item/nms/NMSItem.kt | 71 +++++--- .../cn/fd/ratziel/module/nbt/NBTCompound.kt | 11 +- .../cn/fd/ratziel/module/nbt/NBTList.kt | 5 + 4 files changed, 183 insertions(+), 75 deletions(-) diff --git a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMS12005.kt b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMS12005.kt index d790b15c..8ebf44af 100644 --- a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMS12005.kt +++ b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMS12005.kt @@ -72,6 +72,11 @@ abstract class NMS12005 { } +object NBTEnd : NBTData(NBTType.END) { + override val content = Unit + override fun clone() = this +} + /** * 代码参考自: Taboolib/nms-tag-12005 */ @@ -79,13 +84,13 @@ abstract class NMS12005 { class NMS12005Impl : NMS12005() { fun parse(codec: Codec, tag: NBTCompound): T? { - val result = codec.parse(createSerializationContext(InternalOps), tag) + val result = codec.parse(createSerializationContext(DataDynamicOps), tag) val opt = result.resultOrPartial { error("Failed to parse: $it") } return if (opt.isPresent) opt.get() else null } fun save(codec: Codec, obj: T): NBTCompound? { - val result = codec.encodeStart(createSerializationContext(InternalOps), obj) + val result = codec.encodeStart(createSerializationContext(DataDynamicOps), obj) val opt = result.resultOrPartial { error("Failed to save: $it") } return if (opt.isPresent) opt.get() as? NBTCompound else null } @@ -108,24 +113,56 @@ class NMS12005Impl : NMS12005() { ?: ref.structure.getMethodByType("createSerializationContext", DynamicOps::class.java) } - private object InternalOps : DynamicOps { + /** + * 摘抄修改自: [net.minecraft.nbt.DynamicOpsNBT] + */ + object DataDynamicOps : DynamicOps { override fun empty() = NBTEnd + override fun convertTo(ops: DynamicOps, data: NBTData): U = when (data.type.id) { + 0 -> ops.empty() + 1 -> ops.createByte((data as NBTByte).content) + 2 -> ops.createShort((data as NBTShort).content) + 3 -> ops.createInt((data as NBTInt).content) + 4 -> ops.createLong((data as NBTLong).content) + 5 -> ops.createFloat((data as NBTFloat).content) + 6 -> ops.createDouble((data as NBTDouble).content) + 7 -> ops.createByteList(ByteBuffer.wrap((data as NBTByteArray).content)) + 8 -> ops.createString(data.toString()) + 9 -> convertList(ops, data) + 10 -> convertMap(ops, data) + 11 -> ops.createIntList(Arrays.stream((data as NBTIntArray).content)) + 12 -> ops.createLongList(Arrays.stream((data as NBTLongArray).content)) + else -> throw IllegalStateException("Unknown tag type: $data") + } + + override fun remove(tag: NBTData, str: String) = if (tag is NBTCompound) tag.cloneShallow().apply { remove(str) } else tag + override fun createNumeric(number: Number) = NBTAdapter.adapt(number) override fun createString(str: String) = NBTString(str) - override fun remove(tag: NBTData, str: String) = if (tag is NBTCompound) tag.cloneShallow().apply { remove(str) } else tag - override fun createList(stream: Stream) = NBTList().apply { addAll(stream.asSequence()) } - override fun getStream(tag: NBTData): DataResult> = - if (tag is NBTList) DataResult.success(tag.stream()) else DataResult.error { "Not a list: $tag" } - override fun createMap(stream: Stream>) = NBTCompound().apply { stream.forEach { put((it.first as NBTString).content, it.second) } } + override fun getStringValue(data: NBTData): DataResult = + if (data is NBTString) DataResult.success(data.content) else DataResult.error { "Not a string: $data" } + + override fun getNumberValue(data: NBTData): DataResult { + val number = data.content as? Number + return if (number != null) DataResult.success(number) else DataResult.error { "Not a number: $data" } + } + + override fun getStream(tag: NBTData): DataResult> = + if (tag is NBTList) { + DataResult.success(if (tag.elementType == NBTType.COMPOUND) { + tag.stream().map { tryUnwrap(it as NBTCompound) } + } else tag.stream()) + } else DataResult.error { "Not a list: $tag" } + override fun getMapValues(tag: NBTData): DataResult>> = if (tag is NBTCompound) DataResult.success(tag.entries.stream().map { Pair.of(this.createString(it.key), it.value) }) @@ -138,57 +175,99 @@ class NMS12005Impl : NMS12005() { return DataResult.error({ "key is not a string: $data2" }, data1) } else { val newMap = (data1 as? NBTCompound)?.cloneShallow() ?: NBTCompound() - newMap.put(data2.content, data3) + newMap[data2.content] = data3 return DataResult.success(newMap) } } - override fun mergeToList(data1: NBTData, data2: NBTData): DataResult = when (data1) { - is NBTEnd -> DataResult.success(initial(data2)) - is NBTList -> DataResult.success(if (data2 is NBTList) NBTList(data1.plus(data2)) else initial(data2)) - is NBTByteArray -> DataResult.success(if (data2 is NBTByteArray) NBTByteArray(data1.content.plus(data2.content)) else initial(data2)) - is NBTIntArray -> DataResult.success(if (data2 is NBTIntArray) NBTIntArray(data1.content.plus(data2.content)) else initial(data2)) - is NBTLongArray -> DataResult.success(if (data2 is NBTLongArray) NBTLongArray(data1.content.plus(data2.content)) else initial(data2)) - else -> DataResult.error({ "mergeToList called with not a list: $data1" }, data1) + override fun mergeToList(data1: NBTData, data2: NBTData): DataResult { + val collector = createCollector(data1) + return if (collector != null) DataResult.success(collector.accept(data2).result()) + else DataResult.error({ "mergeToList called with not a list: $data1" }, data1) + } + + fun isWrapper(tag: NBTCompound) = tag.content.size == 1 && tag.contains("") + fun wrapElement(data: NBTData) = NBTCompound().apply { put("", data) } + fun wrapIfNeeded(data: NBTData) = if (data is NBTCompound && !isWrapper(data)) data else wrapElement(data) + fun tryUnwrap(tag: NBTCompound): NBTData { + if (tag.content.size == 1) { + val unwrap = tag[""] + if (unwrap != null) return unwrap + } + return tag + } + + fun createCollector(data: NBTData): ListCollector? = when (data) { + is NBTEnd -> InitialListCollector + is NBTList -> when (data.type) { + NBTType.END -> InitialListCollector + NBTType.COMPOUND -> HeterogenousListCollector(data) + else -> HomogenousListCollector(data) + } + + is NBTByteArray -> ByteListCollector(data.content.toMutableList()) + is NBTIntArray -> IntListCollector(data.content.toMutableList()) + is NBTLongArray -> LongListCollector(data.content.toMutableList()) + else -> null } - private fun initial(data: NBTData) = when (data) { - is NBTEnd -> NBTList() - is NBTList, is NBTByteArray, is NBTIntArray, is NBTLongArray -> data - else -> NBTList().apply { add(data) } + interface ListCollector { + fun accept(data: NBTData): ListCollector + fun result(): NBTData + fun acceptAll(iterable: Iterable): ListCollector { + var collector = this + iterable.forEach { collector = collector.accept(it) } + return collector + } } - override fun getStringValue(data: NBTData): DataResult = - if (data is NBTString) DataResult.success(data.content) else DataResult.error { "Not a string: $data" } + class HeterogenousListCollector(val result: NBTList = NBTList()) : ListCollector { + constructor(list: List) : this(NBTList(list.map { wrapElement(NBTInt(it)) })) + constructor(list: List) : this(NBTList(list.map { wrapElement(NBTByte(it)) })) + constructor(list: List) : this(NBTList(list.map { wrapElement(NBTLong(it)) })) - override fun getNumberValue(data: NBTData): DataResult { - val number = data.content as? Number - return if (number != null) DataResult.success(number) else DataResult.error { "Not a number: $data" } + override fun accept(data: NBTData) = this.apply { result.add(wrapIfNeeded(data)) } + override fun result() = this.result + } + + class HomogenousListCollector(val result: NBTList = NBTList()) : ListCollector { + override fun result() = this.result + override fun accept(data: NBTData) = + if (data.type == result.elementType) this.apply { result.add(data) } else HeterogenousListCollector().acceptAll(result).accept(data) } - override fun convertTo(ops: DynamicOps, data: NBTData): U = - when (data.type.id) { - 0 -> ops.empty() - 1 -> ops.createByte((data as NBTByte).content) - 2 -> ops.createShort((data as NBTShort).content) - 3 -> ops.createInt((data as NBTInt).content) - 4 -> ops.createLong((data as NBTLong).content) - 5 -> ops.createFloat((data as NBTFloat).content) - 6 -> ops.createDouble((data as NBTDouble).content) - 7 -> ops.createByteList(ByteBuffer.wrap((data as NBTByteArray).content)) - 8 -> ops.createString(data.toString()) - 9 -> convertList(ops, data) - 10 -> convertMap(ops, data) - 11 -> ops.createIntList(Arrays.stream((data as NBTIntArray).content)) - 12 -> ops.createLongList(Arrays.stream((data as NBTLongArray).content)) - else -> throw IllegalStateException("Unknown tag type: $data") + object InitialListCollector : ListCollector { + override fun result() = NBTList() + override fun accept(data: NBTData) = when (data) { + is NBTCompound -> HeterogenousListCollector().accept(data) + is NBTByte -> ByteListCollector(mutableListOf(data.content)) + is NBTInt -> IntListCollector(mutableListOf(data.content)) + is NBTLong -> LongListCollector(mutableListOf(data.content)) + else -> HomogenousListCollector(NBTList().apply { add(data) }) } + } - } + class ByteListCollector(val values: MutableList = mutableListOf()) : ListCollector { + override fun result() = NBTByteArray(values.toByteArray()) + override fun accept(data: NBTData) = + if (data is NBTByte) this.apply { values.add(data.content) } + else HeterogenousListCollector(values).accept(data) + } -} + class IntListCollector(val values: MutableList = mutableListOf()) : ListCollector { + override fun result() = NBTIntArray(values.toIntArray()) + override fun accept(data: NBTData) = + if (data is NBTInt) this.apply { values.add(data.content) } + else HeterogenousListCollector(values).accept(data) + } + + class LongListCollector(val values: MutableList = mutableListOf()) : ListCollector { + override fun result() = NBTLongArray(values.toLongArray()) + override fun accept(data: NBTData) = + if (data is NBTLong) this.apply { values.add(data.content) } + else HeterogenousListCollector(values).accept(data) + } + + } -object NBTEnd : NBTData(NBTType.END) { - override val content = Unit - override fun clone() = this } \ No newline at end of file diff --git a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMSItem.kt b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMSItem.kt index d9525c60..7de21dfb 100644 --- a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMSItem.kt +++ b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/item/nms/NMSItem.kt @@ -1,19 +1,16 @@ package cn.fd.ratziel.module.item.nms -import cn.fd.ratziel.module.item.nbt.* import cn.fd.ratziel.module.nbt.* import net.minecraft.core.component.DataComponentMap import net.minecraft.core.component.DataComponentPatch import net.minecraft.core.component.DataComponents import net.minecraft.core.component.PatchedDataComponentMap -import net.minecraft.nbt.* import net.minecraft.world.item.component.CustomData import taboolib.library.reflex.ReflexClass import taboolib.library.reflex.UnsafeAccess import taboolib.module.nms.MinecraftVersion import taboolib.module.nms.nmsProxy import java.lang.invoke.MethodHandle -import net.minecraft.world.item.ItemStack as NMSItemStack /** * NMSItem @@ -77,37 +74,40 @@ abstract class NMSItem { /** * 1.20.5+ */ -@Suppress("unused", "MemberVisibilityCanBePrivate") +@Suppress("unused", "MemberVisibilityCanBePrivate", "DEPRECATION") class NMSItemImpl2 : NMSItem() { val componentsField by lazy { ReflexClass.of(RefItemStack.nmsClass).getField("components", remap = true) } + val customDataConstructor by lazy { + ReflexClass.of(CustomData::class.java).structure.getConstructorByType(NBTTagCompound::class.java) + } + + val customDataTagField by lazy { + ReflexClass.of(CustomData::class.java).getField("tag", remap = true) + } + override fun getTag(nmsItem: Any): NBTCompound? { val dcp = (nmsItem as NMSItemStack).componentsPatch - return NMS12005.INSTANCE.savePatch(dcp)?.let { NBTCompound.of(it) } + return NMS12005.INSTANCE.savePatch(dcp) } override fun setTag(nmsItem: Any, tag: NBTCompound) { - val dcp = NMS12005.INSTANCE.parsePatch(tag) as? DataComponentPatch - val components = componentsField.get(nmsItem) as? PatchedDataComponentMap - if (components != null) { - components.restorePatch(dcp) - } else { - val newComponents = PatchedDataComponentMap(DataComponentMap.EMPTY) - newComponents.restorePatch(dcp) - componentsField.set(nmsItem, newComponents) - } + val dcp = NMS12005.INSTANCE.parsePatch(tag) as? DataComponentPatch ?: return + val components = PatchedDataComponentMap(DataComponentMap.EMPTY) + components.restorePatch(dcp) + componentsField.set(nmsItem, components) } override fun getCustomTag(nmsItem: Any): NBTCompound? { val customData = (nmsItem as NMSItemStack).get(DataComponents.CUSTOM_DATA) - return customData?.copyTag()?.let { NBTCompound.of(it) } + return customData?.unsafe?.let { fromNms(it) } as? NBTCompound } override fun setCustomTag(nmsItem: Any, tag: NBTCompound) { - val customData = CustomData.of(tag.getRaw() as NBTTagCompound) + val customData = customDataConstructor.instance(toNms(tag))!! as CustomData (nmsItem as NMSItemStack).set(DataComponents.CUSTOM_DATA, customData) } @@ -126,8 +126,8 @@ class NMSItemImpl2 : NMSItem() { is NBTIntArray -> NBTTagIntArray(data.content.copyOf()) is NBTByteArray -> NBTTagByteArray(data.content.copyOf()) is NBTLongArray -> NBTTagLongArray(data.content.copyOf()) - is cn.fd.ratziel.module.nbt.NBTList -> NBTTagList().also { nmsList -> data.content.forEach { nmsList.add(toNms(it)) } } - is NBTCompound -> NBTTagCompound().also { nmsCompound -> data.content.forEach { nmsCompound.put(it.key, toNms(it.value)) } } + is NBTList -> NBTTagList().apply { data.content.forEach { add(toNms(it)) } } + is NBTCompound -> NBTTagCompound().apply { data.content.forEach { put(it.key, toNms(it.value)) } } else -> throw UnsupportedOperationException("NBTData cannot convert to NmsNBTData: $data") } @@ -142,7 +142,7 @@ class NMSItemImpl2 : NMSItem() { is NBTTagByteArray -> NBTByteArray(nmsData.asByteArray.copyOf()) is NBTTagIntArray -> NBTIntArray(nmsData.asIntArray.copyOf()) is NBTTagLongArray -> NBTLongArray(nmsData.asLongArray.copyOf()) - is NBTTagList -> cn.fd.ratziel.module.nbt.NBTList().apply { nmsData.forEach { add(fromNms(it)) } } + is NBTTagList -> NBTList().apply { nmsData.forEach { add(fromNms(it)) } } is NBTTagCompound -> NBTCompound().apply { nmsData.allKeys.forEach { put(it, fromNms(nmsData.get(it)!!)) } } else -> throw UnsupportedOperationException("NmsNBTData cannot convert to NBTData: $nmsData") } @@ -186,7 +186,9 @@ class NMSItemImpl1 : NMSItem() { * private NBTTagCompound A * private NBTTagCompound tag */ - val nmsTagField = ReflexClass.of(RefItemStack.nmsClass).structure.getField(if (MinecraftVersion.isUniversal) "A" else "tag") + val nmsTagField by lazy { + ReflexClass.of(RefItemStack.nmsClass).structure.getField(if (MinecraftVersion.isUniversal) "A" else "tag") + } /** * public nms.ItemStack p() @@ -214,8 +216,9 @@ class NMSItemImpl1 : NMSItem() { val nbtTagByteArrayGetter = unreflectGetter(if (MinecraftVersion.isUniversal) "c" else "data") val nbtTagIntArrayGetter = unreflectGetter(if (MinecraftVersion.isUniversal) "c" else "data") val nbtTagLongArrayGetter = - if (!MinecraftVersion.isHigherOrEqual(MinecraftVersion.V1_12)) null - else unreflectGetter(if (MinecraftVersion.isUniversal) "c" else "b") + if (MinecraftVersion.isHigherOrEqual(MinecraftVersion.V1_12)) { + unreflectGetter(if (MinecraftVersion.isUniversal) "c" else "b") + } else null val new = MinecraftVersion.isHigherOrEqual(MinecraftVersion.V1_15) @@ -230,7 +233,7 @@ class NMSItemImpl1 : NMSItem() { is NBTIntArray -> NBTTagIntArray12(data.content.copyOf()) is NBTByteArray -> NBTTagByteArray12(data.content.copyOf()) is NBTLongArray -> NBTTagLongArray12(data.content.copyOf()) - is cn.fd.ratziel.module.nbt.NBTList -> NBTTagList12().also { src -> + is NBTList -> NBTTagList12().also { src -> // 反射获取字段: // private final List list; val list = nbtTagListGetter.get>(src) @@ -238,7 +241,7 @@ class NMSItemImpl1 : NMSItem() { if (dataList.isNotEmpty()) { dataList.forEach { list.add(toNms(it)) } // 修改 NBTTagList 的类型,不改他妈这条 List 作废,天坑。。。 - nbtTagListTypeSetter.set(src, dataList.first().type.id) + nbtTagListTypeSetter.set(src, dataList.first().type.id.toByte()) } } @@ -263,7 +266,7 @@ class NMSItemImpl1 : NMSItem() { is NBTTagByteArray12 -> NBTByteArray(nbtTagByteArrayGetter.get(nmsData).copyOf()) is NBTTagIntArray12 -> NBTIntArray(nbtTagIntArrayGetter.get(nmsData).copyOf()) is NBTTagLongArray12 -> NBTLongArray(nbtTagLongArrayGetter!!.get(nmsData).copyOf()) - is NBTTagList12 -> cn.fd.ratziel.module.nbt.NBTList().apply { nbtTagListGetter.get>(nmsData).forEach { add(fromNms(it)) } } + is NBTTagList12 -> NBTList().apply { nbtTagListGetter.get>(nmsData).forEach { add(fromNms(it)) } } is NBTTagCompound12 -> NBTCompound().apply { nbtTagCompoundGetter.get>(nmsData).forEach { put(it.key, fromNms(it.value)) } } else -> throw UnsupportedOperationException("NmsNBTData cannot convert to NBTData: $nmsData") } @@ -283,6 +286,8 @@ class NMSItemImpl1 : NMSItem() { } +typealias NMSItemStack = net.minecraft.world.item.ItemStack + typealias NBTTagCompound12 = net.minecraft.server.v1_12_R1.NBTTagCompound typealias NBTTagList12 = net.minecraft.server.v1_12_R1.NBTTagList typealias NBTTagByte12 = net.minecraft.server.v1_12_R1.NBTTagByte @@ -302,4 +307,18 @@ typealias NBTTagInt15 = net.minecraft.server.v1_15_R1.NBTTagInt typealias NBTTagLong15 = net.minecraft.server.v1_15_R1.NBTTagLong typealias NBTTagFloat15 = net.minecraft.server.v1_15_R1.NBTTagFloat typealias NBTTagDouble15 = net.minecraft.server.v1_15_R1.NBTTagDouble -typealias NBTTagString15 = net.minecraft.server.v1_15_R1.NBTTagString \ No newline at end of file +typealias NBTTagString15 = net.minecraft.server.v1_15_R1.NBTTagString + +typealias NBTBase = net.minecraft.nbt.NBTBase +typealias NBTTagCompound = net.minecraft.nbt.NBTTagCompound +typealias NBTTagList = net.minecraft.nbt.NBTTagList +typealias NBTTagByte = net.minecraft.nbt.NBTTagByte +typealias NBTTagShort = net.minecraft.nbt.NBTTagShort +typealias NBTTagInt = net.minecraft.nbt.NBTTagInt +typealias NBTTagLong = net.minecraft.nbt.NBTTagLong +typealias NBTTagFloat = net.minecraft.nbt.NBTTagFloat +typealias NBTTagDouble = net.minecraft.nbt.NBTTagDouble +typealias NBTTagString = net.minecraft.nbt.NBTTagString +typealias NBTTagByteArray = net.minecraft.nbt.NBTTagByteArray +typealias NBTTagIntArray = net.minecraft.nbt.NBTTagIntArray +typealias NBTTagLongArray = net.minecraft.nbt.NBTTagLongArray \ No newline at end of file diff --git a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTCompound.kt b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTCompound.kt index 8d9409c7..9cc24080 100644 --- a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTCompound.kt +++ b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTCompound.kt @@ -12,12 +12,10 @@ open class NBTCompound(override val content: MutableMap) : NBTD constructor() : this(ConcurrentHashMap()) - constructor(map: Map) : this(ConcurrentHashMap(map)) - /** * 克隆数据 */ - override fun clone() = NBTCompound(this.content.mapValues { it.value.clone() }) + override fun clone() = of(this.content.mapValues { it.value.clone() }) /** * 浅克隆数据 @@ -61,4 +59,11 @@ open class NBTCompound(override val content: MutableMap) : NBTD return this } + companion object { + + @JvmStatic + fun of(map: Map): NBTCompound = NBTCompound(ConcurrentHashMap(map)) + + } + } \ No newline at end of file diff --git a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTList.kt b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTList.kt index 882e1934..a12bfc06 100644 --- a/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTList.kt +++ b/project/module-item/src/main/kotlin/cn/fd/ratziel/module/nbt/NBTList.kt @@ -14,6 +14,11 @@ open class NBTList(override val content: MutableList) : NBTData(NBTType constructor(array: Array) : this(array.toMutableList()) + /** + * 存储元素的类型 + */ + val elementType get() = if(content.isNotEmpty()) content.first().type else NBTType.END + /** * 克隆数据 */