Make it so that the code to execute patterns is contained in PatternIota, enabling other Iotas to be executable if we want.

This commit is contained in:
Talia-12 2023-04-04 21:30:42 +10:00
parent 8396c90c5f
commit e7cac1e30d
3 changed files with 131 additions and 112 deletions

View file

@ -1,14 +1,10 @@
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.PatternShapeMatch
import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.*
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
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.SpellContinuation
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.casting.iota.ListIota
@ -16,14 +12,9 @@ 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
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
/**
@ -109,22 +100,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
)
}
if (iota is PatternIota) {
return executePattern(iota.pattern, world, continuation)
} else {
return CastResult(
continuation,
null,
listOf(
OperatorSideEffect.DoMishap(
MishapUnescapedValue(iota),
Mishap.Context(HexPattern(HexDir.WEST), null)
)
), // Should never matter
ResolvedPatternType.INVALID,
HexEvalSounds.MISHAP
)
}
return iota.execute(this, world, continuation)
} catch (exception: Exception) {
// This means something very bad has happened
exception.printStackTrace()
@ -146,92 +122,6 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
}
}
/**
* When the server gets a packet from the client with a new pattern,
* handle it functionally.
*/
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)
val action = if (lookup is Normal || lookup is PerWorld) {
val key = when (lookup) {
is Normal -> lookup.key
is PerWorld -> lookup.key
else -> throw IllegalStateException()
}
val reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.REQUIRES_ENLIGHTENMENT)
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment)
IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!.action
} else if (lookup is Special) {
castedName = lookup.handler.name
lookup.handler.act()
} else if (lookup is PatternShapeMatch.Nothing) {
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
var userData2: CompoundTag? = null
val result = action.operate(
this.env,
this.image.stack.toMutableList(),
this.image.userData.copy(),
continuation
)
cont2 = result.newContinuation
stack2 = result.newStack
userData2 = result.newUserdata
// TODO parens also break prescience
sideEffects.addAll(result.sideEffects)
val hereFd = this.image
val fd = if (stack2 != null) {
hereFd.copy(
stack = stack2,
userData = userData2,
)
} else {
hereFd
}
return CastResult(
cont2,
fd,
sideEffects,
ResolvedPatternType.EVALUATED,
env.soundType,
)
} catch (mishap: Mishap) {
return CastResult(
continuation,
null,
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, castedName))),
mishap.resolutionType(env),
HexEvalSounds.MISHAP
)
}
}
/**
* Execute the side effects of a pattern, updating our aggregated info.
*/

View file

@ -1,10 +1,23 @@
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.casting.eval.CastResult;
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation;
import at.petrak.hexcasting.api.casting.math.HexDir;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.casting.mishaps.MishapUnescapedValue;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class Iota {
@NotNull
protected final Object payload;
@ -34,6 +47,25 @@ public abstract class Iota {
*/
abstract public @NotNull Tag serialize();
/**
* This method is called when this iota is executed (i.e. Hermes is run on a list containing it, unescaped).
* By default it will return a {@link CastResult} indicating an error has occurred.
*/
public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) {
return new CastResult(
continuation,
null,
List.of(
new OperatorSideEffect.DoMishap(
new MishapUnescapedValue(this),
new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null)
)
), // Should never matter
ResolvedPatternType.INVALID,
HexEvalSounds.MISHAP
);
}
public Component display() {
return this.type.display(this.serialize());
}

View file

@ -1,16 +1,40 @@
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.PatternShapeMatch;
import at.petrak.hexcasting.api.casting.castables.Action;
import at.petrak.hexcasting.api.casting.eval.CastResult;
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooMuch;
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidPattern;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static at.petrak.hexcasting.api.utils.HexUtils.isOfTag;
public class PatternIota extends Iota {
public PatternIota(@NotNull HexPattern pattern) {
super(HexIotaTypes.PATTERN, pattern);
@ -41,9 +65,82 @@ public class PatternIota extends Iota {
return this.getPattern().serializeToNBT();
}
@Override
public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) {
@Nullable Component castedName = null;
try {
var lookup = PatternRegistryManifest.matchPattern(this.getPattern(), world, false);
vm.getEnv().precheckAction(lookup);
Action action;
if (lookup instanceof PatternShapeMatch.Normal || lookup instanceof PatternShapeMatch.PerWorld) {
ResourceKey<ActionRegistryEntry> key;
if (lookup instanceof PatternShapeMatch.Normal normal) {
key = normal.key;
} else {
PatternShapeMatch.PerWorld perWorld = (PatternShapeMatch.PerWorld) lookup;
key = perWorld.key;
}
var reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), key, HexTags.Actions.REQUIRES_ENLIGHTENMENT);
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment);
action = Objects.requireNonNull(IXplatAbstractions.INSTANCE.getActionRegistry().get(key)).action();
} else if (lookup instanceof PatternShapeMatch.Special special) {
castedName = special.handler.getName();
action = special.handler.act();
} else if (lookup instanceof PatternShapeMatch.Nothing) {
throw new MishapInvalidPattern();
} else throw new IllegalStateException();
var opCount = 0;
if (vm.getImage().getUserData().contains(HexAPI.OP_COUNT_USERDATA)) {
opCount = vm.getImage().getUserData().getInt(HexAPI.OP_COUNT_USERDATA);
} else
vm.getImage().getUserData().putInt(HexAPI.OP_COUNT_USERDATA, 0);
if (opCount + 1 > HexConfig.server().maxOpCount()) {
throw new MishapEvalTooMuch();
}
vm.getImage().getUserData().putInt(HexAPI.OP_COUNT_USERDATA, opCount + 1);
var result = action.operate(
vm.getEnv(),
new ArrayList<>(vm.getImage().getStack()),
vm.getImage().getUserData().copy(),
continuation
);
var cont2 = result.getNewContinuation();
var stack2 = result.getNewStack();
var userData2 = result.getNewUserdata();
// TODO parens also break prescience
var sideEffects = result.getSideEffects();
var hereFd = vm.getImage();
hereFd = hereFd.copy(stack2, hereFd.getParenCount(), hereFd.getParenthesized(), hereFd.getEscapeNext(), userData2);
return new CastResult(
cont2,
hereFd,
sideEffects,
ResolvedPatternType.EVALUATED,
vm.getEnv().getSoundType()
);
} catch (Mishap mishap) {
return new CastResult(
continuation,
null,
List.of(new OperatorSideEffect.DoMishap(mishap, new Mishap.Context(this.getPattern(), castedName))),
mishap.resolutionType(vm.getEnv()),
HexEvalSounds.MISHAP
);
}
}
public static IotaType<PatternIota> TYPE = new IotaType<>() {
@Nullable
@Override
public PatternIota deserialize(Tag tag, ServerLevel world) throws IllegalArgumentException {
return PatternIota.deserialize(tag);