diff --git a/Common/src/main/java/at/petrak/hexcasting/README.md b/Common/src/main/java/at/petrak/hexcasting/README.md
index a96585f0..ffbae014 100644
--- a/Common/src/main/java/at/petrak/hexcasting/README.md
+++ b/Common/src/main/java/at/petrak/hexcasting/README.md
@@ -2,7 +2,7 @@ Hello, intrepid Github reader!
The "flavor text" words for things in this mod and the internal names are different. (Sorry.)
-- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/casting/eval/CastingHarness.kt)
+- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/casting/eval/vm/CastingVM.kt)
- A "Pattern" is a [`HexPattern`](api/casting/math/HexPattern.kt)
- An "Action" is an [`Operator`](api/casting/castables/Action.kt)
- An action that pushes a spell is a [`Spell`](api/casting/castables/SpellAction.kt)
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java
index 1436b797..32294f0b 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java
@@ -108,6 +108,11 @@ public interface HexAPI {
return FrozenColorizer.DEFAULT.get();
}
+ /**
+ * Location in the userdata of the ravenmind
+ */
+ ResourceLocation RAVENMIND_USERDATA = modLoc("ravenmind");
+
static HexAPI instance() {
return INSTANCE.get();
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java b/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java
index 4fc1e2c5..45e3e404 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java
@@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.addldata;
import at.petrak.hexcasting.api.casting.iota.Iota;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;
@@ -14,7 +14,7 @@ public interface ADIotaHolder {
default Iota readIota(ServerLevel world) {
var tag = readIotaTag();
if (tag != null) {
- return HexIotaTypes.deserialize(tag, world);
+ return IotaType.deserialize(tag, world);
} else {
return null;
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/block/circle/BlockEntityAbstractImpetus.java b/Common/src/main/java/at/petrak/hexcasting/api/block/circle/BlockEntityAbstractImpetus.java
index 7d931f82..6c0ee74c 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/block/circle/BlockEntityAbstractImpetus.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/block/circle/BlockEntityAbstractImpetus.java
@@ -1,14 +1,14 @@
package at.petrak.hexcasting.api.block.circle;
import at.petrak.hexcasting.api.block.HexBlockEntity;
+import at.petrak.hexcasting.api.casting.ParticleSpray;
+import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
+import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
+import at.petrak.hexcasting.api.casting.iota.PatternIota;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.misc.MediaConstants;
import at.petrak.hexcasting.api.mod.HexConfig;
-import at.petrak.hexcasting.api.casting.ParticleSpray;
-import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
-import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
-import at.petrak.hexcasting.api.casting.iota.PatternIota;
import at.petrak.hexcasting.api.utils.MediaHelper;
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker;
import at.petrak.hexcasting.common.lib.HexItems;
@@ -284,7 +284,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
var ctx = new CastingEnvironment(splayer, InteractionHand.MAIN_HAND,
new SpellCircleContext(this.getBlockPos(), bounds, this.activatorAlwaysInRange()));
- var harness = new CastingHarness(ctx);
+ var harness = new CastingVM(ctx);
BlockPos erroredPos = null;
for (var tracked : this.trackedBlocks) {
@@ -292,7 +292,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
if (bs.getBlock() instanceof BlockCircleComponent cc) {
var newPattern = cc.getPattern(tracked, bs, this.level);
if (newPattern != null) {
- var info = harness.executeIota(new PatternIota(newPattern), splayer.getLevel());
+ var info = harness.queueAndExecuteIota(new PatternIota(newPattern), splayer.getLevel());
if (!info.getResolutionType().getSuccess()) {
erroredPos = tracked;
break;
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java
index e8787fd9..6ab913c4 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java
@@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.casting.eval;
-import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.ParticleSpray;
+import at.petrak.hexcasting.api.casting.PatternShapeMatch;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.casting.mishaps.MishapDisallowedSpell;
import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway;
@@ -9,7 +9,6 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapLocationTooFarAway;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexConfig;
import net.minecraft.core.BlockPos;
-import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
@@ -57,7 +56,7 @@ public abstract class CastingEnvironment {
*
* This is used for stuff like requiring enlightenment and pattern denylists
*/
- public void precheckAction(ResourceKey key) throws Mishap {
+ public void precheckAction(PatternShapeMatch match) throws Mishap {
if (!HexConfig.server().isActionAllowed(key.location())) {
throw new MishapDisallowedSpell();
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ControllerInfo.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ControllerInfo.kt
deleted file mode 100644
index c17e096c..00000000
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ControllerInfo.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package at.petrak.hexcasting.api.casting.eval
-
-import net.minecraft.nbt.CompoundTag
-
-/**
- * Information for the sake of the GUI.
- */
-data class ControllerInfo(
- val isStackClear: Boolean,
- val resolutionType: ResolvedPatternType,
- val stack: List,
- val parenthesized: List,
- val ravenmind: CompoundTag?,
- val parenCount: Int,
-)
-
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ExecutionClientView.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ExecutionClientView.kt
new file mode 100644
index 00000000..5534653a
--- /dev/null
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ExecutionClientView.kt
@@ -0,0 +1,15 @@
+package at.petrak.hexcasting.api.casting.eval
+
+import net.minecraft.network.chat.Component
+
+/**
+ * Information sent back to the client
+ */
+data class ExecutionClientView(
+ val isStackClear: Boolean,
+ val resolutionType: ResolvedPatternType,
+
+ val stackDescs: List,
+ val ravenmind: Component?,
+)
+
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/README.md b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/README.md
new file mode 100644
index 00000000..039c0738
--- /dev/null
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/README.md
@@ -0,0 +1,74 @@
+# How Casting Works
+
+because I keep forgetting.
+
+- [`CastingVM`][] is the casting virtual machine. It is what figures out what to do with an incoming iota to be
+ executed,
+ producing a functional state update. This is transient and is reconstructed every time the whole state needs to be
+ saved and loaded.
+- [`CastingImage`][] is the state of the cast itself. If [`CastingVM`][] is an emulator, [`CastingImage`][] is a
+ snapshot of the
+ emulator's memory. This is the only thing serialized to NBT.
+- [`CastingEnvironment`][] is what is doing the casting, abstractly. Stuff like "the player with a staff," "the player
+ with a trinket," "a spell circle." This is an abstract class that Hexcasting (and addons!) make subclasses of.
+
+## Beware The Pipeline
+
+1. An iota or list of iotas come to be executed. This is the entrypoint to the VM,
+ `CastingVM#queueAndExecuteIotas`, and returns an [`ExecutionClientView`][] (might change later).
+2. Those iotas are put into a [`ContinuationFrame`][] (specifically, [`FrameEvaluate`][]).
+3. While there are still frames, control flows to the top one.
+4. The frame (usually) returns control flow back to the VM by calling `CastingVM#executeInner` with an iota.
+5. `executeInner` first does some pre-checks with intro/retro/consideration (like escaping embedded iotas), but usually
+ makes sure that the passed iota is a pattern and passes *that* on to `executePattern`.
+6. `executePattern` is where the execution actually happens. The pattern is matched to an action or special handler.
+ and executed.
+7. That execution doesn't mutate anything, but returns a [`CastResult`]. It contains:
+ - the rest of the current continuation (read as: the patterns to execute next);
+ - the updated state of the [`CastingImage`];
+ - a list of `OperatorSideEffects`, like withdrawing media, casting spells or mishaping;
+ - misc display info like the color the pattern should be when drawn to the staff GUI and the sound.
+8. Each of the side effects is applied to the world in turn. If any of them make the casting stop (like mishaping),
+ then it does!
+9. If there's still iotas left in the current continuation, then control goes back to step 5, called on the next iota in
+ continuation.
+10. Otherwise, control goes to the top frame of the stack (step 3). And if there are no stack frames, execution is
+ finished! What a ride.
+
+## The Hell's A Continuation And A Continuation Frame
+
+IDFK ask Alwinfy.
+
+But as I understand it, a [continuation frame][ContinuationFrame] tells the VM what to do *next*.
+
+While there are frames left on the stack (not the normal stack, a special execution stack), it is popped and
+queried. It will then operate the VM into executing a [continuation][Continuation] in a certain way.
+
+Continuations are just a linked list of iotas to execute. The VM goes through each one and executes them.
+
+- For staffcasting, each pattern drawn spins up the VM with a `FrameEvaluate`, which will provide exactly one
+ continuation containing the pattern.
+- For trinket casting, the VM gets a `FrameEvaluate` again, but the continuation list it provides has
+ all the patterns in the trinket.
+- Hermes' Gambit pushes a new `FrameEvaluate` to the stack with the pattern list argument inside it. This doesn't clear
+ the continuation underneath, so once those patterns are through execution control goes right back to where it was
+ interrupted.
+- Thoth's and Charon's do something with different kinds of stack frames, I don't really understand it.
+- Iris' gambit is waaaay outside of my pay grade
+
+[CastingVM]: vm/CastingVM.kt
+
+[CastingImage]: vm/CastingImage.kt
+
+[CastingEnvironment]: CastingEnvironment.java
+
+[ExecutionClientView]: ExecutionClientView.kt
+
+[ContinuationFrame]: vm/ContinuationFrame.kt
+
+[Continuation]: vm/SpellContinuation.kt
+
+[FrameEvaluate]: vm/FrameEvaluate.kt
+
+[CastResult]: CastResult.kt
+
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt
index ab1e091d..a2cb880b 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt
@@ -3,7 +3,7 @@ package at.petrak.hexcasting.api.casting.eval.sideeffects
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.RenderedSpell
-import at.petrak.hexcasting.api.casting.eval.CastingHarness
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM
import at.petrak.hexcasting.api.casting.mishaps.Mishap
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.mod.HexStatistics
@@ -18,10 +18,10 @@ import net.minecraft.world.item.ItemStack
*/
sealed class OperatorSideEffect {
/** Return whether to cancel all further [OperatorSideEffect] */
- abstract fun performEffect(harness: CastingHarness): Boolean
+ abstract fun performEffect(harness: CastingVM): Boolean
data class RequiredEnlightenment(val awardStat: Boolean) : OperatorSideEffect() {
- override fun performEffect(harness: CastingHarness): Boolean {
+ override fun performEffect(harness: CastingVM): Boolean {
harness.ctx.caster?.sendSystemMessage("hexcasting.message.cant_great_spell".asTranslatedComponent)
if (awardStat)
@@ -38,7 +38,7 @@ sealed class OperatorSideEffect {
val awardStat: Boolean = true
) :
OperatorSideEffect() {
- override fun performEffect(harness: CastingHarness): Boolean {
+ override fun performEffect(harness: CastingVM): Boolean {
this.spell.cast(harness.ctx)
if (awardStat)
harness.ctx.caster?.awardStat(HexStatistics.SPELLS_CAST)
@@ -48,14 +48,14 @@ sealed class OperatorSideEffect {
}
data class ConsumeMedia(val amount: Int) : OperatorSideEffect() {
- override fun performEffect(harness: CastingHarness): Boolean {
+ override fun performEffect(harness: CastingVM): Boolean {
val leftoverMedia = harness.ctx.extractMedia(this.amount.toLong())
return leftoverMedia > 0
}
}
data class Particles(val spray: ParticleSpray) : OperatorSideEffect() {
- override fun performEffect(harness: CastingHarness): Boolean {
+ override fun performEffect(harness: CastingVM): Boolean {
harness.ctx.produceParticles(this.spray, harness.ctx.colorizer)
this.spray.sprayParticles(harness.ctx.world, harness.getColorizer())
@@ -64,7 +64,7 @@ sealed class OperatorSideEffect {
}
data class DoMishap(val mishap: Mishap, val errorCtx: Mishap.Context) : OperatorSideEffect() {
- override fun performEffect(harness: CastingHarness): Boolean {
+ override fun performEffect(harness: CastingVM): Boolean {
val spray = mishap.particleSpray(harness.ctx)
val color = mishap.accentColor(harness.ctx, errorCtx)
spray.sprayParticles(harness.ctx.world, color)
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
new file mode 100644
index 00000000..46bc57a2
--- /dev/null
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt
@@ -0,0 +1,78 @@
+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.*
+import net.minecraft.nbt.CompoundTag
+import net.minecraft.nbt.Tag
+import net.minecraft.server.level.ServerLevel
+
+/**
+ * The state of a casting VM, containing the stack and all
+ */
+data class CastingImage private constructor(
+ val stack: List,
+
+ val parenCount: Int,
+ val parenthesized: List,
+ val escapeNext: Boolean,
+
+ val userData: CompoundTag
+) {
+ public constructor() : this(listOf(), 0, listOf(), false, CompoundTag())
+
+ fun serializeToNbt() = NBTBuilder {
+ TAG_STACK %= stack.serializeToNBT()
+
+ TAG_PAREN_COUNT %= parenCount
+ TAG_ESCAPE_NEXT %= escapeNext
+ TAG_PARENTHESIZED %= parenthesized.serializeToNBT()
+
+ TAG_USERDATA %= userData
+ }
+
+ companion object {
+ const val TAG_STACK = "stack"
+ const val TAG_PAREN_COUNT = "open_parens"
+ const val TAG_PARENTHESIZED = "parenthesized"
+ const val TAG_ESCAPE_NEXT = "escape_next"
+ const val TAG_USERDATA = "userdata"
+
+ @JvmStatic
+ public fun loadFromNbt(tag: CompoundTag, world: ServerLevel): CastingImage {
+ return try {
+ val stack = mutableListOf()
+ val stackTag = tag.getList(TAG_STACK, Tag.TAG_COMPOUND)
+ for (subtag in stackTag) {
+ val datum = IotaType.deserialize(subtag.asCompound, world)
+ stack.add(datum)
+ }
+
+ val userData = if (tag.contains(TAG_USERDATA)) {
+ tag.getCompound(TAG_USERDATA)
+ } else if (tag.contains("local")) {
+ NBTBuilder {
+ TAG_USERDATA %= tag.getCompound("local")
+ }
+ } else {
+ 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 parenCount = tag.getInt(CastingVM.TAG_PAREN_COUNT)
+ val escapeNext = tag.getBoolean(CastingVM.TAG_ESCAPE_NEXT)
+
+ CastingImage(stack, parenCount, parenthesized, escapeNext, userData)
+ } catch (exn: Exception) {
+ HexAPI.LOGGER.warn("error while loading a CastingImage", exn)
+ CastingImage()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingHarness.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt
similarity index 55%
rename from Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingHarness.kt
rename to Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt
index 5515e575..9c48a034 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingHarness.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt
@@ -1,116 +1,64 @@
-package at.petrak.hexcasting.api.casting.eval
+package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.HexAPI
-import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.PatternShapeMatch
import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
import at.petrak.hexcasting.api.casting.SpellList
-import at.petrak.hexcasting.api.casting.castables.Action
+import at.petrak.hexcasting.api.casting.eval.*
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
-import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate
-import at.petrak.hexcasting.api.casting.eval.vm.FunctionalData
-import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
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.casting.iota.PatternIota
import at.petrak.hexcasting.api.casting.math.HexDir
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.*
-import at.petrak.hexcasting.api.misc.DiscoveryHandlers
-import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.mod.HexTags
import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.common.casting.PatternRegistryManifest
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions
-import com.mojang.datafixers.util.Either
-import net.minecraft.nbt.CompoundTag
-import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
-import net.minecraft.sounds.SoundSource
-import net.minecraft.world.level.gameevent.GameEvent
-import net.minecraft.world.phys.Vec3
/**
- * Keeps track of a player casting a spell on the server.
- * It's stored as NBT on the player.
- *
- * TODO oh god this entire class needs a gigantic refactor. why are there like 6 different entrypoints for casting
- * a pattern. oh god.
+ * The virtual machine! This is the glue that determines the next iteration of a [CastingImage], using a
+ * [CastingEnvironment] to affect the world.
*/
-class CastingHarness private constructor(
- var stack: MutableList,
- var ravenmind: Iota?,
- var parenCount: Int,
- var parenthesized: List,
- var escapeNext: Boolean,
- val ctx: CastingEnvironment,
- val prepackagedColorizer: FrozenColorizer? // for trinkets with colorizers
-) {
-
- @JvmOverloads
- constructor(
- ctx: CastingEnvironment,
- prepackagedColorizer: FrozenColorizer? = null
- ) : this(mutableListOf(), null, 0, mutableListOf(), false, ctx, prepackagedColorizer)
+class CastingVM(val image: CastingImage, val env: CastingEnvironment) {
/**
* Execute a single iota.
*/
- fun executeIota(iota: Iota, world: ServerLevel): ControllerInfo = executeIotas(listOf(iota), world)
-
- private fun displayPatternDebug(escapeNext: Boolean, parenCount: Int, iotaRepresentation: Component) {
- if (this.ctx.debugPatterns) {
- val display = " ".repeat(parenCount).asTextComponent
- if (escapeNext)
- display.append("\\ ".asTextComponent.gold)
- display.append(iotaRepresentation)
-
- this.ctx.caster.sendSystemMessage(display)
- }
- }
+ fun queueAndExecuteIota(iota: Iota, world: ServerLevel): ExecutionClientView = queueAndExecuteIotas(listOf(iota), world)
/**
- * Given a list of iotas, execute them in sequence.
+ * The main entrypoint to the VM. Given a list of iotas, execute them in sequence, and return whatever the client
+ * needs to see.
*/
- fun executeIotas(iotas: List, world: ServerLevel): ControllerInfo {
+ fun queueAndExecuteIotas(iotas: List, world: ServerLevel): ExecutionClientView {
// Initialize the continuation stack to a single top-level eval for all iotas.
var continuation = SpellContinuation.Done.pushFrame(FrameEvaluate(SpellList.LList(0, iotas), false))
// Begin aggregating info
val info = TempControllerInfo(earlyExit = false)
var lastResolutionType = ResolvedPatternType.UNRESOLVED
- var sound = HexEvalSounds.NOTHING
while (continuation is SpellContinuation.NotDone && !info.earlyExit) {
// Take the top of the continuation stack...
val next = continuation.frame
// ...and execute it.
// TODO there used to be error checking code here; I'm pretty sure any and all mishaps should already
// get caught and folded into CastResult by evaluate.
- val result = next.evaluate(continuation.next, world, this)
+ val image2 = next.evaluate(continuation.next, world, this)
// Then write all pertinent data back to the harness for the next iteration.
- if (result.newData != null) {
- this.applyFunctionalData(result.newData)
+ if (image2.newData != null) {
+ this.applyFunctionalData(image2.newData)
}
- continuation = result.continuation
- lastResolutionType = result.resolutionType
- performSideEffects(info, result.sideEffects)
- info.earlyExit = info.earlyExit || !lastResolutionType.success
- sound = if (result.sound == HexEvalSounds.MISHAP) {
- HexEvalSounds.MISHAP
- } else {
- sound.greaterOf(result.sound)
- }
- }
+ this.env.postExecution(image2)
- sound.sound?.let {
- this.ctx.world.playSound(
- null, this.ctx.position.x, this.ctx.position.y, this.ctx.position.z, it,
- SoundSource.PLAYERS, 1f, 1f
- )
- // TODO: is it worth mixing in to the immut map and making our own game event with blackjack and hookers
- this.ctx.world.gameEvent(this.ctx.caster, GameEvent.ITEM_INTERACT_FINISH, this.ctx.position)
+ continuation = image2.continuation
+ lastResolutionType = image2.resolutionType
+ performSideEffects(info, image2.sideEffects)
+ info.earlyExit = info.earlyExit || !lastResolutionType.success
}
if (continuation is SpellContinuation.NotDone) {
@@ -118,29 +66,23 @@ class CastingHarness private constructor(
if (lastResolutionType.success) ResolvedPatternType.EVALUATED else ResolvedPatternType.ERRORED
}
- val (stackDescs, parenDescs, ravenmind) = generateDescs()
+ val (stackDescs, ravenmind) = generateDescs()
- return ControllerInfo(
- this.stack.isEmpty() && this.parenCount == 0 && !this.escapeNext,
- lastResolutionType,
- stackDescs,
- parenDescs,
- ravenmind,
- this.parenCount
- )
+ val isStackClear = image.stack.isEmpty() && image.parenCount == 0 && !image.escapeNext
+ return ExecutionClientView(isStackClear, lastResolutionType, stackDescs, ravenmind)
}
/**
* this DOES NOT THROW THINGS
*/
@Throws()
- fun getUpdate(iota: Iota, world: ServerLevel, continuation: SpellContinuation): CastResult {
+ fun executeInner(iota: Iota, world: ServerLevel, continuation: SpellContinuation): CastResult {
try {
// TODO we can have a special intro/retro sound
// ALSO TODO need to add reader macro-style things
try {
this.handleParentheses(iota)?.let { (data, resolutionType) ->
- return@getUpdate CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
+ return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
}
} catch (e: MishapTooManyCloseParens) {
// This is ridiculous and needs to be fixed
@@ -162,7 +104,7 @@ class CastingHarness private constructor(
}
if (iota is PatternIota) {
- return updateWithPattern(iota.pattern, world, continuation)
+ return executePattern(iota.pattern, world, continuation)
} else {
return CastResult(
continuation,
@@ -202,11 +144,13 @@ class CastingHarness private constructor(
* When the server gets a packet from the client with a new pattern,
* handle it functionally.
*/
- private fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
+ private fun executePattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
var castedName: Component? = null
try {
val lookup = PatternRegistryManifest.matchPattern(newPat, world, false)
- val lookupResult: Either> = if (lookup is Normal || lookup is PerWorld) {
+ this.env.precheckAction(lookup)
+
+ val action = if (lookup is Normal || lookup is PerWorld) {
val key = when (lookup) {
is Normal -> lookup.key
is PerWorld -> lookup.key
@@ -214,63 +158,33 @@ class CastingHarness private constructor(
}
val reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.REQUIRES_ENLIGHTENMENT)
- val canEnlighten = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.CAN_START_ENLIGHTEN)
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment)
- if (!ctx.isCasterEnlightened && reqsEnlightenment) {
- Either.right(listOf(OperatorSideEffect.RequiredEnlightenment(canEnlighten)))
- } else {
- val regiEntry = IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!
- Either.left(regiEntry.action)
- }
+ IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!.action
} else if (lookup is Special) {
castedName = lookup.handler.name
- Either.left(lookup.handler.act())
+ lookup.handler.act()
} else if (lookup is PatternShapeMatch.Nothing) {
throw MishapInvalidPattern()
- } else {
- throw IllegalStateException()
- }
- // TODO: the config denylist should be handled per VM type.
- // I just removed it for now, should re-add it...
+ } else throw IllegalStateException()
val sideEffects = mutableListOf()
var stack2: List? = null
var cont2 = continuation
var ravenmind2: Iota? = null
- if (lookupResult.left().isPresent) {
- val action = lookupResult.left().get()
- displayPatternDebug(false, 0, castedName)
- val result = action.operate(
- continuation,
- this.stack.toMutableList(),
- this.ravenmind,
- this.ctx
- )
- cont2 = result.newContinuation
- stack2 = result.newStack
- ravenmind2 = result.newRavenmind
- // TODO parens also break prescience
- sideEffects.addAll(result.sideEffects)
- } else {
- val problems = lookupResult.right().get()
- sideEffects.addAll(problems)
- }
-
- // Stick a poofy particle effect at the caster position
- // TODO again this should be on the VM lalala
- if (this.ctx.spellCircle == null)
- sideEffects.add(
- OperatorSideEffect.Particles(
- ParticleSpray(
- this.ctx.position,
- Vec3(0.0, 1.0, 0.0),
- 0.5, 1.0
- )
- )
- )
+ val result = action.operate(
+ continuation,
+ this.stack.toMutableList(),
+ this.ravenmind,
+ this.ctx
+ )
+ cont2 = result.newContinuation
+ stack2 = result.newStack
+ ravenmind2 = result.newRavenmind
+ // TODO parens also break prescience
+ sideEffects.addAll(result.sideEffects)
val hereFd = this.getFunctionalData()
val fd = if (stack2 != null) {
@@ -282,25 +196,6 @@ class CastingHarness private constructor(
hereFd
}
- // TODO again this should be per VM
- var soundType = if (this.ctx.source == CastingEnvironment.CastSource.STAFF) {
- HexEvalSounds.OPERATOR
- } else {
- HexEvalSounds.NOTHING
- }
- for (se in sideEffects) {
- if (se is OperatorSideEffect.AttemptSpell) {
- soundType = if (se.hasCastingSound) {
- soundType.greaterOf(HexEvalSounds.SPELL)
- } else {
- // WITH CATLIKE TREAD
- // UPON OUR PREY WE STEAL
- HexEvalSounds.NOTHING
- }
- } else if (se is OperatorSideEffect.DoMishap) {
- soundType = HexEvalSounds.MISHAP
- }
- }
return CastResult(
cont2,
fd,
@@ -333,11 +228,14 @@ class CastingHarness private constructor(
}
}
- fun generateDescs() = Triple(
- stack.map(HexIotaTypes::serialize),
- parenthesized.map(HexIotaTypes::serialize),
- ravenmind?.let(HexIotaTypes::serialize)
- )
+ fun generateDescs(): Pair, Component?> {
+ val stackDescs = this.image.stack.map { it.display() }
+ val ravenmind = if (this.image.userData.contains(HexAPI.RAVENMIND_USERDATA.toString())) {
+ val tag = this.image.userData.getCompound(HexAPI.RAVENMIND_USERDATA.toString())
+ IotaType.getDisplay(tag)
+ } else null
+ return Pair(stackDescs, ravenmind)
+ }
/**
* Return the functional update represented by the current state (for use with `copy`)
@@ -478,92 +376,6 @@ class CastingHarness private constructor(
return out
}
- fun getColorizer(): FrozenColorizer {
- if (this.prepackagedColorizer != null)
- return this.prepackagedColorizer
-
- return IXplatAbstractions.INSTANCE.getColorizer(this.ctx.caster)
- }
-
-
- fun serializeToNBT() = NBTBuilder {
- TAG_STACK %= stack.serializeToNBT()
-
- if (ravenmind != null)
- TAG_LOCAL %= HexIotaTypes.serialize(ravenmind!!)
- TAG_PAREN_COUNT %= parenCount
- TAG_ESCAPE_NEXT %= escapeNext
-
- TAG_PARENTHESIZED %= parenthesized.serializeToNBT()
-
- if (prepackagedColorizer != null)
- TAG_PREPACKAGED_COLORIZER %= prepackagedColorizer.serializeToNBT()
- }
-
-
- companion object {
- const val TAG_STACK = "stack"
- const val TAG_LOCAL = "local"
- const val TAG_PAREN_COUNT = "open_parens"
- const val TAG_PARENTHESIZED = "parenthesized"
- const val TAG_ESCAPE_NEXT = "escape_next"
- const val TAG_PREPACKAGED_COLORIZER = "prepackaged_colorizer"
-
- init {
- DiscoveryHandlers.addMediaHolderDiscoverer {
- it.ctx.caster.inventory.items
- .filter(::isMediaItem)
- .mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
- }
- DiscoveryHandlers.addMediaHolderDiscoverer {
- it.ctx.caster.inventory.armor
- .filter(::isMediaItem)
- .mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
- }
- DiscoveryHandlers.addMediaHolderDiscoverer {
- it.ctx.caster.inventory.offhand
- .filter(::isMediaItem)
- .mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
- }
- }
-
- @JvmStatic
- fun fromNBT(nbt: CompoundTag, ctx: CastingEnvironment): CastingHarness {
- return try {
- val stack = mutableListOf()
- val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
- for (subtag in stackTag) {
- val datum = HexIotaTypes.deserialize(subtag.asCompound, ctx.world)
- stack.add(datum)
- }
-
- val ravenmind = if (nbt.contains(TAG_LOCAL))
- HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
- else
- null
-
- val parenthesized = mutableListOf()
- val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
- for (subtag in parenTag) {
- parenthesized.add(HexIotaTypes.deserialize(subtag.downcast(CompoundTag.TYPE), ctx.world))
- }
-
- val parenCount = nbt.getInt(TAG_PAREN_COUNT)
- val escapeNext = nbt.getBoolean(TAG_ESCAPE_NEXT)
-
- val colorizer = if (nbt.contains(TAG_PREPACKAGED_COLORIZER)) {
- FrozenColorizer.fromNBT(nbt.getCompound(TAG_PREPACKAGED_COLORIZER))
- } else {
- null
- }
-
- CastingHarness(stack, ravenmind, parenCount, parenthesized, escapeNext, ctx, colorizer)
- } catch (exn: Exception) {
- CastingHarness(ctx)
- }
- }
- }
-
data class TempControllerInfo(
var earlyExit: Boolean,
)
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/ContinuationFrame.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/ContinuationFrame.kt
index 24235613..04460430 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/ContinuationFrame.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/ContinuationFrame.kt
@@ -1,7 +1,6 @@
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.SpellList
-import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.getList
@@ -31,7 +30,7 @@ sealed interface ContinuationFrame {
* For Evaluate, this consumes one pattern; for ForEach this queues the next iteration of the outer loop.
* @return the result of this pattern step
*/
- fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult
+ fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingVM): CastResult
/**
* The OpHalt instruction wants us to "jump to" the END of the nearest meta-eval.
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt
index bec172a1..082b2c68 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt
@@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
-import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.NBTBuilder
@@ -23,7 +22,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
override fun evaluate(
continuation: SpellContinuation,
level: ServerLevel,
- harness: CastingHarness
+ harness: CastingVM
): CastResult {
// If there are patterns left...
return if (list.nonEmpty) {
@@ -32,7 +31,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
continuation.pushFrame(FrameEvaluate(list.cdr, this.isMetacasting))
} else continuation
// ...before evaluating the first one in the list.
- val update = harness.getUpdate(list.car, level, newCont)
+ val update = harness.executeInner(list.car, level, newCont)
if (this.isMetacasting && update.sound != HexEvalSounds.MISHAP) {
update.copy(sound = HexEvalSounds.HERMES)
} else {
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameFinishEval.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameFinishEval.kt
index 75c42654..cf20275e 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameFinishEval.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameFinishEval.kt
@@ -1,7 +1,6 @@
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.eval.CastResult
-import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.NBTBuilder
@@ -20,7 +19,7 @@ object FrameFinishEval : ContinuationFrame {
override fun evaluate(
continuation: SpellContinuation,
level: ServerLevel,
- harness: CastingHarness
+ harness: CastingVM
): CastResult {
return CastResult(
continuation,
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt
index 25dc4bb0..6b629ba7 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt
@@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
-import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
@@ -39,7 +38,7 @@ data class FrameForEach(
override fun evaluate(
continuation: SpellContinuation,
level: ServerLevel,
- harness: CastingHarness
+ harness: CastingVM
): CastResult {
// If this isn't the very first Thoth step (i.e. no Thoth computations run yet)...
val stack = if (baseStack == null) {
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java
index 85491970..5d60e74f 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java
@@ -30,7 +30,7 @@ public abstract class Iota {
/**
* Serialize this under the {@code data} tag.
*
- * You probably don't want to call this directly; use {@link HexIotaTypes#serialize}.
+ * You probably don't want to call this directly; use {@link IotaType#serialize}.
*/
abstract public @NotNull Tag serialize();
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/IotaType.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/IotaType.java
index 97cfc9c0..de76dc3e 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/IotaType.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/IotaType.java
@@ -1,15 +1,27 @@
package at.petrak.hexcasting.api.casting.iota;
+import at.petrak.hexcasting.api.HexAPI;
+import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
+import com.mojang.datafixers.util.Pair;
+import net.minecraft.ChatFormatting;
+import net.minecraft.client.gui.Font;
+import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextColor;
+import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
+import net.minecraft.util.FormattedCharSequence;
import javax.annotation.Nullable;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Objects;
// Take notes from ForgeRegistryEntry
public abstract class IotaType {
+
/**
* Spell datums are stored as such: {@code { "type": "modid:type", "datum": a_tag }}.
*
@@ -41,4 +53,149 @@ public abstract class IotaType {
return Component.translatable("hexcasting.iota." + key)
.withStyle(style -> style.withColor(TextColor.fromRgb(color())));
}
+
+ public static CompoundTag serialize(Iota iota) {
+ var type = iota.getType();
+ var typeId = HexIotaTypes.REGISTRY.getKey(type);
+ if (typeId == null) {
+ throw new IllegalStateException(
+ "Tried to serialize an unregistered iota type. Iota: " + iota
+ + " ; Type" + type.getClass().getTypeName());
+ }
+
+ // We check if it's too big on serialization; if it is we just return a garbage.
+ if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
+ // Garbage will never be too large so we just recurse
+ return serialize(new GarbageIota());
+ }
+ var dataTag = iota.serialize();
+ var out = new CompoundTag();
+ out.putString(HexIotaTypes.KEY_TYPE, typeId.toString());
+ out.put(HexIotaTypes.KEY_DATA, dataTag);
+ return out;
+ }
+
+ public static boolean isTooLargeToSerialize(Iterable examinee) {
+ // We don't recurse here, just a work queue (or work stack, if we liked.)
+ // Each element is a found sub-iota, and how deep it is.
+ //
+ // TODO: is it worth trying to cache the depth and size statically on a SpellList.
+ var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
+ int totalEltsFound = 1; // count the first list
+ while (!listsToExamine.isEmpty()) {
+ var iotaPair = listsToExamine.removeFirst();
+ var sublist = iotaPair.getFirst();
+ int depth = iotaPair.getSecond();
+ for (var iota : sublist) {
+ totalEltsFound++;
+ if (totalEltsFound >= HexIotaTypes.MAX_SERIALIZATION_TOTAL) {
+ return true; // too bad
+ }
+ if (iota instanceof ListIota subsublist) {
+ if (depth + 1 >= HexIotaTypes.MAX_SERIALIZATION_DEPTH) {
+ return true;
+ }
+ listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
+ }
+ }
+ }
+ // we made it!
+ return false;
+ }
+
+ /**
+ * This method attempts to find the type from the {@code type} key.
+ * See {@link HexIotaTypes#serialize(Iota)} for the storage format.
+ *
+ * @return {@code null} if it cannot get the type.
+ */
+ @org.jetbrains.annotations.Nullable
+ public static IotaType> getTypeFromTag(CompoundTag tag) {
+ if (!tag.contains(HexIotaTypes.KEY_TYPE, Tag.TAG_STRING)) {
+ return null;
+ }
+ var typeKey = tag.getString(HexIotaTypes.KEY_TYPE);
+ if (!ResourceLocation.isValidResourceLocation(typeKey)) {
+ return null;
+ }
+ var typeLoc = new ResourceLocation(typeKey);
+ return HexIotaTypes.REGISTRY.get(typeLoc);
+ }
+
+ /**
+ * Attempt to deserialize an iota from a tag.
+ *
+ * Iotas are saved as such:
+ *
+ * {
+ * "type": "hexcasting:atype",
+ * "data": {...}
+ * }
+ *
+ */
+ public static Iota deserialize(CompoundTag tag, ServerLevel world) {
+ var type = getTypeFromTag(tag);
+ if (type == null) {
+ return new GarbageIota();
+ }
+ var data = tag.get(HexIotaTypes.KEY_DATA);
+ if (data == null) {
+ return new GarbageIota();
+ }
+ Iota deserialized;
+ try {
+ deserialized = Objects.requireNonNullElse(type.deserialize(data, world), new NullIota());
+ } catch (IllegalArgumentException exn) {
+ HexAPI.LOGGER.warn("Caught an exception deserializing an iota", exn);
+ deserialized = new GarbageIota();
+ }
+ return deserialized;
+ }
+
+ private static Component brokenIota() {
+ return Component.translatable("hexcasting.spelldata.unknown")
+ .withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC);
+ }
+
+ public static Component getDisplay(CompoundTag tag) {
+ var type = getTypeFromTag(tag);
+ if (type == null) {
+ return brokenIota();
+ }
+ var data = tag.get(HexIotaTypes.KEY_DATA);
+ if (data == null) {
+ return brokenIota();
+ }
+ return type.display(data);
+ }
+
+ public static FormattedCharSequence getDisplayWithMaxWidth(CompoundTag tag, int maxWidth, Font font) {
+ var type = getTypeFromTag(tag);
+ if (type == null) {
+ return brokenIota().getVisualOrderText();
+ }
+ var data = tag.get(HexIotaTypes.KEY_DATA);
+ if (data == null) {
+ return brokenIota().getVisualOrderText();
+ }
+ var display = type.display(data);
+ var splitted = font.split(display, maxWidth - font.width("..."));
+ if (splitted.isEmpty())
+ return FormattedCharSequence.EMPTY;
+ else if (splitted.size() == 1)
+ return splitted.get(0);
+ else {
+ var first = splitted.get(0);
+ return FormattedCharSequence.fromPair(first,
+ Component.literal("...").withStyle(ChatFormatting.GRAY).getVisualOrderText());
+ }
+ }
+
+ public static int getColor(CompoundTag tag) {
+ var type = getTypeFromTag(tag);
+ if (type == null) {
+ return HexUtils.ERROR_COLOR;
+ }
+ return type.color();
+ }
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java
index ca4727c7..355c2643 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/ListIota.java
@@ -68,7 +68,7 @@ public class ListIota extends Iota {
public @NotNull Tag serialize() {
var out = new ListTag();
for (var subdatum : this.getList()) {
- out.add(HexIotaTypes.serialize(subdatum));
+ out.add(IotaType.serialize(subdatum));
}
return out;
}
@@ -82,7 +82,7 @@ public class ListIota extends Iota {
for (var sub : listTag) {
var csub = HexUtils.downcast(sub, CompoundTag.TYPE);
- var subiota = HexIotaTypes.deserialize(csub, world);
+ var subiota = IotaType.deserialize(csub, world);
if (subiota == null) {
return null;
}
@@ -100,7 +100,7 @@ public class ListIota extends Iota {
Tag sub = list.get(i);
var csub = HexUtils.downcast(sub, CompoundTag.TYPE);
- out.append(HexIotaTypes.getDisplay(csub));
+ out.append(IotaType.getDisplay(csub));
if (i < list.size() - 1) {
out.append(", ");
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java b/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
index d0ae62a8..46f4074f 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/item/IotaHolderItem.java
@@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.ClientTickCounter;
@@ -45,7 +46,7 @@ public interface IotaHolderItem {
var tag = dh.readIotaTag(stack);
if (tag != null) {
- return HexIotaTypes.deserialize(tag, world);
+ return IotaType.deserialize(tag, world);
} else {
return null;
}
@@ -83,7 +84,7 @@ public interface IotaHolderItem {
return HexUtils.ERROR_COLOR;
}
- return HexIotaTypes.getColor(tag);
+ return IotaType.getColor(tag);
}
/**
@@ -100,7 +101,7 @@ public interface IotaHolderItem {
TooltipFlag flag) {
var datumTag = self.readIotaTag(stack);
if (datumTag != null) {
- var cmp = HexIotaTypes.getDisplay(datumTag);
+ var cmp = IotaType.getDisplay(datumTag);
components.add(Component.translatable("hexcasting.spelldata.onitem", cmp));
if (flag.isAdvanced()) {
diff --git a/Common/src/main/java/at/petrak/hexcasting/api/misc/DiscoveryHandlers.java b/Common/src/main/java/at/petrak/hexcasting/api/misc/DiscoveryHandlers.java
index a4dc4400..f6299137 100644
--- a/Common/src/main/java/at/petrak/hexcasting/api/misc/DiscoveryHandlers.java
+++ b/Common/src/main/java/at/petrak/hexcasting/api/misc/DiscoveryHandlers.java
@@ -2,7 +2,7 @@ package at.petrak.hexcasting.api.misc;
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import com.google.common.collect.Lists;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@@ -15,10 +15,11 @@ import java.util.function.Predicate;
public class DiscoveryHandlers {
private static final List> HAS_LENS_PREDICATE = new ArrayList<>();
- private static final List>> MEDIA_HOLDER_DISCOVERY = new ArrayList<>();
+ private static final List>> MEDIA_HOLDER_DISCOVERY = new ArrayList<>();
private static final List> GRID_SCALE_MODIFIERS = new ArrayList<>();
private static final List>> ITEM_SLOT_DISCOVERER = new ArrayList<>();
- private static final List>> OPERATIVE_SLOT_DISCOVERER = new ArrayList<>();
+ private static final List>> OPERATIVE_SLOT_DISCOVERER =
+ new ArrayList<>();
private static final List> DEBUG_DISCOVERER = new ArrayList<>();
public static boolean hasLens(Player player) {
@@ -30,7 +31,7 @@ public class DiscoveryHandlers {
return false;
}
- public static List collectMediaHolders(CastingHarness harness) {
+ public static List collectMediaHolders(CastingVM harness) {
List holders = Lists.newArrayList();
for (var discoverer : MEDIA_HOLDER_DISCOVERY) {
holders.addAll(discoverer.apply(harness));
@@ -76,7 +77,7 @@ public class DiscoveryHandlers {
HAS_LENS_PREDICATE.add(predicate);
}
- public static void addMediaHolderDiscoverer(Function> discoverer) {
+ public static void addMediaHolderDiscoverer(Function> discoverer) {
MEDIA_HOLDER_DISCOVERY.add(discoverer);
}
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 c0c9e041..b8abdc0d 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
@@ -3,9 +3,9 @@
package at.petrak.hexcasting.api.utils
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.casting.math.HexCoord
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import net.minecraft.ChatFormatting
import net.minecraft.core.Registry
import net.minecraft.nbt.*
@@ -251,7 +251,7 @@ inline operator fun WeakValue.setValue(thisRef: Any?, property: KProperty
* Returns an empty list if it's too complicated.
*/
fun Iterable.serializeToNBT() =
- if (HexIotaTypes.isTooLargeToSerialize(this))
+ if (IotaType.isTooLargeToSerialize(this))
ListTag()
else
ListIota(this.toList()).serialize()
diff --git a/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java b/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
index 991d16b9..4846e857 100644
--- a/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
+++ b/Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java
@@ -2,6 +2,7 @@ package at.petrak.hexcasting.client;
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.item.MediaHolderItem;
@@ -19,7 +20,6 @@ import at.petrak.hexcasting.common.items.magic.ItemPackagedHex;
import at.petrak.hexcasting.common.lib.HexBlockEntities;
import at.petrak.hexcasting.common.lib.HexBlocks;
import at.petrak.hexcasting.common.lib.HexItems;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.xplat.IClientXplatAbstractions;
import com.mojang.datafixers.util.Pair;
import net.minecraft.ChatFormatting;
@@ -138,7 +138,7 @@ public class RegisterClientStuff {
if (iotaTag == null) {
return 0xff_ffffff;
}
- return HexIotaTypes.getColor(iotaTag);
+ return IotaType.getColor(iotaTag);
}, HexBlocks.AKASHIC_BOOKSHELF);
}
@@ -192,7 +192,7 @@ public class RegisterClientStuff {
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile) {
var iotaTag = tile.getIotaTag();
if (iotaTag != null) {
- var display = HexIotaTypes.getDisplay(iotaTag);
+ var display = IotaType.getDisplay(iotaTag);
lines.add(new Pair<>(new ItemStack(Items.BOOK), display));
}
}
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 c4a934bd..fdc98611 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
@@ -1,21 +1,21 @@
package at.petrak.hexcasting.client.gui
-import at.petrak.hexcasting.api.misc.DiscoveryHandlers
-import at.petrak.hexcasting.api.mod.HexConfig
-import at.petrak.hexcasting.api.mod.HexTags
-import at.petrak.hexcasting.api.casting.eval.ControllerInfo
+import at.petrak.hexcasting.api.casting.eval.ExecutionClientView
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
+import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.casting.math.HexAngle
import at.petrak.hexcasting.api.casting.math.HexCoord
import at.petrak.hexcasting.api.casting.math.HexDir
import at.petrak.hexcasting.api.casting.math.HexPattern
+import at.petrak.hexcasting.api.misc.DiscoveryHandlers
+import at.petrak.hexcasting.api.mod.HexConfig
+import at.petrak.hexcasting.api.mod.HexTags
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import at.petrak.hexcasting.client.*
import at.petrak.hexcasting.client.ktxt.accumulatedScroll
import at.petrak.hexcasting.client.sound.GridSoundInstance
import at.petrak.hexcasting.common.lib.HexSounds
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import at.petrak.hexcasting.common.network.MsgNewSpellPatternSyn
import at.petrak.hexcasting.xplat.IClientXplatAbstractions
import com.mojang.blaze3d.systems.RenderSystem
@@ -59,7 +59,7 @@ class GuiSpellcasting constructor(
this.calculateIotaDisplays()
}
- fun recvServerUpdate(info: ControllerInfo, index: Int) {
+ fun recvServerUpdate(info: ExecutionClientView, index: Int) {
this.patterns.getOrNull(index)?.let {
it.type = info.resolutionType
}
@@ -75,7 +75,7 @@ class GuiSpellcasting constructor(
val mc = Minecraft.getInstance()
val width = (this.width * LHS_IOTAS_ALLOCATION).toInt()
this.stackDescs =
- this.cachedStack.map { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font) }
+ this.cachedStack.map { IotaType.getDisplayWithMaxWidth(it, width, mc.font) }
.asReversed()
// this.parenDescs = if (this.cachedParens.isNotEmpty())
// this.cachedParens.flatMap { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font) }
@@ -86,7 +86,7 @@ class GuiSpellcasting constructor(
this.parenDescs = emptyList()
this.ravenmind =
this.cachedRavenmind?.let {
- HexIotaTypes.getDisplayWithMaxWidth(
+ IotaType.getDisplayWithMaxWidth(
it,
(this.width * RHS_IOTAS_ALLOCATION).toInt(),
mc.font
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java
index 119ad21c..db3f96ac 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java
@@ -1,8 +1,8 @@
package at.petrak.hexcasting.common.blocks.akashic;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.math.HexPattern;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
@@ -57,7 +57,7 @@ public class BlockAkashicRecord extends Block {
var tile = (BlockEntityAkashicBookshelf) slevel.getBlockEntity(foundPos);
var tag = tile.getIotaTag();
- return tag == null ? null : HexIotaTypes.deserialize(tag, slevel);
+ return tag == null ? null : IotaType.deserialize(tag, slevel);
}
// TODO get comparators working again and also cache the number of iotas somehow?
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java
index d851731b..7fe9945c 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java
@@ -2,9 +2,9 @@ package at.petrak.hexcasting.common.blocks.akashic;
import at.petrak.hexcasting.api.block.HexBlockEntity;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.HexBlockEntities;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
@@ -38,7 +38,7 @@ public class BlockEntityAkashicBookshelf extends HexBlockEntity {
public void setNewMapping(HexPattern pattern, Iota iota) {
var previouslyEmpty = this.pattern == null;
this.pattern = pattern;
- this.iotaTag = HexIotaTypes.serialize(iota);
+ this.iotaTag = IotaType.serialize(iota);
if (previouslyEmpty) {
var oldBs = this.getBlockState();
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/env/StaffCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/env/StaffCastEnv.java
index 6066ceb1..bc225763 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/casting/env/StaffCastEnv.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/env/StaffCastEnv.java
@@ -2,7 +2,7 @@ package at.petrak.hexcasting.common.casting.env;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.casting.eval.CastResult;
-import at.petrak.hexcasting.api.casting.eval.ControllerInfo;
+import at.petrak.hexcasting.api.casting.eval.ExecutionClientView;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
@@ -74,13 +74,13 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed());
- ControllerInfo clientInfo;
+ ExecutionClientView clientInfo;
if (autoFail) {
var descs = harness.generateDescs();
- clientInfo = new ControllerInfo(harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
+ clientInfo = new ExecutionClientView(harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
descs.getFirst(), descs.getSecond(), descs.getThird(), harness.getParenCount());
} else {
- clientInfo = harness.executeIota(new PatternIota(msg.pattern()), sender.getLevel());
+ clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
}
if (clientInfo.isStackClear()) {
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemAbacus.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemAbacus.java
index 8c933e68..1cdccbb1 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemAbacus.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemAbacus.java
@@ -1,10 +1,10 @@
package at.petrak.hexcasting.common.items;
-import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
+import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.utils.NBTHelper;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.common.lib.HexSounds;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
@@ -30,7 +30,7 @@ public class ItemAbacus extends Item implements IotaHolderItem {
public @Nullable
CompoundTag readIotaTag(ItemStack stack) {
var datum = new DoubleIota(NBTHelper.getDouble(stack, TAG_VALUE));
- return HexIotaTypes.serialize(datum);
+ return IotaType.serialize(datum);
}
@Override
@@ -66,7 +66,7 @@ public class ItemAbacus extends Item implements IotaHolderItem {
@Override
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List pTooltipComponents,
- TooltipFlag pIsAdvanced) {
+ TooltipFlag pIsAdvanced) {
IotaHolderItem.appendHoverText(this, pStack, pTooltipComponents, pIsAdvanced);
}
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
index 48ea9b5e..92e66c73 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemFocus.java
@@ -1,10 +1,10 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.iota.NullIota;
import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.utils.NBTHelper;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@@ -57,7 +57,7 @@ public class ItemFocus extends Item implements IotaHolderItem {
stack.removeTagKey(TAG_DATA);
stack.removeTagKey(TAG_SEALED);
} else if (!isSealed(stack)) {
- NBTHelper.put(stack, TAG_DATA, HexIotaTypes.serialize(datum));
+ NBTHelper.put(stack, TAG_DATA, IotaType.serialize(datum));
}
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
index f05bcc4b..3a91c4f6 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/ItemSpellbook.java
@@ -1,10 +1,10 @@
package at.petrak.hexcasting.common.items;
-import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.iota.NullIota;
+import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.utils.NBTHelper;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
@@ -42,7 +42,7 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
@Override
public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip,
- TooltipFlag isAdvanced) {
+ TooltipFlag isAdvanced) {
boolean sealed = isSealed(stack);
boolean empty = false;
if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
@@ -148,14 +148,14 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
pages.remove(key);
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
} else {
- pages.put(key, HexIotaTypes.serialize(datum));
+ pages.put(key, IotaType.serialize(datum));
}
if (pages.isEmpty()) {
NBTHelper.remove(stack, TAG_PAGES);
}
} else if (datum != null) {
- NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, HexIotaTypes.serialize(datum));
+ NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, IotaType.serialize(datum));
} else {
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemPackagedHex.java b/Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemPackagedHex.java
index 50d463c0..05f35557 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemPackagedHex.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemPackagedHex.java
@@ -1,11 +1,11 @@
package at.petrak.hexcasting.common.items.magic;
-import at.petrak.hexcasting.api.item.HexHolderItem;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.Iota;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
+import at.petrak.hexcasting.api.item.HexHolderItem;
import at.petrak.hexcasting.api.utils.NBTHelper;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
@@ -66,7 +66,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
var out = new ArrayList();
for (var patTag : patsTag) {
CompoundTag tag = NBTHelper.getAsCompound(patTag);
- out.add(HexIotaTypes.deserialize(tag, level));
+ out.add(IotaType.deserialize(tag, level));
}
return out;
}
@@ -75,7 +75,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
public void writeHex(ItemStack stack, List program, int media) {
ListTag patsTag = new ListTag();
for (Iota pat : program) {
- patsTag.add(HexIotaTypes.serialize(pat));
+ patsTag.add(IotaType.serialize(pat));
}
NBTHelper.putList(stack, TAG_PROGRAM, patsTag);
@@ -107,8 +107,8 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
}
var sPlayer = (ServerPlayer) player;
var ctx = new CastingEnvironment(sPlayer, usedHand, CastingEnvironment.CastSource.PACKAGED_HEX);
- var harness = new CastingHarness(ctx);
- var info = harness.executeIotas(instrs, sPlayer.getLevel());
+ var harness = new CastingVM(ctx);
+ var info = harness.queueAndExecuteIotas(instrs, sPlayer.getLevel());
boolean broken = breakAfterDepletion() && getMedia(stack) == 0;
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexIotaTypes.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexIotaTypes.java
index 8d5def2d..96674128 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexIotaTypes.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexIotaTypes.java
@@ -2,22 +2,13 @@ package at.petrak.hexcasting.common.lib.hex;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.casting.iota.*;
-import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
-import com.mojang.datafixers.util.Pair;
-import net.minecraft.ChatFormatting;
-import net.minecraft.client.gui.Font;
import net.minecraft.core.Registry;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.Tag;
-import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.util.FormattedCharSequence;
-import org.jetbrains.annotations.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
-import java.util.*;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.function.BiConsumer;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
@@ -34,151 +25,6 @@ public class HexIotaTypes {
public static final int MAX_SERIALIZATION_DEPTH = 256;
public static final int MAX_SERIALIZATION_TOTAL = 1024;
- public static CompoundTag serialize(Iota iota) {
- var type = iota.getType();
- var typeId = REGISTRY.getKey(type);
- if (typeId == null) {
- throw new IllegalStateException(
- "Tried to serialize an unregistered iota type. Iota: " + iota
- + " ; Type" + type.getClass().getTypeName());
- }
-
- // We check if it's too big on serialization; if it is we just return a garbage.
- if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
- // Garbage will never be too large so we just recurse
- return serialize(new GarbageIota());
- }
- var dataTag = iota.serialize();
- var out = new CompoundTag();
- out.putString(KEY_TYPE, typeId.toString());
- out.put(KEY_DATA, dataTag);
- return out;
- }
-
- public static boolean isTooLargeToSerialize(Iterable examinee) {
- // We don't recurse here, just a work queue (or work stack, if we liked.)
- // Each element is a found sub-iota, and how deep it is.
- //
- // TODO: is it worth trying to cache the depth and size statically on a SpellList.
- var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
- int totalEltsFound = 1; // count the first list
- while (!listsToExamine.isEmpty()) {
- var iotaPair = listsToExamine.removeFirst();
- var sublist = iotaPair.getFirst();
- int depth = iotaPair.getSecond();
- for (var iota : sublist) {
- totalEltsFound++;
- if (totalEltsFound >= MAX_SERIALIZATION_TOTAL) {
- return true; // too bad
- }
- if (iota instanceof ListIota subsublist) {
- if (depth + 1 >= MAX_SERIALIZATION_DEPTH) {
- return true;
- }
- listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
- }
- }
- }
- // we made it!
- return false;
- }
-
- /**
- * This method attempts to find the type from the {@code type} key.
- * See {@link HexIotaTypes#serialize(Iota)} for the storage format.
- *
- * @return {@code null} if it cannot get the type.
- */
- @Nullable
- public static IotaType> getTypeFromTag(CompoundTag tag) {
- if (!tag.contains(KEY_TYPE, Tag.TAG_STRING)) {
- return null;
- }
- var typeKey = tag.getString(KEY_TYPE);
- if (!ResourceLocation.isValidResourceLocation(typeKey)) {
- return null;
- }
- var typeLoc = new ResourceLocation(typeKey);
- return REGISTRY.get(typeLoc);
- }
-
- /**
- * Attempt to deserialize an iota from a tag.
- *
- * Iotas are saved as such:
- *
- * {
- * "type": "hexcasting:atype",
- * "data": {...}
- * }
- *
- */
- public static Iota deserialize(CompoundTag tag, ServerLevel world) {
- var type = getTypeFromTag(tag);
- if (type == null) {
- return new GarbageIota();
- }
- var data = tag.get(KEY_DATA);
- if (data == null) {
- return new GarbageIota();
- }
- Iota deserialized;
- try {
- deserialized = Objects.requireNonNullElse(type.deserialize(data, world), new NullIota());
- } catch (IllegalArgumentException exn) {
- HexAPI.LOGGER.warn("Caught an exception deserializing an iota", exn);
- deserialized = new GarbageIota();
- }
- return deserialized;
- }
-
- private static Component brokenIota() {
- return Component.translatable("hexcasting.spelldata.unknown")
- .withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC);
- }
-
- public static Component getDisplay(CompoundTag tag) {
- var type = getTypeFromTag(tag);
- if (type == null) {
- return brokenIota();
- }
- var data = tag.get(KEY_DATA);
- if (data == null) {
- return brokenIota();
- }
- return type.display(data);
- }
-
- public static FormattedCharSequence getDisplayWithMaxWidth(CompoundTag tag, int maxWidth, Font font) {
- var type = getTypeFromTag(tag);
- if (type == null) {
- return brokenIota().getVisualOrderText();
- }
- var data = tag.get(KEY_DATA);
- if (data == null) {
- return brokenIota().getVisualOrderText();
- }
- var display = type.display(data);
- var splitted = font.split(display, maxWidth - font.width("..."));
- if (splitted.isEmpty())
- return FormattedCharSequence.EMPTY;
- else if (splitted.size() == 1)
- return splitted.get(0);
- else {
- var first = splitted.get(0);
- return FormattedCharSequence.fromPair(first,
- Component.literal("...").withStyle(ChatFormatting.GRAY).getVisualOrderText());
- }
- }
-
- public static int getColor(CompoundTag tag) {
- var type = getTypeFromTag(tag);
- if (type == null) {
- return HexUtils.ERROR_COLOR;
- }
- return type.color();
- }
-
public static void registerTypes(BiConsumer, ResourceLocation> r) {
for (var e : TYPES.entrySet()) {
r.accept(e.getValue(), e.getKey());
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgNewSpellPatternAck.java b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgNewSpellPatternAck.java
index 125bec2a..88a0218d 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgNewSpellPatternAck.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgNewSpellPatternAck.java
@@ -1,6 +1,6 @@
package at.petrak.hexcasting.common.network;
-import at.petrak.hexcasting.api.casting.eval.ControllerInfo;
+import at.petrak.hexcasting.api.casting.eval.ExecutionClientView;
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
import at.petrak.hexcasting.common.lib.HexSounds;
@@ -16,7 +16,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
/**
* Sent server->client when the player finishes casting a spell.
*/
-public record MsgNewSpellPatternAck(ControllerInfo info, int index) implements IMessage {
+public record MsgNewSpellPatternAck(ExecutionClientView info, int index) implements IMessage {
public static final ResourceLocation ID = modLoc("pat_sc");
@Override
@@ -38,7 +38,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info, int index) implements I
var parenCount = buf.readVarInt();
return new MsgNewSpellPatternAck(
- new ControllerInfo(isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
+ new ExecutionClientView(isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
);
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
index 985491af..0dce6f86 100644
--- a/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
+++ b/Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java
@@ -1,9 +1,9 @@
package at.petrak.hexcasting.common.network;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.common.items.ItemAbacus;
import at.petrak.hexcasting.common.items.ItemSpellbook;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.common.lib.HexItems;
import at.petrak.hexcasting.common.lib.HexSounds;
import io.netty.buffer.ByteBuf;
@@ -144,7 +144,7 @@ public record MsgShiftScrollSyn(double mainHandDelta, double offHandDelta, boole
var datumTag = HexItems.ABACUS.readIotaTag(stack);
if (datumTag != null) {
- var popup = HexIotaTypes.getDisplay(datumTag);
+ var popup = IotaType.getDisplay(datumTag);
sender.displayClientMessage(
Component.translatable("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
}
diff --git a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java
index 98070e28..ec5bac82 100644
--- a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java
+++ b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java
@@ -6,9 +6,9 @@ import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.player.FlightAbility;
@@ -79,7 +79,7 @@ public interface IXplatAbstractions {
void setFlight(ServerPlayer target, FlightAbility flight);
- void setHarness(ServerPlayer target, @Nullable CastingHarness harness);
+ void setHarness(ServerPlayer target, @Nullable CastingVM harness);
void setPatterns(ServerPlayer target, List patterns);
@@ -91,7 +91,7 @@ public interface IXplatAbstractions {
Sentinel getSentinel(Player player);
- CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand);
+ CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand);
List getPatternsSavedInUi(ServerPlayer player);
diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCHarness.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCHarness.java
index 90603d06..237a700a 100644
--- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCHarness.java
+++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCHarness.java
@@ -1,7 +1,7 @@
package at.petrak.hexcasting.fabric.cc;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import dev.onyxstudios.cca.api.v3.component.Component;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
@@ -18,16 +18,16 @@ public class CCHarness implements Component {
this.owner = owner;
}
- public CastingHarness getHarness(InteractionHand hand) {
+ public CastingVM getHarness(InteractionHand hand) {
var ctx = new CastingEnvironment(this.owner, hand, CastingEnvironment.CastSource.STAFF);
if (this.lazyLoadedTag.isEmpty()) {
- return new CastingHarness(ctx);
+ return new CastingVM(ctx);
} else {
- return CastingHarness.fromNBT(this.lazyLoadedTag, ctx);
+ return CastingVM.fromNBT(this.lazyLoadedTag, ctx);
}
}
- public void setHarness(@Nullable CastingHarness harness) {
+ public void setHarness(@Nullable CastingVM harness) {
this.lazyLoadedTag = harness == null ? new CompoundTag() : harness.serializeToNBT();
}
diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/adimpl/CCItemIotaHolder.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/adimpl/CCItemIotaHolder.java
index 5f879164..c0f8993f 100644
--- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/adimpl/CCItemIotaHolder.java
+++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/adimpl/CCItemIotaHolder.java
@@ -1,8 +1,8 @@
package at.petrak.hexcasting.fabric.cc.adimpl;
-import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.casting.iota.Iota;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
+import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.fabric.cc.HexCardinalComponents;
import dev.onyxstudios.cca.api.v3.item.ItemComponent;
import net.minecraft.nbt.CompoundTag;
@@ -56,7 +56,7 @@ public abstract class CCItemIotaHolder extends ItemComponent implements CCIotaHo
@Override
public @Nullable CompoundTag readIotaTag() {
var iota = this.provider.apply(this.stack);
- return iota == null ? null : HexIotaTypes.serialize(iota);
+ return iota == null ? null : IotaType.serialize(iota);
}
@Override
diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java
index 560d153a..0499eae6 100644
--- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java
+++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java
@@ -6,9 +6,9 @@ import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexConfig;
@@ -165,7 +165,7 @@ public class FabricXplatImpl implements IXplatAbstractions {
}
@Override
- public void setHarness(ServerPlayer target, CastingHarness harness) {
+ public void setHarness(ServerPlayer target, CastingVM harness) {
var cc = HexCardinalComponents.HARNESS.get(target);
cc.setHarness(harness);
}
@@ -201,7 +201,7 @@ public class FabricXplatImpl implements IXplatAbstractions {
}
@Override
- public CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand) {
+ public CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand) {
var cc = HexCardinalComponents.HARNESS.get(player);
return cc.getHarness(hand);
}
diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/cap/adimpl/CapStaticIotaHolder.java b/Forge/src/main/java/at/petrak/hexcasting/forge/cap/adimpl/CapStaticIotaHolder.java
index d55edcfb..1eb438a1 100644
--- a/Forge/src/main/java/at/petrak/hexcasting/forge/cap/adimpl/CapStaticIotaHolder.java
+++ b/Forge/src/main/java/at/petrak/hexcasting/forge/cap/adimpl/CapStaticIotaHolder.java
@@ -2,7 +2,7 @@ package at.petrak.hexcasting.forge.cap.adimpl;
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.casting.iota.Iota;
-import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
+import at.petrak.hexcasting.api.casting.iota.IotaType;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
@@ -17,7 +17,7 @@ public record CapStaticIotaHolder(Function provider,
public @Nullable
CompoundTag readIotaTag() {
var iota = provider.apply(stack);
- return iota == null ? null : HexIotaTypes.serialize(iota);
+ return iota == null ? null : IotaType.serialize(iota);
}
@Override
diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java b/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java
index 87571e60..cdd2ba5f 100644
--- a/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java
+++ b/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java
@@ -8,9 +8,9 @@ import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
-import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
+import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexTags;
@@ -183,7 +183,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
}
@Override
- public void setHarness(ServerPlayer player, CastingHarness harness) {
+ public void setHarness(ServerPlayer player, CastingVM harness) {
player.getPersistentData().put(TAG_HARNESS, harness == null ? new CompoundTag() : harness.serializeToNBT());
}
@@ -237,10 +237,10 @@ public class ForgeXplatImpl implements IXplatAbstractions {
}
@Override
- public CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand) {
+ public CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand) {
// This is always from a staff because we don't need to load the harness when casting from item
var ctx = new CastingEnvironment(player, hand, CastingEnvironment.CastSource.STAFF);
- return CastingHarness.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
+ return CastingVM.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
}
@Override