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,19 +31,47 @@ 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
when (val pl = payload) { ?: NBTBuilder { TAG_WIDGET %= Widget.GARBAGE.name }
is Entity -> TAG_ENTITY %= compound {
TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid)
TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName) // 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 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")
} }
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 = override fun toString(): String =
@ -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<*> =
@ -162,10 +194,10 @@ class SpellDatum<T : Any> private constructor(val payload: T) {
val vec = vecFromNBT(nbt.getLongArray(key)) val vec = vecFromNBT(nbt.getLongArray(key))
// the focus color is really more red, but we don't want to show an error-y color // the focus color is really more red, but we don't want to show an error-y color
out += String.format( out += String.format(
"(%.2f, %.2f, %.2f)", "(%.2f, %.2f, %.2f)",
vec.x, vec.x,
vec.y, vec.y,
vec.z vec.z
).lightPurple ).lightPurple
} }
TAG_LIST -> { TAG_LIST -> {
@ -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

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

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