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 b5c4996d..f3d959d9 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java @@ -112,6 +112,12 @@ public interface HexAPI { * Location in the userdata of the ravenmind */ String RAVENMIND_USERDATA = modLoc("ravenmind").toString(); + /** + * Location in the userdata of the evaluation depth + */ + String EVAL_DEPTH_USERDATA = modLoc("eval_depth").toString(); + + String MARKED_MOVED_USERDATA = modLoc("impulsed").toString(); static HexAPI instance() { return INSTANCE.get(); diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt index 6f434769..08248d31 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt @@ -22,6 +22,12 @@ interface SpellAction : Action { ctx: CastingEnvironment ): Triple>? + fun executeWithUserdata( + args: List, ctx: CastingEnvironment, userData: CompoundTag + ): Triple>? { + return this.execute(args, ctx) + } + override fun operate( env: CastingEnvironment, stack: MutableList, @@ -32,7 +38,8 @@ interface SpellAction : Action { throw MishapNotEnoughArgs(this.argc, stack.size) val args = stack.takeLast(this.argc) for (_i in 0 until this.argc) stack.removeLast() - val executeResult = this.execute(args, env) ?: return OperationResult(stack, userData, listOf(), continuation) + val executeResult = this.executeWithUserdata(args, env, userData) + ?: return OperationResult(stack, userData, listOf(), continuation) val (spell, media, particles) = executeResult val sideEffects = mutableListOf() 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 b282d928..3b3b2b56 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 @@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway; import at.petrak.hexcasting.api.casting.mishaps.MishapLocationTooFarAway; import at.petrak.hexcasting.api.misc.FrozenColorizer; import at.petrak.hexcasting.api.mod.HexConfig; +import at.petrak.hexcasting.api.utils.HexUtils; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; @@ -20,9 +21,7 @@ import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.function.Predicate; /** @@ -32,12 +31,9 @@ import java.util.function.Predicate; */ public abstract class CastingEnvironment { protected final ServerLevel world; - // TODO move this to the env? - protected final Set entitiesGivenMotion; protected CastingEnvironment(ServerLevel world) { this.world = world; - this.entitiesGivenMotion = new HashSet<>(); } public final ServerLevel getWorld() { @@ -59,8 +55,7 @@ public abstract class CastingEnvironment { public abstract MishapEnvironment getMishapEnvironment(); /** - * Get the sound that this I/O module makes. - *

+ * Get the sound that this I/O module makes upon receiving a pattern */ public abstract EvalSound getSoundType(); @@ -162,12 +157,10 @@ public abstract class CastingEnvironment { } } - public void markEntityAsImpulsed(Entity e) { - this.entitiesGivenMotion.add(e); - } + public abstract InteractionHand castingHand(); - public boolean hasBeenGivenMotion(Entity e) { - return this.entitiesGivenMotion.contains(e); + public InteractionHand otherHand() { + return HexUtils.otherHand(this.castingHand()); } /** 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 index 64c2f67d..ae5f2ef4 100644 --- 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 @@ -3,10 +3,13 @@ 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.casting.mishaps.MishapEvalTooDeep +import at.petrak.hexcasting.api.mod.HexConfig import at.petrak.hexcasting.api.utils.* import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.Tag import net.minecraft.server.level.ServerLevel +import net.minecraft.world.entity.Entity /** * The state of a casting VM, containing the stack and all @@ -18,10 +21,9 @@ data class CastingImage private constructor( val parenthesized: List, val escapeNext: Boolean, - val iterationSteps: Int, val userData: CompoundTag ) { - public constructor() : this(listOf(), 0, listOf(), false, CompoundTag()) + constructor() : this(listOf(), 0, listOf(), false, CompoundTag()) fun serializeToNbt() = NBTBuilder { TAG_STACK %= stack.serializeToNBT() @@ -33,17 +35,6 @@ data class CastingImage private constructor( TAG_USERDATA %= userData } - /** - * Throws if we get too deep. - */ - fun incDepth(): CastingImage { - val maxAllowedDepth = HexConfig.server().maxRecurseDepth() - if (this.iterationSteps + 1 > maxAllowedDepth) { - throw MishapEvalTooDeep() - } - return copy(iterationSteps = iterationSteps + 1) - } - companion object { const val TAG_STACK = "stack" const val TAG_PAREN_COUNT = "open_parens" @@ -87,5 +78,31 @@ data class CastingImage private constructor( CastingImage() } } + + /** + * Throws if we get too deep. + */ + @JvmStatic + fun incDepth(userData: CompoundTag): CompoundTag { + val maxAllowedDepth = HexConfig.server().maxRecurseDepth() + val depth = userData.getInt(HexAPI.EVAL_DEPTH_USERDATA) + if (depth + 1 > maxAllowedDepth) { + throw MishapEvalTooDeep() + } + + userData.putInt(HexAPI.EVAL_DEPTH_USERDATA, depth + 1) + return userData + } + + @JvmStatic + fun checkAndMarkGivenMotion(userData: CompoundTag, entity: Entity): Boolean { + val marked = userData.getOrCreateCompound(HexAPI.MARKED_MOVED_USERDATA) + return if (marked.contains(entity.stringUUID)) { + true + } else { + marked.putBoolean(entity.stringUUID, true) + false + } + } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt index 15c57f3b..fb1a7cf8 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt @@ -83,7 +83,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { // ALSO TODO need to add reader macro-style things try { this.handleParentheses(iota)?.let { (data, resolutionType) -> - return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR) + return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.ADD_PATTERN) } } catch (e: MishapTooManyCloseParens) { // This is ridiculous and needs to be fixed diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapImmuneEntity.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapImmuneEntity.kt index 4065d505..d8e9d2f0 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapImmuneEntity.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapImmuneEntity.kt @@ -12,7 +12,7 @@ class MishapImmuneEntity(val entity: Entity) : Mishap() { dyeColor(DyeColor.BLUE) override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList) { - yeetHeldItemsTowards(ctx, entity.position()) + ctx.mishapEnvironment.yeetHeldItemsTowards(entity.position()) } override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/env/PlayerBasedCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/env/PlayerBasedCastEnv.java index 0fc2724f..b884a78e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/env/PlayerBasedCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/env/PlayerBasedCastEnv.java @@ -5,6 +5,8 @@ import at.petrak.hexcasting.api.addldata.ADMediaHolder; import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers; import at.petrak.hexcasting.api.casting.ParticleSpray; import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; +import at.petrak.hexcasting.api.casting.eval.MishapEnvironment; +import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound; import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect; import at.petrak.hexcasting.api.casting.mishaps.Mishap; import at.petrak.hexcasting.api.misc.FrozenColorizer; @@ -13,6 +15,7 @@ import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.mod.HexStatistics; import at.petrak.hexcasting.api.utils.HexUtils; import at.petrak.hexcasting.api.utils.MediaHelper; +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; @@ -44,6 +47,11 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment { return this.caster; } + @Override + public EvalSound getSoundType() { + return HexEvalSounds.ADD_PATTERN; + } + @Override protected List getUsableStacks(StackDiscoveryMode mode) { return switch (mode) { @@ -167,9 +175,13 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment { return this.caster.position(); } + @Override + public MishapEnvironment getMishapEnvironment() { + return new PlayerBasedMishapEnv(this.caster); + } + protected void sendMishapMsgToPlayer(OperatorSideEffect.DoMishap mishap) { var msg = mishap.getMishap().errorMessageWithName(this, mishap.getErrorCtx()); this.caster.sendSystemMessage(msg); } - } 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 bc225763..a58dea20 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 @@ -4,13 +4,11 @@ import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.casting.eval.CastResult; 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; import at.petrak.hexcasting.api.casting.iota.PatternIota; import at.petrak.hexcasting.api.casting.math.HexCoord; import at.petrak.hexcasting.api.misc.FrozenColorizer; import at.petrak.hexcasting.api.mod.HexStatistics; -import at.petrak.hexcasting.api.mod.HexTags; import at.petrak.hexcasting.common.network.MsgNewSpellPatternAck; import at.petrak.hexcasting.common.network.MsgNewSpellPatternSyn; import at.petrak.hexcasting.xplat.IXplatAbstractions; @@ -51,52 +49,45 @@ public class StaffCastEnv extends PlayerBasedCastEnv { } public static void handleNewPatternOnServer(ServerPlayer sender, MsgNewSpellPatternSyn msg) { - var held = sender.getItemInHand(msg.handUsed()); - if (held.is(HexTags.Items.STAVES)) { - boolean autoFail = false; + boolean cheatedPatternOverlap = false; - List resolvedPatterns = msg.resolvedPatterns(); - if (!resolvedPatterns.isEmpty()) { - var allPoints = new HashSet(); - for (int i = 0; i < resolvedPatterns.size() - 1; i++) { - ResolvedPattern pat = resolvedPatterns.get(i); - allPoints.addAll(pat.getPattern().positions(pat.getOrigin())); - } - var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1); - var currentSpellPoints = currentResolvedPattern.getPattern() - .positions(currentResolvedPattern.getOrigin()); - if (currentSpellPoints.stream().anyMatch(allPoints::contains)) { - autoFail = true; - } + List resolvedPatterns = msg.resolvedPatterns(); + if (!resolvedPatterns.isEmpty()) { + var allPoints = new HashSet(); + for (int i = 0; i < resolvedPatterns.size() - 1; i++) { + ResolvedPattern pat = resolvedPatterns.get(i); + allPoints.addAll(pat.getPattern().positions(pat.getOrigin())); } - - sender.awardStat(HexStatistics.PATTERNS_DRAWN); - - var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed()); - - ExecutionClientView clientInfo; - if (autoFail) { - var descs = harness.generateDescs(); - clientInfo = new ExecutionClientView(harness.getStack().isEmpty(), ResolvedPatternType.INVALID, - descs.getFirst(), descs.getSecond(), descs.getThird(), harness.getParenCount()); - } else { - clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel()); + var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1); + var currentSpellPoints = currentResolvedPattern.getPattern() + .positions(currentResolvedPattern.getOrigin()); + if (currentSpellPoints.stream().anyMatch(allPoints::contains)) { + cheatedPatternOverlap = true; } - - if (clientInfo.isStackClear()) { - IXplatAbstractions.INSTANCE.setHarness(sender, null); - IXplatAbstractions.INSTANCE.setPatterns(sender, List.of()); - } else { - IXplatAbstractions.INSTANCE.setHarness(sender, harness); - if (!resolvedPatterns.isEmpty()) { - resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType()); - } - IXplatAbstractions.INSTANCE.setPatterns(sender, resolvedPatterns); - } - - IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender, - new MsgNewSpellPatternAck(clientInfo, resolvedPatterns.size() - 1)); } + if (cheatedPatternOverlap) { + return; + } + + sender.awardStat(HexStatistics.PATTERNS_DRAWN); + + var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed()); + + ExecutionClientView clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel()); + + if (clientInfo.isStackClear()) { + IXplatAbstractions.INSTANCE.setHarness(sender, null); + IXplatAbstractions.INSTANCE.setPatterns(sender, List.of()); + } else { + IXplatAbstractions.INSTANCE.setHarness(sender, harness); + if (!resolvedPatterns.isEmpty()) { + resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType()); + } + IXplatAbstractions.INSTANCE.setPatterns(sender, resolvedPatterns); + } + + IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender, + new MsgNewSpellPatternAck(clientInfo, resolvedPatterns.size() - 1)); } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/eval/OpEval.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/eval/OpEval.kt index cca8ced4..a36e6f55 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/eval/OpEval.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/eval/OpEval.kt @@ -1,9 +1,11 @@ package at.petrak.hexcasting.common.casting.operators.eval import at.petrak.hexcasting.api.casting.SpellList +import at.petrak.hexcasting.api.casting.asActionResult import at.petrak.hexcasting.api.casting.castables.Action import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate import at.petrak.hexcasting.api.casting.eval.vm.FrameFinishEval import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation @@ -23,9 +25,9 @@ object OpEval : Action { val datum = stack.removeLastOrNull() ?: throw MishapNotEnoughArgs(1, 0) val instrs = evaluatable(datum, 0) - val stack = instrs.ifRight { - // FIXME: Casting depth increment not implemented yet - //env.incDepth() + instrs.ifRight { + CastingImage.incDepth(userData) + it.asActionResult } // if not installed already... @@ -39,6 +41,6 @@ object OpEval : Action { val instrsList = instrs.map({ SpellList.LList(0, listOf(PatternIota(it))) }, { it }) val frame = FrameEvaluate(instrsList, true) - return OperationResult(stack, userData, listOf(), newCont.pushFrame(frame)) + return OperationResult(listOf(), userData, listOf(), newCont.pushFrame(frame)) } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpAddMotion.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpAddMotion.kt index 36f370c3..7c61709e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpAddMotion.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpAddMotion.kt @@ -4,10 +4,12 @@ import at.petrak.hexcasting.api.casting.ParticleSpray import at.petrak.hexcasting.api.casting.RenderedSpell import at.petrak.hexcasting.api.casting.castables.SpellAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage import at.petrak.hexcasting.api.casting.getEntity import at.petrak.hexcasting.api.casting.getVec3 import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.misc.MediaConstants +import net.minecraft.nbt.CompoundTag import net.minecraft.world.entity.Entity import net.minecraft.world.phys.Vec3 @@ -18,17 +20,18 @@ object OpAddMotion : SpellAction { // for bug #387 val MAX_MOTION: Double = 8192.0 - override fun execute( + override fun executeWithUserdata( args: List, - ctx: CastingEnvironment - ): Triple> { + ctx: CastingEnvironment, + userData: CompoundTag + ): Triple>? { val target = args.getEntity(0, argc) val motion = args.getVec3(1, argc) ctx.assertEntityInRange(target) + var motionForCost = motion.lengthSqr() - if (ctx.hasBeenGivenMotion(target)) + if (CastingImage.checkAndMarkGivenMotion(userData, target)) motionForCost++ - ctx.markEntityAsMotionAdded(target) val shrunkMotion = if (motion.lengthSqr() > MAX_MOTION * MAX_MOTION) motion.normalize().scale(MAX_MOTION) @@ -48,6 +51,10 @@ object OpAddMotion : SpellAction { ) } + override fun execute(args: List, ctx: CastingEnvironment): Triple>? { + throw IllegalStateException() + } + private data class Spell(val target: Entity, val motion: Vec3) : RenderedSpell { override fun cast(ctx: CastingEnvironment) { target.push(motion.x, motion.y, motion.z) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt index 4f7070d9..e00706da 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt @@ -49,7 +49,7 @@ class OpCreateFluid(val cost: Int, val bucket: Item, val cauldron: BlockState, v ctx.world.setBlock(pos, cauldron, 3) else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid( ctx.world, - ctx.castingHand, + ctx.castingHand(), pos, fluid ) && bucket is BucketItem) { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexEvalSounds.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexEvalSounds.java index 8b50a57a..6587df43 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexEvalSounds.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexEvalSounds.java @@ -15,14 +15,14 @@ public class HexEvalSounds { public static final EvalSound NOTHING = make("nothing", new EvalSound(null, Integer.MIN_VALUE)); - public static final EvalSound OPERATOR = make("operator", + public static final EvalSound ADD_PATTERN = make("operator", new EvalSound(HexSounds.ADD_PATTERN, 0)); public static final EvalSound SPELL = make("spell", new EvalSound(HexSounds.ACTUALLY_CAST, 1000)); public static final EvalSound HERMES = make("hermes", new EvalSound(HexSounds.CAST_HERMES, 2000)); public static final EvalSound THOTH = make("thoth", - new EvalSound(HexSounds.CAST_THOTH, 2000)); + new EvalSound(HexSounds.CAST_THOTH, 2500)); public static final EvalSound MUTE = make("mute", new EvalSound(null, 3000));