Skip to content

Commit 7ac603d

Browse files
authored
Add mishap-detection pattern (#1194)
Adds a system for mishap prevention by simulating patterns to see whether they'd mishap without having to actually cast them. Also removes the assessor/auditor patterns, since this makes them obsolete. <img width="862" height="562" alt="image" src="https://github.com/user-attachments/assets/e508c52e-627a-4dbf-99bd-2b1ce5ab7343" />
1 parent b432177 commit 7ac603d

15 files changed

Lines changed: 125 additions & 163 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
99
### Added
1010

1111
- Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110
12+
- Added Simulate, which causes the next pattern drawn to be simulated (to check for mishaps) rather than executed ([#1194](https://github.com/FallingColors/HexMod/pull/1194)) @Robotgiggle
1213
- Added the `hex_unbreakable` tag for blocks that should be immune to Break Block regardless of the configured mining tier ([#1186](https://github.com/FallingColors/HexMod/pull/1186)) @Robotgiggle @slava110
1314

1415
### Fixed

Common/src/main/java/at/petrak/hexcasting/api/casting/eval/ResolvedPatternType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ enum class ResolvedPatternType(val color: Int, val fadeColor: Int, val success:
66
UNRESOLVED(0x7f7f7f, 0xcccccc, false),
77
EVALUATED(0x7385de, 0xfecbe6, true),
88
ESCAPED(0xddcc73, 0xfffae5, true),
9+
SIMULATED(0xed9d64, 0xffecde, true),
910
UNDONE(0xb26b6b, 0xcca88e, true), // TODO: Pick better colours
1011
ERRORED(0xde6262, 0xffc7a0, false),
1112
INVALID(0xb26b6b, 0xcca88e, false);

Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.casting.iota.Iota
55
import at.petrak.hexcasting.api.casting.iota.IotaType
66
import at.petrak.hexcasting.api.utils.getOrCreateCompound
77
import at.petrak.hexcasting.api.utils.putCompound
8+
import at.petrak.hexcasting.api.utils.compositeCodecSeven
89
import com.mojang.serialization.Codec
910
import com.mojang.serialization.codecs.RecordCodecBuilder
1011
import net.minecraft.nbt.CompoundTag
@@ -22,10 +23,11 @@ data class CastingImage(
2223
val parenCount: Int,
2324
val parenthesized: List<ParenthesizedIota>,
2425
val escapeNext: Boolean,
26+
val simulateNext: Boolean,
2527
val opsConsumed: Long,
2628
val userData: CompoundTag
2729
) {
28-
constructor() : this(listOf(), 0, listOf(), false, 0, CompoundTag())
30+
constructor() : this(listOf(), 0, listOf(), false, false, 0, CompoundTag())
2931

3032
/**
3133
* `escaped` is used by [OpUndo][at.petrak.hexcasting.common.casting.actions.escaping.OpUndo] to determine whether the paren count
@@ -95,22 +97,24 @@ data class CastingImage(
9597
Codec.INT.fieldOf("open_parens").forGetter { it.parenCount },
9698
ParenthesizedIota.CODEC.listOf().fieldOf("parenthesized").forGetter { it.parenthesized },
9799
Codec.BOOL.fieldOf("escape_next").forGetter { it.escapeNext },
100+
Codec.BOOL.fieldOf("simulate_next").forGetter { it.simulateNext },
98101
Codec.LONG.fieldOf("ops_consumed").forGetter { it.opsConsumed },
99102
CompoundTag.CODEC.fieldOf("userData").forGetter { it.userData }
100-
).apply(inst) { a, b, c, d, e, f ->
101-
CastingImage(a, b, c, d, e, f)
103+
).apply(inst) { a, b, c, d, e, f, g ->
104+
CastingImage(a, b, c, d, e, f, g)
102105
}
103106
}.orElseGet(::CastingImage)
104107
@JvmStatic
105-
val STREAM_CODEC = StreamCodec.composite(
108+
val STREAM_CODEC = compositeCodecSeven(
106109
IotaType.TYPED_STREAM_CODEC.apply(ByteBufCodecs.list()), CastingImage::stack,
107110
ByteBufCodecs.VAR_INT, CastingImage::parenCount,
108111
ParenthesizedIota.STREAM_CODEC.apply(ByteBufCodecs.list()), CastingImage::parenthesized,
109112
ByteBufCodecs.BOOL, CastingImage::escapeNext,
113+
ByteBufCodecs.BOOL, CastingImage::simulateNext,
110114
ByteBufCodecs.VAR_LONG, CastingImage::opsConsumed,
111115
ByteBufCodecs.COMPOUND_TAG, { it.userData },
112-
{ a, b, c, d, e, f ->
113-
CastingImage(a, b, c, d, e, f)
116+
{ a, b, c, d, e, f, g ->
117+
CastingImage(a, b, c, d, e, f, g)
114118
}
115119
)
116120

Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.casting.SpellList
55
import at.petrak.hexcasting.api.casting.eval.*
66
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
77
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota
8+
import at.petrak.hexcasting.api.casting.iota.BooleanIota
89
import at.petrak.hexcasting.api.casting.iota.Iota
910
import at.petrak.hexcasting.api.casting.iota.IotaType
1011
import at.petrak.hexcasting.api.casting.iota.ListIota
@@ -111,7 +112,11 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
111112
ravenmind = IotaType.TYPED_CODEC.encodeStart<Tag?>(NbtOps.INSTANCE, newIota).getOrThrow() as CompoundTag?
112113
}
113114

114-
val isStackClear = image.stack.isEmpty() && image.parenCount == 0 && !image.escapeNext && ravenmind == null
115+
val isStackClear = image.stack.isEmpty()
116+
&& image.parenCount == 0
117+
&& !image.escapeNext
118+
&& !image.simulateNext
119+
&& ravenmind == null
115120

116121
this.env.postCast(image)
117122
return ExecutionClientView(isStackClear, lastResolutionType, image.stack, ravenmind)
@@ -150,13 +155,28 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
150155
return CastResult(iota, continuation, newImage, listOf(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE)
151156
}
152157

153-
if (this.image.parenCount > 0) {
158+
val result = if (this.image.parenCount > 0) {
154159
// Handle parens escaping
155-
return iota.executeInParens(this, world, continuation)
160+
iota.executeInParens(this, world, continuation)
156161
} else {
157162
// Handle normal execution behavior
158-
return iota.execute(this, world, continuation)
163+
iota.execute(this, world, continuation)
159164
}
165+
166+
// if simulating, push a bool for whether the cast would have succeeded; do not perform any side effects
167+
if (this.image.simulateNext) {
168+
val tooBig = result.newData != null && IotaType.isTooLargeToSerialize(result.newData.stack)
169+
val newStack = this.image.stack.toMutableList()
170+
newStack.add(BooleanIota(result.resolutionType.success && !tooBig))
171+
val newImage = this.image.copy(
172+
stack = newStack,
173+
simulateNext = false
174+
)
175+
return CastResult(iota, continuation, newImage, listOf(), ResolvedPatternType.SIMULATED, HexEvalSounds.NORMAL_EXECUTE)
176+
}
177+
178+
// otherwise, return the original CastResult to perform all the side effects, stack manip, etc
179+
return result
160180
} catch (exception: Exception) {
161181
// This means something very bad has happened
162182
exception.printStackTrace()

Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import at.petrak.hexcasting.api.casting.iota.NullIota
99
import at.petrak.hexcasting.api.casting.math.HexCoord
1010
import at.petrak.hexcasting.api.casting.validateSubIotas
1111
import at.petrak.hexcasting.api.mod.HexTags
12+
import com.mojang.datafixers.util.Function6
1213
import net.minecraft.ChatFormatting
1314
import net.minecraft.core.HolderLookup
1415
import net.minecraft.core.Registry
1516
import net.minecraft.nbt.*
1617
import net.minecraft.network.chat.Component
1718
import net.minecraft.network.chat.MutableComponent
1819
import net.minecraft.network.chat.Style
20+
import net.minecraft.network.codec.StreamCodec
1921
import net.minecraft.resources.ResourceKey
2022
import net.minecraft.resources.ResourceLocation
2123
import net.minecraft.server.level.ServerLevel
@@ -28,6 +30,7 @@ import net.minecraft.world.phys.Vec2
2830
import net.minecraft.world.phys.Vec3
2931
import java.lang.ref.WeakReference
3032
import java.util.*
33+
import java.util.function.Function
3134
import kotlin.math.absoluteValue
3235
import kotlin.math.max
3336
import kotlin.math.min
@@ -330,3 +333,38 @@ fun <T : Iota> validateIotaList(iotaList: List<T>, serverLevel: ServerLevel): Li
330333
return iotaList.map { validateIota(it, serverLevel) }
331334
}
332335

336+
337+
// vanilla's StreamCodec.composite() only supports up to six fields in 1.21
338+
fun <B, C, T1, T2, T3, T4, T5, T6, T7> compositeCodecSeven(
339+
streamCodec1: StreamCodec<in B, T1>, function1: Function<C, T1>,
340+
streamCodec2: StreamCodec<in B, T2>, function2: Function<C, T2>,
341+
streamCodec3: StreamCodec<in B, T3>, function3: Function<C, T3>,
342+
streamCodec4: StreamCodec<in B, T4>, function4: Function<C, T4>,
343+
streamCodec5: StreamCodec<in B, T5>, function5: Function<C, T5>,
344+
streamCodec6: StreamCodec<in B, T6>, function6: Function<C, T6>,
345+
streamCodec7: StreamCodec<in B, T7>, function7: Function<C, T7>,
346+
createFunction: Function7<T1, T2, T3, T4, T5, T6, T7, C>
347+
): StreamCodec<B, C> {
348+
return object : StreamCodec<B, C> {
349+
override fun decode(stream: B): C {
350+
val field1 = streamCodec1.decode(stream)
351+
val field2 = streamCodec2.decode(stream)
352+
val field3 = streamCodec3.decode(stream)
353+
val field4 = streamCodec4.decode(stream)
354+
val field5 = streamCodec5.decode(stream)
355+
val field6 = streamCodec6.decode(stream)
356+
val field7 = streamCodec7.decode(stream)
357+
return createFunction.invoke(field1, field2, field3, field4, field5, field6, field7)
358+
}
359+
360+
override fun encode(stream: B, obj: C) {
361+
streamCodec1.encode(stream, function1.apply(obj))
362+
streamCodec2.encode(stream, function2.apply(obj))
363+
streamCodec3.encode(stream, function3.apply(obj))
364+
streamCodec4.encode(stream, function4.apply(obj))
365+
streamCodec5.encode(stream, function5.apply(obj))
366+
streamCodec6.encode(stream, function6.apply(obj))
367+
streamCodec7.encode(stream, function7.apply(obj))
368+
}
369+
}
370+
}

Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/directrix/BlockBooleanDirectrix.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Di
6262
? bs.getValue(FACING).getOpposite()
6363
: bs.getValue(FACING);
6464
var imageOut = imageIn.copy(stack, imageIn.getParenCount(), imageIn.getParenthesized(),
65-
imageIn.getEscapeNext(), imageIn.getOpsConsumed(), imageIn.getUserData());
65+
imageIn.getEscapeNext(), imageIn.getSimulateNext(), imageIn.getOpsConsumed(), imageIn.getUserData());
6666

6767
return new ControlFlow.Continue(imageOut, List.of(this.exitPositionFromDirection(pos, outputDir)));
6868
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package at.petrak.hexcasting.common.casting.actions.escaping
2+
3+
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.OperationResult
6+
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage
7+
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
8+
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
9+
10+
object OpSimulate : Action {
11+
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
12+
val image2 = image.copy(
13+
simulateNext = true
14+
)
15+
return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE)
16+
}
17+
}

Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpReadable.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.

Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerReadable.kt

Lines changed: 0 additions & 29 deletions
This file was deleted.

Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWritable.kt

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)