hopefully prevent fork bombs, fix #106
This commit is contained in:
parent
8cbcea2833
commit
63ea5822dd
3 changed files with 73 additions and 27 deletions
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue