hopefully prevent fork bombs, fix #106

This commit is contained in:
gamma-delta 2022-06-09 13:54:30 -05:00
parent 8cbcea2833
commit 63ea5822dd
3 changed files with 73 additions and 27 deletions

View file

@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidSpellDatumType import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidSpellDatumType
import at.petrak.hexcasting.api.utils.* import at.petrak.hexcasting.api.utils.*
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtUtils import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
@ -30,20 +31,48 @@ import java.util.*
class SpellDatum<T : Any> private constructor(val payload: T) { class SpellDatum<T : Any> private constructor(val payload: T) {
val clazz: Class<T> = payload.javaClass val clazz: Class<T> = payload.javaClass
fun serializeToNBT() = NBTBuilder { fun serializeToNBT(): CompoundTag = this.serializeToNBTWithDepthCheck(0, 0)?.first
?: NBTBuilder { TAG_WIDGET %= Widget.GARBAGE.name }
// The second int returned is the number of datums contained in this one.
fun serializeToNBTWithDepthCheck(depth: Int, total: Int): Pair<CompoundTag, Int>? {
if (total > MAX_SERIALIZATION_TOTAL)
return null
val tag = NBTBuilder {
when (val pl = payload) { when (val pl = payload) {
is SpellList -> {
// handle it specially!
if (depth + 1 > MAX_SERIALIZATION_DEPTH) {
return null
}
val outList = ListTag()
var total1 = total + 1 // make mutable and include the list itself
for (elt in pl) {
val (t, subtotal) = elt.serializeToNBTWithDepthCheck(depth + 1, total1) ?: return null
total1 += subtotal
outList.add(t)
}
return Pair(
NBTBuilder { TAG_LIST %= outList },
total1
)
}
is Entity -> TAG_ENTITY %= compound { is Entity -> TAG_ENTITY %= compound {
TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid) TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid)
TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName) TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName)
} }
is Double -> TAG_DOUBLE %= pl is Double -> TAG_DOUBLE %= pl
is Vec3 -> TAG_VEC3 %= pl.serializeToNBT() is Vec3 -> TAG_VEC3 %= pl.serializeToNBT()
is SpellList -> TAG_LIST %= pl.serializeToNBT()
is Widget -> TAG_WIDGET %= pl.name is Widget -> TAG_WIDGET %= pl.name
is HexPattern -> TAG_PATTERN %= pl.serializeToNBT() is HexPattern -> TAG_PATTERN %= pl.serializeToNBT()
else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable") else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable")
} }
} }
return Pair(tag, 1)
}
override fun toString(): String = override fun toString(): String =
buildString { buildString {
@ -77,6 +106,9 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
} }
companion object { companion object {
const val MAX_SERIALIZATION_DEPTH = 256
const val MAX_SERIALIZATION_TOTAL = 1024
@JvmStatic @JvmStatic
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
fun make(payload: Any): SpellDatum<*> = fun make(payload: Any): SpellDatum<*> =
@ -200,7 +232,8 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
val subtag = nbt.getCompound(TAG_ENTITY) val subtag = nbt.getCompound(TAG_ENTITY)
val json = subtag.getString(TAG_ENTITY_NAME_CHEATY) val json = subtag.getString(TAG_ENTITY_NAME_CHEATY)
// handle pre-0.5.0 foci not having the tag // handle pre-0.5.0 foci not having the tag
out += Component.Serializer.fromJson(json)?.aqua ?: "hexcasting.spelldata.entity.whoknows".asTranslatedComponent.white out += Component.Serializer.fromJson(json)?.aqua
?: "hexcasting.spelldata.entity.whoknows".asTranslatedComponent.white
} }
else -> throw IllegalArgumentException("Unknown key $key: $nbt") else -> throw IllegalArgumentException("Unknown key $key: $nbt")
} }

View file

@ -1,12 +1,15 @@
@file:JvmName("HexUtils") @file:JvmName("HexUtils")
package at.petrak.hexcasting.api.utils package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.math.HexCoord import at.petrak.hexcasting.api.spell.math.HexCoord
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.LongArrayTag import net.minecraft.nbt.LongArrayTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.* import net.minecraft.network.chat.*
import net.minecraft.world.InteractionHand import net.minecraft.world.InteractionHand
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
@ -179,14 +182,18 @@ interface WeakValue<T> {
private class WeakReferencedValue<T>(var reference: WeakReference<T>?) : WeakValue<T> { private class WeakReferencedValue<T>(var reference: WeakReference<T>?) : WeakValue<T> {
override var value: T? override var value: T?
get() = reference?.get() get() = reference?.get()
set(value) { reference = value?.let { WeakReference(it) } } set(value) {
reference = value?.let { WeakReference(it) }
}
} }
private class WeakMappedValue<K, T>(val keyGen: (T) -> K) : WeakValue<T> { private class WeakMappedValue<K, T>(val keyGen: (T) -> K) : WeakValue<T> {
val reference = WeakHashMap<K, T>() val reference = WeakHashMap<K, T>()
override var value: T? override var value: T?
get() = reference.values.firstOrNull() get() = reference.values.firstOrNull()
set(value) { if (value != null) reference[keyGen(value)] = value else reference.clear() } set(value) {
if (value != null) reference[keyGen(value)] = value else reference.clear()
}
} }
fun <T> weakReference(value: T? = null): WeakValue<T> = WeakReferencedValue(value?.let { WeakReference(it) }) fun <T> weakReference(value: T? = null): WeakValue<T> = WeakReferencedValue(value?.let { WeakReference(it) })
@ -197,13 +204,19 @@ fun <T, K> weakMapped(keyGen: (T) -> K): WeakValue<T> = WeakMappedValue(keyGen)
inline operator fun <T> WeakValue<T>.getValue(thisRef: Any?, property: KProperty<*>): T? = value inline operator fun <T> WeakValue<T>.getValue(thisRef: Any?, property: KProperty<*>): T? = value
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
inline operator fun <T> WeakValue<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T?) { this.value = value } inline operator fun <T> WeakValue<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
this.value = value
}
/**
* Returns an empty list if it's too complicated.
*/
fun Iterable<SpellDatum<*>>.serializeToNBT(): ListTag { fun Iterable<SpellDatum<*>>.serializeToNBT(): ListTag {
val tag = ListTag() val out = SpellDatum.make(SpellList.LList(0, this.toList())).serializeToNBT()
for (elt in this) return if (out.contains(SpellDatum.TAG_WIDGET))
tag.add(elt.serializeToNBT()) ListTag()
return tag else
out.getList(SpellDatum.TAG_LIST, Tag.TAG_COMPOUND)
} }
// Copy the impl from forge // Copy the impl from forge