meta-eval limit is dead, long live op limit

This commit is contained in:
petrak@ 2023-02-24 20:22:41 -06:00
parent 9a231a634b
commit e24e642432
9 changed files with 38 additions and 36 deletions

View file

@ -113,9 +113,9 @@ public interface HexAPI {
*/
String RAVENMIND_USERDATA = modLoc("ravenmind").toString();
/**
* Location in the userdata of the evaluation depth
* Location in the userdata of the number of ops executed
*/
String EVAL_DEPTH_USERDATA = modLoc("eval_depth").toString();
String OP_COUNT_USERDATA = modLoc("op_count").toString();
String MARKED_MOVED_USERDATA = modLoc("impulsed").toString();

View file

@ -3,8 +3,6 @@ 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
@ -79,21 +77,6 @@ data class CastingImage private constructor(
}
}
/**
* 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)

View file

@ -13,6 +13,7 @@ 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.mod.HexConfig
import at.petrak.hexcasting.api.mod.HexTags
import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.common.casting.PatternRegistryManifest
@ -37,6 +38,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
* needs to see.
*/
fun queueAndExecuteIotas(iotas: List<Iota>, 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
@ -147,6 +149,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
private fun executePattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
var castedName: Component? = null
try {
val lookup = PatternRegistryManifest.matchPattern(newPat, world, false)
this.env.precheckAction(lookup)
@ -169,6 +172,17 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
throw MishapInvalidPattern()
} else throw IllegalStateException()
val opCount = if (this.image.userData.contains(HexAPI.OP_COUNT_USERDATA)) {
this.image.userData.getInt(HexAPI.OP_COUNT_USERDATA)
} else {
this.image.userData.putInt(HexAPI.OP_COUNT_USERDATA, 0)
0
}
if (opCount + 1 > HexConfig.server().maxOpCount()) {
throw MishapEvalTooMuch()
}
this.image.userData.putInt(HexAPI.OP_COUNT_USERDATA, opCount + 1)
val sideEffects = mutableListOf<OperatorSideEffect>()
var stack2: List<Iota>? = null
var cont2 = continuation

View file

@ -5,7 +5,7 @@ import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.world.item.DyeColor
class MishapEvalTooDeep : Mishap() {
class MishapEvalTooMuch : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLUE)

View file

@ -56,7 +56,7 @@ public class HexConfig {
public interface ServerConfigAccess {
int opBreakHarvestLevelBecauseForgeThoughtItWasAGoodIdeaToImplementHarvestTiersUsingAnHonestToGodTopoSort();
int maxRecurseDepth();
int maxOpCount();
int maxSpellCircleLength();
@ -69,7 +69,7 @@ public class HexConfig {
// fun fact, although dimension keys are a RegistryHolder, they aren't a registry, so i can't do tags
boolean canTeleportInThisDimension(ResourceKey<Level> dimension);
int DEFAULT_MAX_RECURSE_DEPTH = 512;
int DEFAULT_MAX_OP_COUNT = 1_000_000;
int DEFAULT_MAX_SPELL_CIRCLE_LENGTH = 1024;
int DEFAULT_OP_BREAK_HARVEST_LEVEL = 3;

View file

@ -72,15 +72,19 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
sender.awardStat(HexStatistics.PATTERNS_DRAWN);
var harness = IXplatAbstractions.INSTANCE.getStaffcastVM(sender, msg.handUsed());
var vm = IXplatAbstractions.INSTANCE.getStaffcastVM(sender, msg.handUsed());
// every time we send a new pattern it'll be happening in a different tick, so reset here
// i don't think we can do this in the casting vm itself because it doesn't know if `queueAndExecuteIotas`
// is being called from the top level or not
vm.getImage().getUserData().remove(HexAPI.OP_COUNT_USERDATA);
ExecutionClientView clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
ExecutionClientView clientInfo = vm.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
if (clientInfo.isStackClear()) {
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, null);
IXplatAbstractions.INSTANCE.setPatterns(sender, List.of());
} else {
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, harness);
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, vm);
if (!resolvedPatterns.isEmpty()) {
resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType());
}

View file

@ -210,7 +210,7 @@
"text.autoconfig.hexcasting.option.client.gridSnapThreshold.@Tooltip": "When using a staff, the distance from one dot you have to go to snap to the next dot, where 0.5 means 50% of the way (0.5-1)",
"text.autoconfig.hexcasting.option.server.opBreakHarvestLevel": "Break Harvest Level",
"text.autoconfig.hexcasting.option.server.maxRecurseDepth": "Max Recurse Depth",
"text.autoconfig.hexcasting.option.server.maxOpCount": "Max Action Count",
"text.autoconfig.hexcasting.option.server.maxSpellCircleLength": "Max Spell Circle Length",
"text.autoconfig.hexcasting.option.server.actionDenyList": "Action Deny List",
"text.autoconfig.hexcasting.option.server.circleActionDenyList": "Circle Action Deny List",
@ -218,7 +218,7 @@
"text.autoconfig.hexcasting.option.server.scrollInjectionsRaw": "Scroll Injection Weights",
"text.autoconfig.hexcasting.option.server.amethystShardModification": "Amethyst Shard Drop Rate Change",
"text.autoconfig.hexcasting.option.server.opBreakHarvestLevel.@Tooltip": "The harvest level of the Break Block spell.\n0 = wood, 1 = stone, 2 = iron, 3 = diamond, 4 = netherite.",
"text.autoconfig.hexcasting.option.server.maxRecurseDepth.@Tooltip": "How many times an action can recursively cast other actions",
"text.autoconfig.hexcasting.option.server.maxOpCount.@Tooltip": "The maximum number of actions that can be executed in one tick, to avoid hanging the server.",
"text.autoconfig.hexcasting.option.server.maxSpellCircleLength.@Tooltip": "The maximum number of slates in a spell circle",
"text.autoconfig.hexcasting.option.server.actionDenyList.@Tooltip": "Resource locations of disallowed actions. Trying to cast one of these will result in a mishap. For example, hexcasting:get_caster will prevent Mind's Reflection",
"text.autoconfig.hexcasting.option.server.circleActionDenyList.@Tooltip": "Resource locations of disallowed actions within circles. Trying to cast one of these from a circle will result in a mishap.",

View file

@ -159,7 +159,7 @@ public class FabricHexConfig extends PartitioningSerializer.GlobalData {
@ConfigEntry.Gui.Tooltip
private int opBreakHarvestLevel = DEFAULT_OP_BREAK_HARVEST_LEVEL;
@ConfigEntry.Gui.Tooltip
private int maxRecurseDepth = DEFAULT_MAX_RECURSE_DEPTH;
private int maxOpCount = DEFAULT_MAX_OP_COUNT;
@ConfigEntry.Gui.Tooltip
private int maxSpellCircleLength = DEFAULT_MAX_SPELL_CIRCLE_LENGTH;
@ConfigEntry.Gui.Tooltip
@ -189,7 +189,7 @@ public class FabricHexConfig extends PartitioningSerializer.GlobalData {
@Override
public void validatePostLoad() throws ValidationException {
this.maxRecurseDepth = Math.max(this.maxRecurseDepth, 0);
this.maxOpCount = Math.max(this.maxOpCount, 0);
this.maxSpellCircleLength = Math.max(this.maxSpellCircleLength, 4);
this.scrollInjections = new Object2IntOpenHashMap<>();
@ -206,8 +206,8 @@ public class FabricHexConfig extends PartitioningSerializer.GlobalData {
}
@Override
public int maxRecurseDepth() {
return maxRecurseDepth;
public int maxOpCount() {
return maxOpCount;
}
@Override

View file

@ -123,7 +123,7 @@ public class ForgeHexConfig implements HexConfig.CommonConfigAccess {
public static class Server implements HexConfig.ServerConfigAccess {
private static ForgeConfigSpec.IntValue opBreakHarvestLevel;
private static ForgeConfigSpec.IntValue maxRecurseDepth;
private static ForgeConfigSpec.IntValue maxOpCount;
private static ForgeConfigSpec.IntValue maxSpellCircleLength;
@ -141,8 +141,9 @@ public class ForgeHexConfig implements HexConfig.CommonConfigAccess {
public Server(ForgeConfigSpec.Builder builder) {
builder.push("Spells");
maxRecurseDepth = builder.comment("How many times a spell can recursively cast other spells")
.defineInRange("maxRecurseDepth", DEFAULT_MAX_RECURSE_DEPTH, 0, Integer.MAX_VALUE);
maxOpCount = builder.comment("The maximum number of actions that can be executed in one tick, to avoid " +
"hanging the server.")
.defineInRange("maxOpCount", DEFAULT_MAX_OP_COUNT, 0, Integer.MAX_VALUE);
opBreakHarvestLevel = builder.comment(
"The harvest level of the Break Block spell.",
"0 = wood, 1 = stone, 2 = iron, 3 = diamond, 4 = netherite."
@ -177,8 +178,8 @@ public class ForgeHexConfig implements HexConfig.CommonConfigAccess {
}
@Override
public int maxRecurseDepth() {
return maxRecurseDepth.get();
public int maxOpCount() {
return maxOpCount.get();
}
@Override