Skip to content
Merged
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 @@ -21,6 +21,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

- 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 ([#1032](https://github.com/FallingColors/HexMod/pull/1032)) @s5bug
- 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 @@ -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 at.petrak.hexcasting.api.utils.compositeCodecSeven
Expand All @@ -18,16 +19,15 @@ 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 simulateNext: Boolean,
val opsConsumed: Long,
val userData: CompoundTag
) {
constructor() : this(listOf(), 0, listOf(), false, false, 0, CompoundTag())
constructor() : this(TreeList.empty(), 0, TreeList.empty(), false, 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 @@ -67,14 +67,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 @@ -93,9 +92,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.BOOL.fieldOf("simulate_next").forGetter { it.simulateNext },
Codec.LONG.fieldOf("ops_consumed").forGetter { it.opsConsumed },
Expand All @@ -106,9 +105,9 @@ data class CastingImage(
}.orElseGet(::CastingImage)
@JvmStatic
val STREAM_CODEC = compositeCodecSeven(
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.BOOL, CastingImage::simulateNext,
ByteBufCodecs.VAR_LONG, CastingImage::opsConsumed,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,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 All @@ -167,8 +165,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
// if simulating, push a bool for whether the cast would have succeeded; do not perform any side effects
if (this.image.simulateNext) {
val tooBig = result.newData != null && IotaType.isTooLargeToSerialize(result.newData.stack)
val newStack = this.image.stack.toMutableList()
newStack.add(BooleanIota(result.resolutionType.success && !tooBig))
val newStack = this.image.stack.appended(BooleanIota(result.resolutionType.success && !tooBig))
val newImage = this.image.copy(
stack = newStack,
simulateNext = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,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.api.utils.TreeList
import at.petrak.hexcasting.common.lib.HexRegistries
import at.petrak.hexcasting.common.lib.hex.HexContinuationTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import net.minecraft.server.level.ServerLevel
*/
data class FrameEvaluate(val list: TreeList<Iota>, val isMetacasting: Boolean) : ContinuationFrame {
// Discard this frame and keep discarding frames.
override fun breakDownwards(stack: List<Iota>) = false to stack
override fun breakDownwards(stack: TreeList<Iota>) = false to stack

// Step the list of patterns, evaluating a single one.
override fun evaluate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.NullIota
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import com.mojang.serialization.MapCodec
import net.minecraft.network.RegistryFriendlyByteBuf
Expand All @@ -16,7 +17,7 @@ import net.minecraft.server.level.ServerLevel
*/
object FrameFinishEval : ContinuationFrame {
// Don't do anything else to the stack, just finish the halt statement.
override fun breakDownwards(stack: List<Iota>) = true to stack
override fun breakDownwards(stack: TreeList<Iota>) = true to stack

// Evaluating it does nothing; it's only a boundary condition.
override fun evaluate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ import kotlin.jvm.optionals.getOrNull
data class FrameForEach(
val data: TreeList<Iota>,
val code: TreeList<Iota>,
val baseStack: List<Iota>?,
val baseStack: TreeList<Iota>?,
val acc: TreeList<Iota>
) : ContinuationFrame {

/** When halting, we add the stack state at halt to the stack accumulator, then return the original pre-Thoth stack, plus the accumulator. */
override fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>> {
val newStack = baseStack?.toMutableList() ?: mutableListOf()
newStack.add(ListIota(acc.appendedAll(stack)))
return true to newStack
override fun breakDownwards(stack: TreeList<Iota>): Pair<Boolean, TreeList<Iota>> {
val newStack = baseStack ?: TreeList.empty()
return true to newStack.appended(ListIota(acc.appendedAll(stack)))
}

/** Step the Thoth computation, enqueueing one code evaluation. */
Expand All @@ -48,7 +47,7 @@ data class FrameForEach(
// If this is the very first Thoth step (i.e. no Thoth computations run yet)...
val (stack, newAcc) = if (baseStack == null) {
// init stack to the harness stack...
harness.image.stack.toList() to acc
harness.image.stack to acc
} else {
// else save the stack to the accumulator and reuse the saved base stack.
baseStack to acc.appendedAll(harness.image.stack)
Expand All @@ -67,8 +66,7 @@ data class FrameForEach(
// Else, dump our final list onto the stack.
Triple(ListIota(newAcc), harness.image, continuation)
}
val tStack = stack.toMutableList()
tStack.add(stackTop)
val tStack = stack.appended(stackTop)
return CastResult(
ListIota(code),
newCont,
Expand All @@ -91,7 +89,7 @@ data class FrameForEach(
inst.group(
TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("data").forGetter { it.data },
TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("code").forGetter { it.code },
IotaType.TYPED_CODEC.listOf().optionalFieldOf("base").forGetter { Optional.ofNullable(it.baseStack) },
TreeList.codecOf(IotaType.TYPED_CODEC).optionalFieldOf("base").forGetter { Optional.ofNullable(it.baseStack) },
TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("accumulator").forGetter { it.acc }
).apply(inst) { a, b, c, d ->
FrameForEach(a, b, c.getOrNull(), d)
Expand All @@ -101,7 +99,7 @@ data class FrameForEach(
IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::data,
IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::code,
ByteBufCodecs.optional(IotaType.TYPED_STREAM_CODEC
.apply(ByteBufCodecs.list())), { Optional.ofNullable(it.baseStack) },
.apply(TreeList.streamCodecOp())), { Optional.ofNullable(it.baseStack) },
IotaType.TYPED_STREAM_CODEC
.apply(TreeList.streamCodecOp()), FrameForEach::acc
) { a, b, c, d ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import at.petrak.hexcasting.api.utils.lightPurple
import at.petrak.hexcasting.common.lib.HexItems
Expand Down Expand Up @@ -37,15 +38,10 @@ abstract class Mishap : RuntimeException() {
*
* You can also mess up the stack with this.
*/
abstract fun execute(env: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>)
abstract fun execute(env: CastingEnvironment, errorCtx: Context, stack: TreeList<Iota>): TreeList<Iota>

protected abstract fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component?

fun executeReturnStack(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>): List<Iota> {
execute(ctx, errorCtx, stack)
return stack
}

/**
* Every error message should be prefixed with the name of the action...
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.common.lib.HexDamageTypes
import net.minecraft.world.entity.Mob
import net.minecraft.world.item.DyeColor
Expand All @@ -12,8 +13,9 @@ class MishapAlreadyBrainswept(val mob: Mob) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.GREEN)

override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: TreeList<Iota>): TreeList<Iota> {
mob.hurt(mob.damageSources().source(HexDamageTypes.OVERCAST, ctx.castingEntity), mob.health)
return stack
}

override fun particleSpray(ctx: CastingEnvironment) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
Expand All @@ -15,8 +16,9 @@ class MishapBadBlock(val pos: BlockPos, val expected: Component) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.LIME)

override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: TreeList<Iota>): TreeList<Iota> {
ctx.world.explode(null, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, 0.25f, Level.ExplosionInteraction.NONE)
return stack
}

override fun particleSpray(ctx: CastingEnvironment) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.api.utils.TreeList
import at.petrak.hexcasting.common.lib.HexDamageTypes
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.Mob
Expand All @@ -14,8 +15,9 @@ class MishapBadBrainsweep(val mob: Mob, val pos: BlockPos) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.GREEN)

override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: TreeList<Iota>): TreeList<Iota> {
trulyHurt(mob, mob.damageSources().source(HexDamageTypes.OVERCAST, ctx.castingEntity), 1f)
return stack
}

override fun particleSpray(ctx: CastingEnvironment): ParticleSpray {
Expand Down
Loading
Loading