Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue #407: Prevent crashes due to unbounded growth. #721

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.casting.iota.IotaType.isTooLargeToSerialize
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.casting.iota.PatternIota
import at.petrak.hexcasting.api.casting.math.HexDir
Expand Down Expand Up @@ -50,15 +51,30 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
// ...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 image2 = next.evaluate(continuation.next, world, this)
val image2 = next.evaluate(continuation.next, world, this).let { result ->
// if stack is unable to be serialized, have the result be an error
if (result.newData != null && isTooLargeToSerialize(result.newData.stack)) {
navarchus marked this conversation as resolved.
Show resolved Hide resolved
result.copy(
newData = null,
sideEffects = result.sideEffects + OperatorSideEffect.DoMishap(MishapStackSize(), Mishap.Context(null, null)),
navarchus marked this conversation as resolved.
Show resolved Hide resolved
resolutionType = ResolvedPatternType.ERRORED,
sound = HexEvalSounds.MISHAP,
)
} else {
result
}
}

// Then write all pertinent data back to the harness for the next iteration.
if (image2.newData != null) {
this.image = image2.newData
}

navarchus marked this conversation as resolved.
Show resolved Hide resolved
this.env.postExecution(image2)

continuation = image2.continuation
lastResolutionType = image2.resolutionType

navarchus marked this conversation as resolved.
Show resolved Hide resolved
try {
performSideEffects(info, image2.sideEffects)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package at.petrak.hexcasting.api.casting.mishaps

import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.pigment.FrozenPigment
import at.petrak.hexcasting.common.lib.HexDamageTypes
import net.minecraft.world.item.DyeColor

class MishapStackSize() : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.BLACK)

override fun resolutionType(ctx: CastingEnvironment) = ResolvedPatternType.ERRORED

override fun execute(env: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
stack.clear()
stack.add(GarbageIota())
}

override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("stack_size")
}
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,7 @@
invalid_spell_datum_type: "Tried to use a value of invalid type as a SpellDatum: %s (class %s). This is a bug in the mod.",
unknown: "threw an exception (%s). This is a bug in the mod.",
shame: "Shame on you!",
stack_size: "My mind was too small to contain the breadth of this spell.",
navarchus marked this conversation as resolved.
Show resolved Hide resolved

invalid_value: {
"": "expected %s at index %s of the stack, but got %s",
Expand Down
66 changes: 33 additions & 33 deletions Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java
navarchus marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,22 @@ public class ForgeHexConfig implements HexConfig.CommonConfigAccess {
public ForgeHexConfig(ForgeConfigSpec.Builder builder) {
builder.push("Media Amounts");
dustMediaAmount = builder.comment("How much media a single Amethyst Dust item is worth")
.defineInRange("dustMediaAmount", DEFAULT_DUST_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
.defineInRange("dustMediaAmount", DEFAULT_DUST_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
shardMediaAmount = builder.comment("How much media a single Amethyst Shard item is worth")
.defineInRange("shardMediaAmount", DEFAULT_SHARD_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
.defineInRange("shardMediaAmount", DEFAULT_SHARD_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
chargedCrystalMediaAmount = builder.comment("How much media a single Charged Amethyst Crystal item is worth")
.defineInRange("chargedCrystalMediaAmount", DEFAULT_CHARGED_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
.defineInRange("chargedCrystalMediaAmount", DEFAULT_CHARGED_MEDIA_AMOUNT, 0, Integer.MAX_VALUE);
mediaToHealthRate = builder.comment("How many points of media a half-heart is worth when casting from HP")
.defineInRange("mediaToHealthRate", DEFAULT_MEDIA_TO_HEALTH_RATE, 0.0, Double.POSITIVE_INFINITY);
.defineInRange("mediaToHealthRate", DEFAULT_MEDIA_TO_HEALTH_RATE, 0.0, Double.POSITIVE_INFINITY);
builder.pop();

builder.push("Cooldowns");
cypherCooldown = builder.comment("Cooldown in ticks of a cypher")
.defineInRange("cypherCooldown", DEFAULT_CYPHER_COOLDOWN, 0, Integer.MAX_VALUE);
.defineInRange("cypherCooldown", DEFAULT_CYPHER_COOLDOWN, 0, Integer.MAX_VALUE);
trinketCooldown = builder.comment("Cooldown in ticks of a trinket")
.defineInRange("trinketCooldown", DEFAULT_TRINKET_COOLDOWN, 0, Integer.MAX_VALUE);
.defineInRange("trinketCooldown", DEFAULT_TRINKET_COOLDOWN, 0, Integer.MAX_VALUE);
artifactCooldown = builder.comment("Cooldown in ticks of a artifact")
.defineInRange("artifactCooldown", DEFAULT_ARTIFACT_COOLDOWN, 0, Integer.MAX_VALUE);
.defineInRange("artifactCooldown", DEFAULT_ARTIFACT_COOLDOWN, 0, Integer.MAX_VALUE);
builder.pop();
}

Expand Down Expand Up @@ -85,19 +85,19 @@ public static class Client implements HexConfig.ClientConfigAccess {

public Client(ForgeConfigSpec.Builder builder) {
ctrlTogglesOffStrokeOrder = builder.comment(
"Whether the ctrl key will instead turn *off* the color gradient on patterns")
.define("ctrlTogglesOffStrokeOrder", DEFAULT_CTRL_TOGGLES_OFF_STROKE_ORDER);
"Whether the ctrl key will instead turn *off* the color gradient on patterns")
.define("ctrlTogglesOffStrokeOrder", DEFAULT_CTRL_TOGGLES_OFF_STROKE_ORDER);
invertSpellbookScrollDirection = builder.comment(
"Whether scrolling up (as opposed to down) will increase the page index of the spellbook, and " +
"vice versa")
.define("invertSpellbookScrollDirection", DEFAULT_INVERT_SPELLBOOK_SCROLL);
"Whether scrolling up (as opposed to down) will increase the page index of the spellbook, and " +
"vice versa")
.define("invertSpellbookScrollDirection", DEFAULT_INVERT_SPELLBOOK_SCROLL);
invertAbacusScrollDirection = builder.comment(
"Whether scrolling up (as opposed to down) will increase the value of the abacus, and vice versa")
.define("invertAbacusScrollDirection", DEFAULT_INVERT_ABACUS_SCROLL);
"Whether scrolling up (as opposed to down) will increase the value of the abacus, and vice versa")
.define("invertAbacusScrollDirection", DEFAULT_INVERT_ABACUS_SCROLL);
gridSnapThreshold = builder.comment(
"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.")
.defineInRange("gridSnapThreshold", DEFAULT_GRID_SNAP_THRESHOLD, 0.5, 1.0);
"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.")
.defineInRange("gridSnapThreshold", DEFAULT_GRID_SNAP_THRESHOLD, 0.5, 1.0);
}

@Override
Expand Down Expand Up @@ -144,38 +144,38 @@ public static class Server implements HexConfig.ServerConfigAccess {
public Server(ForgeConfigSpec.Builder builder) {
builder.push("Spells");
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);
"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."
"The harvest level of the Break Block spell.",
"0 = wood, 1 = stone, 2 = iron, 3 = diamond, 4 = netherite."
).defineInRange("opBreakHarvestLevel", DEFAULT_OP_BREAK_HARVEST_LEVEL, 0, 4);
builder.pop();

builder.push("Spell Circles");
maxSpellCircleLength = builder.comment("The maximum number of slates in a spell circle")
.defineInRange("maxSpellCircleLength", DEFAULT_MAX_SPELL_CIRCLE_LENGTH, 4, Integer.MAX_VALUE);
.defineInRange("maxSpellCircleLength", DEFAULT_MAX_SPELL_CIRCLE_LENGTH, 4, Integer.MAX_VALUE);

circleActionDenyList = builder.comment(
"Resource locations of disallowed actions within circles. Trying to cast one of these in a circle" +
" will result in a mishap. For example: hexcasting:get_caster will prevent Mind's Reflection.")
.defineList("circleActionDenyList", List.of(), Server::isValidReslocArg);
"Resource locations of disallowed actions within circles. Trying to cast one of these in a circle" +
" will result in a mishap. For example: hexcasting:get_caster will prevent Mind's Reflection.")
.defineList("circleActionDenyList", List.of(), Server::isValidReslocArg);
builder.pop();

actionDenyList = builder.comment(
"Resource locations of disallowed actions. Trying to cast one of these will result in a mishap.")
.defineList("actionDenyList", List.of(), Server::isValidReslocArg);
"Resource locations of disallowed actions. Trying to cast one of these will result in a mishap.")
.defineList("actionDenyList", List.of(), Server::isValidReslocArg);

villagersOffendedByMindMurder = builder.comment(
"Should villagers take offense when you flay the mind of their fellow villagers?")
.define("villagersOffendedByMindMurder", true);
"Should villagers take offense when you flay the mind of their fellow villagers?")
.define("villagersOffendedByMindMurder", true);

tpDimDenyList = builder.comment("Resource locations of dimensions you can't Blink or Greater Teleport in.")
.defineList("tpDimDenyList", DEFAULT_DIM_TP_DENYLIST, Server::isValidReslocArg);
.defineList("tpDimDenyList", DEFAULT_DIM_TP_DENYLIST, Server::isValidReslocArg);

doesTrueNameHaveAmbit = builder.comment(
"when false makes player reference iotas behave as normal entity reference iotas")
.define("doesTrueNameHaveAmbit", DEFAULT_TRUE_NAME_HAS_AMBIT);
"when false makes player reference iotas behave as normal entity reference iotas")
.define("doesTrueNameHaveAmbit", DEFAULT_TRUE_NAME_HAS_AMBIT);
}

@Override
Expand Down Expand Up @@ -222,4 +222,4 @@ private static boolean isValidReslocArg(Object o) {
return o instanceof String s && ResourceLocation.isValidResourceLocation(s);
}
}
}
}
Loading