Skip to content

Commit 6cf743f

Browse files
committed
Construct casting vm for each step to fix broken evaluator spell casting
1 parent dce3099 commit 6cf743f

File tree

8 files changed

+93
-42
lines changed

8 files changed

+93
-42
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Pydantic's HISTORY.md](https://github.com/pydantic/pydantic/blob/main/HISTORY.md), and this project *mostly* adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [UNRELEASED]
8+
9+
### Fixed
10+
11+
- Fixed a bug where the Evaluator was unable to cast any spells requiring media.
12+
713
## 0.2.0+1.20.1
814

915
### Added

Common/src/main/kotlin/gay/object/hexdebug/adapter/DebugAdapter.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gay.`object`.hexdebug.adapter
22

33
import at.petrak.hexcasting.api.casting.SpellList
4+
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
45
import at.petrak.hexcasting.api.casting.eval.ExecutionClientView
56
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
67
import at.petrak.hexcasting.api.casting.iota.Iota
@@ -107,12 +108,12 @@ open class DebugAdapter(val player: ServerPlayer) : IDebugProtocolServer {
107108
})
108109
}
109110

110-
fun evaluate(pattern: HexPattern) = evaluate(PatternIota(pattern))
111+
fun evaluate(env: CastingEnvironment, pattern: HexPattern) = evaluate(env, PatternIota(pattern))
111112

112-
fun evaluate(iota: Iota) = evaluate(SpellList.LList(listOf(iota)))
113+
fun evaluate(env: CastingEnvironment, iota: Iota) = evaluate(env, SpellList.LList(listOf(iota)))
113114

114-
fun evaluate(list: SpellList) = debugger?.let {
115-
val result = it.evaluate(list)
115+
fun evaluate(env: CastingEnvironment, list: SpellList) = debugger?.let {
116+
val result = it.evaluate(env, list)
116117
if (result.startedEvaluating) {
117118
setEvaluatorState(ItemEvaluator.EvalState.MODIFIED)
118119
}

Common/src/main/kotlin/gay/object/hexdebug/casting/eval/DebugItemCastEnv.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import gay.`object`.hexdebug.debugger.DebugStepType
77
import net.minecraft.network.chat.Component
88
import net.minecraft.server.level.ServerPlayer
99
import net.minecraft.world.InteractionHand
10-
import org.eclipse.lsp4j.debug.OutputEventArgumentsCategory
1110

1211
class DebugItemCastEnv(
1312
caster: ServerPlayer,
@@ -23,8 +22,6 @@ class DebugItemCastEnv(
2322

2423
override fun sendMishapMsgToPlayer(mishap: OperatorSideEffect.DoMishap) {
2524
super.sendMishapMsgToPlayer(mishap)
26-
mishap.mishap.errorMessageWithName(this, mishap.errorCtx)?.also {
27-
printDebugMessage(caster, it, OutputEventArgumentsCategory.STDERR)
28-
}
25+
printDebugMishap(this, caster, mishap)
2926
}
3027
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package gay.`object`.hexdebug.casting.eval
2+
3+
import at.petrak.hexcasting.api.casting.castables.Action
4+
import at.petrak.hexcasting.api.casting.eval.env.StaffCastEnv
5+
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
6+
import gay.`object`.hexdebug.debugger.DebugStepType
7+
import net.minecraft.network.chat.Component
8+
import net.minecraft.server.level.ServerPlayer
9+
import net.minecraft.world.InteractionHand
10+
11+
class DebugStaffCastEnv(
12+
caster: ServerPlayer,
13+
castingHand: InteractionHand,
14+
) : StaffCastEnv(caster, castingHand), IDebugCastEnv {
15+
override var lastEvaluatedAction: Action? = null
16+
override var lastDebugStepType: DebugStepType? = null
17+
18+
override fun printMessage(message: Component) {
19+
super.printMessage(message)
20+
printDebugMessage(caster, message)
21+
}
22+
23+
override fun sendMishapMsgToPlayer(mishap: OperatorSideEffect.DoMishap) {
24+
super.sendMishapMsgToPlayer(mishap)
25+
printDebugMishap(this, caster, mishap)
26+
}
27+
}

Common/src/main/kotlin/gay/object/hexdebug/casting/eval/IDebugCastEnv.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package gay.`object`.hexdebug.casting.eval
22

33
import at.petrak.hexcasting.api.casting.castables.Action
4+
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
5+
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
6+
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM
47
import gay.`object`.hexdebug.adapter.DebugAdapterManager
58
import gay.`object`.hexdebug.debugger.DebugStepType
69
import net.minecraft.network.chat.Component
@@ -26,4 +29,12 @@ interface IDebugCastEnv {
2629
) {
2730
DebugAdapterManager[caster]?.print(message.string + "\n", category, withSource)
2831
}
32+
33+
fun printDebugMishap(env: CastingEnvironment, caster: ServerPlayer, mishap: OperatorSideEffect.DoMishap) {
34+
mishap.mishap.errorMessageWithName(env, mishap.errorCtx)?.also {
35+
printDebugMessage(caster, it, OutputEventArgumentsCategory.STDERR)
36+
}
37+
}
2938
}
39+
40+
val CastingVM.debugCastEnv get() = env as IDebugCastEnv

Common/src/main/kotlin/gay/object/hexdebug/debugger/HexDebugger.kt

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import at.petrak.hexcasting.common.casting.actions.eval.OpEval
1616
import gay.`object`.hexdebug.adapter.LaunchArgs
1717
import gay.`object`.hexdebug.casting.eval.FrameBreakpoint
1818
import gay.`object`.hexdebug.casting.eval.IDebugCastEnv
19+
import gay.`object`.hexdebug.casting.eval.debugCastEnv
1920
import gay.`object`.hexdebug.debugger.allocators.SourceAllocator
2021
import gay.`object`.hexdebug.debugger.allocators.VariablesAllocator
2122
import gay.`object`.hexdebug.utils.ceilToPow
@@ -29,21 +30,26 @@ import org.eclipse.lsp4j.debug.LoadedSourceEventArgumentsReason as LoadedSourceR
2930
class HexDebugger(
3031
private val initArgs: InitializeRequestArguments,
3132
private val launchArgs: LaunchArgs,
32-
val vm: CastingVM,
33+
private val defaultEnv: CastingEnvironment,
3334
private val world: ServerLevel,
3435
private val onExecute: ((Iota) -> Unit)? = null,
3536
iotas: List<Iota>,
37+
private var image: CastingImage = CastingImage(),
3638
) {
3739
constructor(
3840
initArgs: InitializeRequestArguments,
3941
launchArgs: LaunchArgs,
4042
castArgs: CastArgs,
41-
) : this(initArgs, launchArgs, CastingVM.empty(castArgs.env), castArgs.world, castArgs.onExecute, castArgs.iotas)
43+
image: CastingImage = CastingImage(),
44+
) : this(initArgs, launchArgs, castArgs.env, castArgs.world, castArgs.onExecute, castArgs.iotas, image)
4245

4346
var lastEvaluatedMetadata: IotaMetadata? = null
4447
private set
4548

46-
private val debugCastEnv = vm.env as IDebugCastEnv
49+
// ensure we passed a debug cast env to help catch errors early
50+
init {
51+
defaultEnv as IDebugCastEnv
52+
}
4753

4854
private val variablesAllocator = VariablesAllocator()
4955
private val sourceAllocator = SourceAllocator(iotas.hashCode())
@@ -94,6 +100,8 @@ class HexDebugger(
94100

95101
private val nextFrame get() = (nextContinuation as? NotDone)?.frame
96102

103+
private fun getVM(env: CastingEnvironment? = null) = CastingVM(image, env ?: defaultEnv)
104+
97105
private fun registerNewSource(frame: ContinuationFrame): Source? = getIotas(frame)?.let(::registerNewSource)
98106

99107
private fun registerNewSource(iotas: Iterable<Iota>): Source? {
@@ -154,7 +162,7 @@ class HexDebugger(
154162
val scopes = mutableListOf(
155163
Scope().apply {
156164
name = "Data"
157-
variablesReference = vm.image.run {
165+
variablesReference = image.run {
158166
variablesAllocator.add(
159167
toVariable("Stack", stack.asReversed()),
160168
toVariable("Ravenmind", getRavenmind()),
@@ -163,7 +171,7 @@ class HexDebugger(
163171
},
164172
Scope().apply {
165173
name = "State"
166-
variablesReference = vm.image.run {
174+
variablesReference = image.run {
167175
val variables = mutableListOf(
168176
toVariable("OpsConsumed", opsConsumed.toString()),
169177
toVariable("EscapeNext", escapeNext.toString()),
@@ -241,9 +249,9 @@ class HexDebugger(
241249
}
242250
}
243251

244-
private fun getRavenmind() = vm.image.userData.let {
252+
private fun getRavenmind() = image.userData.let {
245253
if (it.contains(HexAPI.RAVENMIND_USERDATA)) {
246-
IotaType.deserialize(it.getCompound(HexAPI.RAVENMIND_USERDATA), vm.env.world)
254+
IotaType.deserialize(it.getCompound(HexAPI.RAVENMIND_USERDATA), defaultEnv.world)
247255
} else {
248256
NullIota()
249257
}
@@ -350,7 +358,7 @@ class HexDebugger(
350358
?.let { breakpoints[it.source.sourceReference]?.get(it.line) }
351359
?: return false
352360

353-
val escapeNext = vm.image.escapeNext || vm.image.parenCount > 0
361+
val escapeNext = image.escapeNext || image.parenCount > 0
354362

355363
return when (breakpointMode) {
356364
SourceBreakpointMode.EVALUATED -> !escapeNext
@@ -359,6 +367,8 @@ class HexDebugger(
359367
}
360368
}
361369

370+
fun generateDescs() = getVM().generateDescs()
371+
362372
private fun getClientView(vm: CastingVM): ExecutionClientView {
363373
val (stackDescs, ravenmind) = vm.generateDescs()
364374
val isStackClear = nextContinuation is Done // only close the window if we're done evaluating
@@ -368,13 +378,13 @@ class HexDebugger(
368378
/**
369379
* Use [DebugAdapter.evaluate][gay.object.hexdebug.adapter.DebugAdapter.evaluate] instead.
370380
*/
371-
internal fun evaluate(list: SpellList): DebugStepResult {
381+
internal fun evaluate(env: CastingEnvironment, list: SpellList): DebugStepResult {
372382
val startedEvaluating = evaluatorResetData == null
373383
if (startedEvaluating) {
374-
evaluatorResetData = Pair(nextContinuation, vm.image)
384+
evaluatorResetData = Pair(nextContinuation, image)
375385
}
376386
nextContinuation = nextContinuation.pushFrame(FrameEvaluate(list, false))
377-
return executeOnce().copy(startedEvaluating = startedEvaluating)
387+
return executeNextDebugStep(getVM(env)).copy(startedEvaluating = startedEvaluating)
378388
}
379389

380390
/**
@@ -383,7 +393,7 @@ class HexDebugger(
383393
internal fun resetEvaluator() {
384394
evaluatorResetData?.also { (continuation, image) ->
385395
nextContinuation = continuation
386-
vm.image = image
396+
this.image = image
387397
evaluatorResetData = null
388398
}
389399
evaluatorUIPatterns.clear()
@@ -400,14 +410,15 @@ class HexDebugger(
400410
}
401411

402412
fun executeUntilStopped(stepType: RequestStepType? = null): DebugStepResult {
413+
val vm = getVM()
403414
var lastResult: DebugStepResult? = null
404415
var isEscaping: Boolean? = null
405416
var stepDepth = 0
406417
var shouldStop = false
407418
var hitBreakpoint = false
408419

409420
while (true) {
410-
val result = executeOnce(exactlyOnce = true).let {
421+
val result = executeNextDebugStep(vm, exactlyOnce = true).let {
411422
if (it.isDone) return it
412423
lastResult?.plus(it) ?: it
413424
}
@@ -452,8 +463,10 @@ class HexDebugger(
452463
}
453464
}
454465

466+
fun executeOnce() = executeNextDebugStep(getVM())
467+
455468
// Copy of CastingVM.queueExecuteAndWrapIotas to allow stepping by one pattern at a time.
456-
fun executeOnce(exactlyOnce: Boolean = false): DebugStepResult {
469+
private fun executeNextDebugStep(vm: CastingVM, exactlyOnce: Boolean = false): DebugStepResult {
457470
var stepResult = DebugStepResult(StopReason.STEP)
458471

459472
var continuation = nextContinuation // bind locally so we can do smart casting
@@ -464,7 +477,7 @@ class HexDebugger(
464477
// Begin aggregating info
465478
val info = CastingVM.TempControllerInfo(earlyExit = false)
466479
while (continuation is NotDone && !info.earlyExit) {
467-
debugCastEnv.reset()
480+
vm.debugCastEnv.reset()
468481

469482
// Take the top of the continuation stack...
470483
val frame = continuation.frame
@@ -506,7 +519,7 @@ class HexDebugger(
506519
onExecute?.invoke(castResult.cast)
507520
}
508521

509-
val stepType = getStepType(castResult, continuation, newContinuation)
522+
val stepType = getStepType(vm, castResult, continuation, newContinuation)
510523
if (newContinuation is NotDone) {
511524
setIotaOverrides(castResult, continuation, newContinuation, stepType)
512525

@@ -516,7 +529,7 @@ class HexDebugger(
516529
}
517530

518531
// insert a virtual FrameFinishEval if OpEval didn't (ie. if we did a TCO)
519-
if (launchArgs.showTailCallFrames && debugCastEnv.lastEvaluatedAction is OpEval) {
532+
if (launchArgs.showTailCallFrames && vm.debugCastEnv.lastEvaluatedAction is OpEval) {
520533
val invokeMeta = iotaMetadata[castResult.cast]
521534
val nextInvokeMeta = frameInvocationMetadata[newContinuation.next]?.invoke()
522535
if (invokeMeta != null && invokeMeta != nextInvokeMeta) {
@@ -558,11 +571,12 @@ class HexDebugger(
558571
virtualFrames[continuation]?.clear()
559572

560573
nextContinuation = continuation
574+
image = vm.image
561575

562-
if (continuation is Done) {
563-
stepResult = stepResult.done()
564-
}
565-
return stepResult.copy(clientInfo = getClientView(vm))
576+
return when (continuation) {
577+
is Done -> stepResult.done()
578+
is NotDone -> stepResult
579+
}.copy(clientInfo = getClientView(vm))
566580
}
567581

568582
private fun shouldStopAtFrame(continuation: SpellContinuation) =
@@ -608,6 +622,7 @@ class HexDebugger(
608622
}
609623

610624
private fun getStepType(
625+
vm: CastingVM,
611626
castResult: CastResult,
612627
continuation: NotDone,
613628
newContinuation: SpellContinuation,
@@ -645,7 +660,7 @@ class HexDebugger(
645660
return DebugStepType.IN
646661
}
647662

648-
return debugCastEnv.lastDebugStepType
663+
return vm.debugCastEnv.lastDebugStepType
649664
}
650665

651666
private fun setIotaOverrides(
@@ -686,7 +701,7 @@ class HexDebugger(
686701
private fun iotaToString(iota: Iota, isSource: Boolean = false): String = when (iota) {
687702
// i feel like hex should have a thing for this...
688703
is PatternIota -> HexAPI.instance().run {
689-
when (val lookup = PatternRegistryManifest.matchPattern(iota.pattern, vm.env, false)) {
704+
when (val lookup = PatternRegistryManifest.matchPattern(iota.pattern, defaultEnv, false)) {
690705
is PatternShapeMatch.Normal -> getActionI18n(lookup.key, false)
691706
is PatternShapeMatch.PerWorld -> getActionI18n(lookup.key, true)
692707
is PatternShapeMatch.Special -> lookup.handler.name

Common/src/main/kotlin/gay/object/hexdebug/items/ItemDebugger.kt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package gay.`object`.hexdebug.items
22

33
import at.petrak.hexcasting.api.casting.iota.ListIota
4-
import at.petrak.hexcasting.api.casting.iota.PatternIota
54
import at.petrak.hexcasting.api.mod.HexConfig
65
import at.petrak.hexcasting.api.utils.getBoolean
76
import at.petrak.hexcasting.api.utils.getInt
87
import at.petrak.hexcasting.api.utils.putBoolean
98
import at.petrak.hexcasting.api.utils.putInt
109
import at.petrak.hexcasting.common.items.magic.ItemPackagedHex
11-
import at.petrak.hexcasting.common.msgs.MsgNewSpiralPatternsS2C
1210
import at.petrak.hexcasting.xplat.IXplatAbstractions
1311
import gay.`object`.hexdebug.HexDebug
1412
import gay.`object`.hexdebug.adapter.DebugAdapterManager
@@ -90,13 +88,7 @@ class ItemDebugger(properties: Properties) : ItemPackagedHex(properties) {
9088
} ?: return InteractionResultHolder.fail(stack)
9189

9290
val ctx = DebugItemCastEnv(serverPlayer, usedHand)
93-
val args = CastArgs(instrs, ctx, serverLevel) {
94-
if (it is PatternIota) {
95-
val packet = MsgNewSpiralPatternsS2C(serverPlayer.uuid, listOf(it.pattern), 140)
96-
IXplatAbstractions.INSTANCE.sendPacketToPlayer(serverPlayer, packet)
97-
IXplatAbstractions.INSTANCE.sendPacketTracking(serverPlayer, packet)
98-
}
99-
}
91+
val args = CastArgs(instrs, ctx, serverLevel)
10092

10193
if (!debugAdapter.startDebugging(args)) {
10294
return noClient(player, stack)

Common/src/main/kotlin/gay/object/hexdebug/items/ItemEvaluator.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import at.petrak.hexcasting.common.msgs.*
77
import at.petrak.hexcasting.xplat.IXplatAbstractions
88
import gay.`object`.hexdebug.HexDebug
99
import gay.`object`.hexdebug.adapter.DebugAdapterManager
10+
import gay.`object`.hexdebug.casting.eval.DebugStaffCastEnv
1011
import gay.`object`.hexdebug.utils.itemPredicate
1112
import net.minecraft.client.player.LocalPlayer
1213
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction
@@ -47,7 +48,7 @@ class ItemEvaluator(properties: Properties) : ItemStaff(properties) {
4748
}
4849

4950
val patterns = debugger.evaluatorUIPatterns
50-
val (stack, ravenmind) = debugger.vm.generateDescs()
51+
val (stack, ravenmind) = debugger.generateDescs()
5152
IXplatAbstractions.INSTANCE.sendPacketToPlayer(
5253
player, MsgOpenSpellGuiS2C(hand, patterns, stack, ravenmind, 0)
5354
)
@@ -82,7 +83,8 @@ class ItemEvaluator(properties: Properties) : ItemStaff(properties) {
8283
return
8384
}
8485

85-
val clientInfo = debugAdapter.evaluate(msg.pattern) ?: return
86+
val env = DebugStaffCastEnv(sender, msg.handUsed)
87+
val clientInfo = debugAdapter.evaluate(env, msg.pattern) ?: return
8688

8789
debugger.evaluatorUIPatterns.clear()
8890
if (!clientInfo.isStackClear) {

0 commit comments

Comments
 (0)