From e50f2331762456799a160663202270248b3b175b Mon Sep 17 00:00:00 2001 From: Talia-12 Date: Sun, 4 Jun 2023 17:17:33 +1000 Subject: [PATCH] made Evanition interact properly with unescaped and escaped introspection/retrospections. --- .../api/casting/eval/vm/CastingImage.kt | 41 ++++++++++++++++--- .../api/casting/eval/vm/CastingVM.kt | 18 +++++--- .../petrak/hexcasting/api/utils/HexUtils.kt | 16 ++++++++ .../hexcasting/client/gui/GuiSpellcasting.kt | 2 +- 4 files changed, 65 insertions(+), 12 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt index bf58503c..d04b1c29 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt @@ -1,10 +1,14 @@ package at.petrak.hexcasting.api.casting.eval.vm import at.petrak.hexcasting.api.HexAPI +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota.Companion.TAG_ESCAPED +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota.Companion.TAG_IOTAS import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.IotaType +import at.petrak.hexcasting.api.casting.iota.ListIota import at.petrak.hexcasting.api.utils.* import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.ListTag import net.minecraft.nbt.Tag import net.minecraft.server.level.ServerLevel import net.minecraft.world.entity.Entity @@ -16,7 +20,7 @@ data class CastingImage private constructor( val stack: List, val parenCount: Int, - val parenthesized: List, + val parenthesized: List, val escapeNext: Boolean, val opsConsumed: Long, @@ -24,6 +28,30 @@ data class CastingImage private constructor( ) { constructor() : this(listOf(), 0, listOf(), false, 0, CompoundTag()) + data class ParenthesizedIota(val iota: Iota, val escaped: Boolean) { + companion object { + const val TAG_IOTAS = "iotas" + const val TAG_ESCAPED = "escaped" + } + } + + /** + * Returns an empty list if it's too complicated. + */ + private fun Iterable.serializeToNBT(): CompoundTag { + val tag = CompoundTag() + + if (IotaType.isTooLargeToSerialize(this.map { it.iota })) { + tag.put(TAG_IOTAS, ListTag()) + tag.put(TAG_ESCAPED, ListTag()) + } else { + tag.put(TAG_IOTAS, ListIota(this.map { it.iota }).serialize()) + tag.put(TAG_ESCAPED, this.map { it.escaped }.serializeToNBT()) + } + + return tag + } + /** * Return a copy of this with the given number of ops additionally exhausted */ @@ -74,10 +102,13 @@ data class CastingImage private constructor( CompoundTag() } - val parenthesized = mutableListOf() - val parenTag = tag.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND) - for (subtag in parenTag) { - parenthesized.add(IotaType.deserialize(subtag.downcast(CompoundTag.TYPE), world)) + val parenthesized = mutableListOf() + val parenTag = tag.getCompound(TAG_PARENTHESIZED) + val parenIotasTag = parenTag.getList(TAG_IOTAS, Tag.TAG_COMPOUND) + val parenEscapedTag = parenTag.getByteArray(TAG_ESCAPED) + + for ((subtag, isEscapedByte) in parenIotasTag.zipWithDefault(parenEscapedTag) { _ -> 0 }) { + parenthesized.add(ParenthesizedIota(IotaType.deserialize(subtag.downcast(CompoundTag.TYPE), world), isEscapedByte != 0.toByte())) } val parenCount = tag.getInt(TAG_PAREN_COUNT) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt index 9d12b259..f2bcce36 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt @@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.casting.PatternShapeMatch.* import at.petrak.hexcasting.api.casting.SpellList import at.petrak.hexcasting.api.casting.eval.* import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.IotaType import at.petrak.hexcasting.api.casting.iota.ListIota @@ -156,7 +157,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { val out = if (displayDepth > 0) { if (this.image.escapeNext) { val newParens = this.image.parenthesized.toMutableList() - newParens.add(iota) + newParens.add(ParenthesizedIota(iota, true)) this.image.copy( escapeNext = false, parenthesized = newParens @@ -173,13 +174,18 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { SpecialPatterns.EVANITION.anglesSignature() -> { val newParens = this.image.parenthesized.toMutableList() val last = newParens.removeLastOrNull() - this.image.copy(parenthesized = newParens) to if (last == null) ResolvedPatternType.ERRORED else ResolvedPatternType.UNDONE + val newParenCount = this.image.parenCount + if (last == null || last.escaped || last.iota !is PatternIota) 0 else when (last.iota.pattern) { + SpecialPatterns.INTROSPECTION -> -1 + SpecialPatterns.RETROSPECTION -> 1 + else -> -1 + } + this.image.copy(parenthesized = newParens, parenCount = newParenCount) to if (last == null) ResolvedPatternType.ERRORED else ResolvedPatternType.UNDONE } SpecialPatterns.INTROSPECTION.anglesSignature() -> { // we have escaped the parens onto the stack; we just also record our count. val newParens = this.image.parenthesized.toMutableList() - newParens.add(iota) + newParens.add(ParenthesizedIota(iota, false)) this.image.copy( parenthesized = newParens, parenCount = this.image.parenCount + 1 @@ -191,7 +197,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { displayDepth-- if (newParenCount == 0) { val newStack = this.image.stack.toMutableList() - newStack.add(ListIota(this.image.parenthesized.toList())) + newStack.add(ListIota(this.image.parenthesized.toList().map { it.iota })) this.image.copy( stack = newStack, parenCount = newParenCount, @@ -203,7 +209,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { // we have this situation: "(()" // we need to add the close paren val newParens = this.image.parenthesized.toMutableList() - newParens.add(iota) + newParens.add(ParenthesizedIota(iota, false)) this.image.copy( parenCount = newParenCount, parenthesized = newParens @@ -213,7 +219,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { else -> { val newParens = this.image.parenthesized.toMutableList() - newParens.add(iota) + newParens.add(ParenthesizedIota(iota, false)) this.image.copy( parenthesized = newParens ) to ResolvedPatternType.ESCAPED diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt index 19b6fe0a..20929776 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt @@ -267,6 +267,22 @@ fun Iterable.serializeToNBT() = else ListIota(this.toList()).serialize() +fun Iterable.serializeToNBT(): ByteArrayTag { + val out = ByteArray(if (this is Collection<*>) this.size else 10) + for ((i, b) in this.withIndex()) { + out[i] = if (b) 1 else 0 + } + return ByteArrayTag(out) +} + +fun List.zipWithDefault(array: ByteArray, default: (idx: Int) -> Byte): List> { + val list = ArrayList>(this.size) + var i = 0 + for (element in this) list.add(element to array.getOrElse(i++, default)) + + return list +} + // Copy the impl from forge fun ItemStack.serializeToNBT(): CompoundTag { val out = CompoundTag() diff --git a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt index 8979b55f..321915fc 100644 --- a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt +++ b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt @@ -69,7 +69,7 @@ class GuiSpellcasting constructor( // TODO this is the kinda hacky bit if (info.resolutionType == ResolvedPatternType.UNDONE) { - this.patterns.getOrNull(index - 1)?.let { it.type = ResolvedPatternType.UNDONE } + this.patterns.reversed().drop(1).firstOrNull { it.type == ResolvedPatternType.ESCAPED }?.let { it.type = ResolvedPatternType.UNDONE } this.patterns.getOrNull(index)?.let { it.type = ResolvedPatternType.EVALUATED } } else this.patterns.getOrNull(index)?.let { it.type = info.resolutionType