Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Internal

- Removed APIs deprecated in `0.11.4` ([#1126](https://github.com/FallingColors/HexMod/pull/1126), [#1127](https://github.com/FallingColors/HexMod/pull/1127) [#1129](https://github.com/FallingColors/HexMod/pull/1129), [#1137](https://github.com/FallingColors/HexMod/pull/1137), [#1142](https://github.com/FallingColors/HexMod/pull/1142)) @s5bug
- ListIota now uses the asymptotically-efficient TreeList internally ([#1032](https://github.com/FallingColors/HexMod/pull/1032)) @s5bug
- SpellList has been removed, the Casting Image and Casting Frames now store iotas in a TreeList ([#1033](https://github.com/FallingColors/HexMod/pull/1033)) @s5bug

## `0.11.3` - 2025-11-22

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import at.petrak.hexcasting.api.casting.iota.*
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import at.petrak.hexcasting.api.utils.validateIota
import com.mojang.datafixers.util.Either
Expand Down Expand Up @@ -43,7 +44,7 @@ fun List<Iota>.getEntity(level: ServerLevel, idx: Int, argc: Int = 0): Entity {
}
}

fun List<Iota>.getList(idx: Int, argc: Int = 0): SpellList {
fun List<Iota>.getList(idx: Int, argc: Int = 0): TreeList<Iota> {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is ListIota) {
return x.list
Expand Down Expand Up @@ -271,7 +272,7 @@ fun List<Iota>.getNumOrVec(idx: Int, argc: Int = 0): Either<Double, Vec3> {
}
}

fun List<Iota>.getLongOrList(idx: Int, argc: Int = 0): Either<Long, SpellList> {
fun List<Iota>.getLongOrList(idx: Int, argc: Int = 0): Either<Long, TreeList<Iota>> {
val datum = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (datum is DoubleIota) {
val double = datum.double
Expand All @@ -289,7 +290,7 @@ fun List<Iota>.getLongOrList(idx: Int, argc: Int = 0): Either<Long, SpellList> {
)
}

fun evaluatable(datum: Iota, reverseIdx: Int): Either<Iota, SpellList> =
fun evaluatable(datum: Iota, reverseIdx: Int): Either<Iota, TreeList<Iota>> =
when (datum) {
is ListIota -> Either.right(datum.list)
else -> if (datum.executable()) Either.left(datum) else throw MishapInvalidIota(
Expand Down Expand Up @@ -320,7 +321,7 @@ inline val Boolean.asActionResult get() = listOf(BooleanIota(this))
inline val Double.asActionResult get() = listOf(DoubleIota(this))
inline val Number.asActionResult get() = listOf(DoubleIota(this.toDouble()))

inline val SpellList.asActionResult get() = listOf(ListIota(this))
inline val TreeList<Iota>.asActionResult get() = listOf(ListIota(this))
inline val List<Iota>.asActionResult get() = listOf(ListIota(this))

inline val BlockPos.asActionResult get() = listOf(Vec3Iota(Vec3.atCenterOf(this)))
Expand Down
108 changes: 0 additions & 108 deletions Common/src/main/java/at/petrak/hexcasting/api/casting/SpellList.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ abstract class OperatorBasic(arity: Int, accepts: IotaMultiPredicate) : Operator

@Throws(Mishap::class)
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
val stack = image.stack.toMutableList()
val args = stack.takeLast(arity)
repeat(arity) { stack.removeLast() }
val stack = image.stack
val args = stack.takeRight(arity)
val stackWithoutArgs = stack.dropRight(arity)

val ret = apply(args, env)
ret.forEach(Consumer { e: Iota -> stack.add(e) })
val stackWithResult = stackWithoutArgs.appendedAll(ret)

val image2 = image.copy(stack = stack, opsConsumed = image.opsConsumed + 1)
val image2 = image.copy(stack = stackWithResult, opsConsumed = image.opsConsumed + 1)
return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,21 @@ interface ConstMediaAction : Action {
}

override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
val stack = image.stack.toMutableList()
val stack = image.stack

if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc)
repeat(this.argc) { stack.removeLast() }
val args = stack.takeRight(this.argc)
val stackWithoutArgs = stack.dropRight(this.argc)
val result = this.executeWithOpCount(args, env)
stack.addAll(result.resultStack)
val stackWithResult = stackWithoutArgs.appendedAll(result.resultStack)

if (env.extractMedia(this.mediaCost, true) > 0)
throw MishapNotEnoughMedia(this.mediaCost)

val sideEffects = mutableListOf<OperatorSideEffect>(OperatorSideEffect.ConsumeMedia(this.mediaCost))

val image2 = image.copy(stack = stack, opsConsumed = image.opsConsumed + result.opCount)
val image2 = image.copy(stack = stackWithResult, opsConsumed = image.opsConsumed + result.opCount)
return OperationResult(image2, sideEffects, continuation, HexEvalSounds.NORMAL_EXECUTE)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ interface SpellAction : Action {
}

override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
val stack = image.stack.toMutableList()
val stack = image.stack

if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast()
val args = stack.takeRight(this.argc)
val stackWithoutArgs = stack.dropRight(this.argc)

// execute!
val userDataMut = image.userData.copy()
Expand All @@ -65,7 +65,7 @@ interface SpellAction : Action {
for (spray in result.particles)
sideEffects.add(OperatorSideEffect.Particles(spray))

val image2 = image.copy(stack = stack, opsConsumed = image.opsConsumed + result.opCount, userData = userDataMut)
val image2 = image.copy(stack = stackWithoutArgs, opsConsumed = image.opsConsumed + result.opCount, userData = userDataMut)

val sound = if (this.hasCastingSound(env)) HexEvalSounds.SPELL else HexEvalSounds.MUTE
return OperationResult(image2, sideEffects, continuation, sound)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ sealed class OperatorSideEffect {
)
)

harness.image = harness.image.copy(stack = mishap.executeReturnStack(harness.env, errorCtx, harness.image.stack.toMutableList()))
harness.image = harness.image.copy(stack = mishap.execute(harness.env, errorCtx, harness.image.stack))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.api.utils.getOrCreateCompound
import at.petrak.hexcasting.api.utils.putCompound
import com.mojang.serialization.Codec
Expand All @@ -17,15 +18,14 @@ import java.util.Optional
* The state of a casting VM, containing the stack and all
*/
data class CastingImage(
val stack: List<Iota>,

val stack: TreeList<Iota>,
val parenCount: Int,
val parenthesized: List<ParenthesizedIota>,
val parenthesized: TreeList<ParenthesizedIota>,
val escapeNext: Boolean,
val opsConsumed: Long,
val userData: CompoundTag
) {
constructor() : this(listOf(), 0, listOf(), false, 0, CompoundTag())
constructor() : this(TreeList.empty(), 0, TreeList.empty(), false, 0, CompoundTag())

/**
* `escaped` is used by [OpUndo][at.petrak.hexcasting.common.casting.actions.escaping.OpUndo] to determine whether the paren count
Expand Down Expand Up @@ -65,14 +65,13 @@ data class CastingImage(
/**
* Returns a copy of this with escape/paren-related fields cleared.
*/
fun withResetEscape() = this.copy(parenCount = 0, parenthesized = listOf(), escapeNext = false)
fun withResetEscape() = this.copy(parenCount = 0, parenthesized = TreeList.empty(), escapeNext = false)

/**
* Returns a copy of this with the provided iota added to the parenthesized list.
*/
fun withNewParenthesized(iota: Iota): CastingImage {
val newParens = this.parenthesized.toMutableList()
newParens.add(ParenthesizedIota(iota, false))
val newParens = this.parenthesized.appended(ParenthesizedIota(iota, false))
return this.copy(parenthesized = newParens)
}

Expand All @@ -91,9 +90,9 @@ data class CastingImage(
@JvmStatic
val CODEC = RecordCodecBuilder.create<CastingImage> { inst ->
inst.group(
IotaType.TYPED_CODEC.listOf().fieldOf("stack").forGetter { it.stack },
TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("stack").forGetter { it.stack },
Codec.INT.fieldOf("open_parens").forGetter { it.parenCount },
ParenthesizedIota.CODEC.listOf().fieldOf("parenthesized").forGetter { it.parenthesized },
TreeList.codecOf(ParenthesizedIota.CODEC).fieldOf("parenthesized").forGetter { it.parenthesized },
Codec.BOOL.fieldOf("escape_next").forGetter { it.escapeNext },
Codec.LONG.fieldOf("ops_consumed").forGetter { it.opsConsumed },
CompoundTag.CODEC.fieldOf("userData").forGetter { it.userData }
Expand All @@ -103,9 +102,9 @@ data class CastingImage(
}.orElseGet(::CastingImage)
@JvmStatic
val STREAM_CODEC = StreamCodec.composite(
IotaType.TYPED_STREAM_CODEC.apply(ByteBufCodecs.list()), CastingImage::stack,
IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), CastingImage::stack,
ByteBufCodecs.VAR_INT, CastingImage::parenCount,
ParenthesizedIota.STREAM_CODEC.apply(ByteBufCodecs.list()), CastingImage::parenthesized,
ParenthesizedIota.STREAM_CODEC.apply(TreeList.streamCodecOp()), CastingImage::parenthesized,
ByteBufCodecs.BOOL, CastingImage::escapeNext,
ByteBufCodecs.VAR_LONG, CastingImage::opsConsumed,
ByteBufCodecs.COMPOUND_TAG, { it.userData },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.casting.eval.vm

import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
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
Expand All @@ -15,6 +15,7 @@ import at.petrak.hexcasting.api.casting.mishaps.Mishap
import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooMuch
import at.petrak.hexcasting.api.casting.mishaps.MishapInternalException
import at.petrak.hexcasting.api.casting.mishaps.MishapStackSize
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.api.utils.validateIota
import at.petrak.hexcasting.api.utils.validateIotaList
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
Expand Down Expand Up @@ -50,7 +51,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
stack = validateIotaList(this.image.stack, world)
)
// Initialize the continuation stack to a single top-level eval for all iotas.
var continuation = SpellContinuation.Done.pushFrame(FrameEvaluate(SpellList.LList(0, iotas), false))
var continuation = SpellContinuation.Done.pushFrame(FrameEvaluate(TreeList.from(iotas), false))
// Begin aggregating info
val info = TempControllerInfo(earlyExit = false)
var lastResolutionType = ResolvedPatternType.UNRESOLVED
Expand Down Expand Up @@ -132,16 +133,14 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
val newImage: CastingImage
if (this.image.parenCount > 0) {
// if we're inside parentheses, add the iota to the list with escaped set to true
val newParens = this.image.parenthesized.toMutableList()
newParens.add(ParenthesizedIota(iota, true))
val newParens = this.image.parenthesized.appended(ParenthesizedIota(iota, true))
newImage = this.image.copy(
escapeNext = false,
parenthesized = newParens
)
} else {
// if we're not in parentheses, just push the iota to the stack
val newStack = this.image.stack.toMutableList()
newStack.add(iota)
val newStack = this.image.stack.appended(iota)
newImage = this.image.copy(
stack = newStack,
escapeNext = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.common.lib.HexRegistries
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.common.lib.hex.HexContinuationTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions
import com.mojang.serialization.Codec
Expand Down Expand Up @@ -41,7 +42,7 @@ interface ContinuationFrame {
* In other words, we should consume Evaluate frames until we hit a FinishEval or Thoth frame.
* @return whether the break should stop here, alongside the new stack state (e.g. for finalizing a Thoth)
*/
fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>>
fun breakDownwards(stack: TreeList<Iota>): Pair<Boolean, TreeList<Iota>>

/**
* Return the number of iotas contained inside this frame, used for determining whether it is valid to serialise.
Expand Down Expand Up @@ -81,7 +82,6 @@ interface ContinuationFrame {
}

companion object {

/**
* This method attempts to find the type from the `type` key.
* See [ContinuationFrame.serializeToNBT] for the storage format.
Expand Down
Loading
Loading