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.utils.*
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
@ -30,20 +31,48 @@ import java.util.*
class SpellDatum<T : Any> private constructor(val payload: T) {
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) {
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 {
TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid)
TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName)
}
is Double -> TAG_DOUBLE %= pl
is Vec3 -> TAG_VEC3 %= pl.serializeToNBT()
is SpellList -> TAG_LIST %= pl.serializeToNBT()
is Widget -> TAG_WIDGET %= pl.name
is HexPattern -> TAG_PATTERN %= pl.serializeToNBT()
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 =
buildString {
@ -77,6 +106,9 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
}
companion object {
const val MAX_SERIALIZATION_DEPTH = 256
const val MAX_SERIALIZATION_TOTAL = 1024
@JvmStatic
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
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 json = subtag.getString(TAG_ENTITY_NAME_CHEATY)
// 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")
}

View file

@ -9,17 +9,17 @@ import net.minecraft.server.level.ServerLevel
*
* ...Surely this won't have any performance implications.
*/
sealed class SpellList: Iterable<SpellDatum<*>> {
sealed class SpellList : Iterable<SpellDatum<*>> {
abstract val nonEmpty: Boolean
abstract val car: SpellDatum<*>
abstract val cdr: SpellList
class LPair(override val car: SpellDatum<*>, override val cdr: SpellList): SpellList() {
class LPair(override val car: SpellDatum<*>, override val cdr: SpellList) : SpellList() {
override val nonEmpty = true
}
class LList(val idx: Int, val list: List<SpellDatum<*>>): SpellList() {
class LList(val idx: Int, val list: List<SpellDatum<*>>) : SpellList() {
override val nonEmpty: Boolean
get() = idx < list.size
override val car: SpellDatum<*>
@ -69,7 +69,7 @@ sealed class SpellList: Iterable<SpellDatum<*>> {
override fun iterator() = SpellListIterator(this)
class SpellListIterator(var list: SpellList): Iterator<SpellDatum<*>> {
class SpellListIterator(var list: SpellList) : Iterator<SpellDatum<*>> {
override fun hasNext() = list.nonEmpty
override operator fun next(): SpellDatum<*> {
val car = list.car

View file

@ -1,12 +1,15 @@
@file:JvmName("HexUtils")
package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.math.HexCoord
import net.minecraft.ChatFormatting
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.LongArrayTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.*
import net.minecraft.world.InteractionHand
import net.minecraft.world.item.ItemStack
@ -179,14 +182,18 @@ interface WeakValue<T> {
private class WeakReferencedValue<T>(var reference: WeakReference<T>?) : WeakValue<T> {
override var value: T?
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> {
val reference = WeakHashMap<K, T>()
override var value: T?
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) })
@ -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
@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 {
val tag = ListTag()
for (elt in this)
tag.add(elt.serializeToNBT())
return tag
val out = SpellDatum.make(SpellList.LList(0, this.toList())).serializeToNBT()
return if (out.contains(SpellDatum.TAG_WIDGET))
ListTag()
else
out.getList(SpellDatum.TAG_LIST, Tag.TAG_COMPOUND)
}
// Copy the impl from forge