aaaaaugh. also document things
This commit is contained in:
parent
117fbfb781
commit
24367669e5
39 changed files with 483 additions and 515 deletions
|
@ -2,7 +2,7 @@ Hello, intrepid Github reader!
|
|||
|
||||
The "flavor text" words for things in this mod and the internal names are different. (Sorry.)
|
||||
|
||||
- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/casting/eval/CastingHarness.kt)
|
||||
- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/casting/eval/vm/CastingVM.kt)
|
||||
- A "Pattern" is a [`HexPattern`](api/casting/math/HexPattern.kt)
|
||||
- An "Action" is an [`Operator`](api/casting/castables/Action.kt)
|
||||
- An action that pushes a spell is a [`Spell`](api/casting/castables/SpellAction.kt)
|
||||
|
|
|
@ -108,6 +108,11 @@ public interface HexAPI {
|
|||
return FrozenColorizer.DEFAULT.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Location in the userdata of the ravenmind
|
||||
*/
|
||||
ResourceLocation RAVENMIND_USERDATA = modLoc("ravenmind");
|
||||
|
||||
static HexAPI instance() {
|
||||
return INSTANCE.get();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.api.addldata;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -14,7 +14,7 @@ public interface ADIotaHolder {
|
|||
default Iota readIota(ServerLevel world) {
|
||||
var tag = readIotaTag();
|
||||
if (tag != null) {
|
||||
return HexIotaTypes.deserialize(tag, world);
|
||||
return IotaType.deserialize(tag, world);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.misc.MediaConstants;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
|
||||
import at.petrak.hexcasting.api.casting.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.utils.MediaHelper;
|
||||
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
|
@ -284,7 +284,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
|
||||
var ctx = new CastingEnvironment(splayer, InteractionHand.MAIN_HAND,
|
||||
new SpellCircleContext(this.getBlockPos(), bounds, this.activatorAlwaysInRange()));
|
||||
var harness = new CastingHarness(ctx);
|
||||
var harness = new CastingVM(ctx);
|
||||
|
||||
BlockPos erroredPos = null;
|
||||
for (var tracked : this.trackedBlocks) {
|
||||
|
@ -292,7 +292,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
if (bs.getBlock() instanceof BlockCircleComponent cc) {
|
||||
var newPattern = cc.getPattern(tracked, bs, this.level);
|
||||
if (newPattern != null) {
|
||||
var info = harness.executeIota(new PatternIota(newPattern), splayer.getLevel());
|
||||
var info = harness.queueAndExecuteIota(new PatternIota(newPattern), splayer.getLevel());
|
||||
if (!info.getResolutionType().getSuccess()) {
|
||||
erroredPos = tracked;
|
||||
break;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.api.casting.eval;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.PatternShapeMatch;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapDisallowedSpell;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway;
|
||||
|
@ -9,7 +9,6 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapLocationTooFarAway;
|
|||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
@ -57,7 +56,7 @@ public abstract class CastingEnvironment {
|
|||
* <p>
|
||||
* This is used for stuff like requiring enlightenment and pattern denylists
|
||||
*/
|
||||
public void precheckAction(ResourceKey<ActionRegistryEntry> key) throws Mishap {
|
||||
public void precheckAction(PatternShapeMatch match) throws Mishap {
|
||||
if (!HexConfig.server().isActionAllowed(key.location())) {
|
||||
throw new MishapDisallowedSpell();
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package at.petrak.hexcasting.api.casting.eval
|
||||
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
|
||||
/**
|
||||
* Information for the sake of the GUI.
|
||||
*/
|
||||
data class ControllerInfo(
|
||||
val isStackClear: Boolean,
|
||||
val resolutionType: ResolvedPatternType,
|
||||
val stack: List<CompoundTag>,
|
||||
val parenthesized: List<CompoundTag>,
|
||||
val ravenmind: CompoundTag?,
|
||||
val parenCount: Int,
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package at.petrak.hexcasting.api.casting.eval
|
||||
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
/**
|
||||
* Information sent back to the client
|
||||
*/
|
||||
data class ExecutionClientView(
|
||||
val isStackClear: Boolean,
|
||||
val resolutionType: ResolvedPatternType,
|
||||
|
||||
val stackDescs: List<Component>,
|
||||
val ravenmind: Component?,
|
||||
)
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# How Casting Works
|
||||
|
||||
because I keep forgetting.
|
||||
|
||||
- [`CastingVM`][] is the casting virtual machine. It is what figures out what to do with an incoming iota to be
|
||||
executed,
|
||||
producing a functional state update. This is transient and is reconstructed every time the whole state needs to be
|
||||
saved and loaded.
|
||||
- [`CastingImage`][] is the state of the cast itself. If [`CastingVM`][] is an emulator, [`CastingImage`][] is a
|
||||
snapshot of the
|
||||
emulator's memory. This is the only thing serialized to NBT.
|
||||
- [`CastingEnvironment`][] is what is doing the casting, abstractly. Stuff like "the player with a staff," "the player
|
||||
with a trinket," "a spell circle." This is an abstract class that Hexcasting (and addons!) make subclasses of.
|
||||
|
||||
## Beware The Pipeline
|
||||
|
||||
1. An iota or list of iotas come to be executed. This is the entrypoint to the VM,
|
||||
`CastingVM#queueAndExecuteIotas`, and returns an [`ExecutionClientView`][] (might change later).
|
||||
2. Those iotas are put into a [`ContinuationFrame`][] (specifically, [`FrameEvaluate`][]).
|
||||
3. While there are still frames, control flows to the top one.
|
||||
4. The frame (usually) returns control flow back to the VM by calling `CastingVM#executeInner` with an iota.
|
||||
5. `executeInner` first does some pre-checks with intro/retro/consideration (like escaping embedded iotas), but usually
|
||||
makes sure that the passed iota is a pattern and passes *that* on to `executePattern`.
|
||||
6. `executePattern` is where the execution actually happens. The pattern is matched to an action or special handler.
|
||||
and executed.
|
||||
7. That execution doesn't mutate anything, but returns a [`CastResult`]. It contains:
|
||||
- the rest of the current continuation (read as: the patterns to execute next);
|
||||
- the updated state of the [`CastingImage`];
|
||||
- a list of `OperatorSideEffects`, like withdrawing media, casting spells or mishaping;
|
||||
- misc display info like the color the pattern should be when drawn to the staff GUI and the sound.
|
||||
8. Each of the side effects is applied to the world in turn. If any of them make the casting stop (like mishaping),
|
||||
then it does!
|
||||
9. If there's still iotas left in the current continuation, then control goes back to step 5, called on the next iota in
|
||||
continuation.
|
||||
10. Otherwise, control goes to the top frame of the stack (step 3). And if there are no stack frames, execution is
|
||||
finished! What a ride.
|
||||
|
||||
## The Hell's A Continuation And A Continuation Frame
|
||||
|
||||
IDFK ask Alwinfy.
|
||||
|
||||
But as I understand it, a [continuation frame][ContinuationFrame] tells the VM what to do *next*.
|
||||
|
||||
While there are frames left on the stack (not the normal stack, a special execution stack), it is popped and
|
||||
queried. It will then operate the VM into executing a [continuation][Continuation] in a certain way.
|
||||
|
||||
Continuations are just a linked list of iotas to execute. The VM goes through each one and executes them.
|
||||
|
||||
- For staffcasting, each pattern drawn spins up the VM with a `FrameEvaluate`, which will provide exactly one
|
||||
continuation containing the pattern.
|
||||
- For trinket casting, the VM gets a `FrameEvaluate` again, but the continuation list it provides has
|
||||
all the patterns in the trinket.
|
||||
- Hermes' Gambit pushes a new `FrameEvaluate` to the stack with the pattern list argument inside it. This doesn't clear
|
||||
the continuation underneath, so once those patterns are through execution control goes right back to where it was
|
||||
interrupted.
|
||||
- Thoth's and Charon's do something with different kinds of stack frames, I don't really understand it.
|
||||
- Iris' gambit is waaaay outside of my pay grade
|
||||
|
||||
[CastingVM]: vm/CastingVM.kt
|
||||
|
||||
[CastingImage]: vm/CastingImage.kt
|
||||
|
||||
[CastingEnvironment]: CastingEnvironment.java
|
||||
|
||||
[ExecutionClientView]: ExecutionClientView.kt
|
||||
|
||||
[ContinuationFrame]: vm/ContinuationFrame.kt
|
||||
|
||||
[Continuation]: vm/SpellContinuation.kt
|
||||
|
||||
[FrameEvaluate]: vm/FrameEvaluate.kt
|
||||
|
||||
[CastResult]: CastResult.kt
|
||||
|
|
@ -3,7 +3,7 @@ package at.petrak.hexcasting.api.casting.eval.sideeffects
|
|||
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray
|
||||
import at.petrak.hexcasting.api.casting.RenderedSpell
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM
|
||||
import at.petrak.hexcasting.api.casting.mishaps.Mishap
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.mod.HexStatistics
|
||||
|
@ -18,10 +18,10 @@ import net.minecraft.world.item.ItemStack
|
|||
*/
|
||||
sealed class OperatorSideEffect {
|
||||
/** Return whether to cancel all further [OperatorSideEffect] */
|
||||
abstract fun performEffect(harness: CastingHarness): Boolean
|
||||
abstract fun performEffect(harness: CastingVM): Boolean
|
||||
|
||||
data class RequiredEnlightenment(val awardStat: Boolean) : OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
override fun performEffect(harness: CastingVM): Boolean {
|
||||
harness.ctx.caster?.sendSystemMessage("hexcasting.message.cant_great_spell".asTranslatedComponent)
|
||||
|
||||
if (awardStat)
|
||||
|
@ -38,7 +38,7 @@ sealed class OperatorSideEffect {
|
|||
val awardStat: Boolean = true
|
||||
) :
|
||||
OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
override fun performEffect(harness: CastingVM): Boolean {
|
||||
this.spell.cast(harness.ctx)
|
||||
if (awardStat)
|
||||
harness.ctx.caster?.awardStat(HexStatistics.SPELLS_CAST)
|
||||
|
@ -48,14 +48,14 @@ sealed class OperatorSideEffect {
|
|||
}
|
||||
|
||||
data class ConsumeMedia(val amount: Int) : OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
override fun performEffect(harness: CastingVM): Boolean {
|
||||
val leftoverMedia = harness.ctx.extractMedia(this.amount.toLong())
|
||||
return leftoverMedia > 0
|
||||
}
|
||||
}
|
||||
|
||||
data class Particles(val spray: ParticleSpray) : OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
override fun performEffect(harness: CastingVM): Boolean {
|
||||
harness.ctx.produceParticles(this.spray, harness.ctx.colorizer)
|
||||
this.spray.sprayParticles(harness.ctx.world, harness.getColorizer())
|
||||
|
||||
|
@ -64,7 +64,7 @@ sealed class OperatorSideEffect {
|
|||
}
|
||||
|
||||
data class DoMishap(val mishap: Mishap, val errorCtx: Mishap.Context) : OperatorSideEffect() {
|
||||
override fun performEffect(harness: CastingHarness): Boolean {
|
||||
override fun performEffect(harness: CastingVM): Boolean {
|
||||
val spray = mishap.particleSpray(harness.ctx)
|
||||
val color = mishap.accentColor(harness.ctx, errorCtx)
|
||||
spray.sprayParticles(harness.ctx.world, color)
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
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.utils.*
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
/**
|
||||
* The state of a casting VM, containing the stack and all
|
||||
*/
|
||||
data class CastingImage private constructor(
|
||||
val stack: List<Iota>,
|
||||
|
||||
val parenCount: Int,
|
||||
val parenthesized: List<Iota>,
|
||||
val escapeNext: Boolean,
|
||||
|
||||
val userData: CompoundTag
|
||||
) {
|
||||
public constructor() : this(listOf(), 0, listOf(), false, CompoundTag())
|
||||
|
||||
fun serializeToNbt() = NBTBuilder {
|
||||
TAG_STACK %= stack.serializeToNBT()
|
||||
|
||||
TAG_PAREN_COUNT %= parenCount
|
||||
TAG_ESCAPE_NEXT %= escapeNext
|
||||
TAG_PARENTHESIZED %= parenthesized.serializeToNBT()
|
||||
|
||||
TAG_USERDATA %= userData
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG_STACK = "stack"
|
||||
const val TAG_PAREN_COUNT = "open_parens"
|
||||
const val TAG_PARENTHESIZED = "parenthesized"
|
||||
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||
const val TAG_USERDATA = "userdata"
|
||||
|
||||
@JvmStatic
|
||||
public fun loadFromNbt(tag: CompoundTag, world: ServerLevel): CastingImage {
|
||||
return try {
|
||||
val stack = mutableListOf<Iota>()
|
||||
val stackTag = tag.getList(TAG_STACK, Tag.TAG_COMPOUND)
|
||||
for (subtag in stackTag) {
|
||||
val datum = IotaType.deserialize(subtag.asCompound, world)
|
||||
stack.add(datum)
|
||||
}
|
||||
|
||||
val userData = if (tag.contains(TAG_USERDATA)) {
|
||||
tag.getCompound(TAG_USERDATA)
|
||||
} else if (tag.contains("local")) {
|
||||
NBTBuilder {
|
||||
TAG_USERDATA %= tag.getCompound("local")
|
||||
}
|
||||
} else {
|
||||
CompoundTag()
|
||||
}
|
||||
|
||||
val parenthesized = mutableListOf<Iota>()
|
||||
val parenTag = tag.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
|
||||
for (subtag in parenTag) {
|
||||
parenthesized.add(IotaType.deserialize(subtag.downcast(CompoundTag.TYPE), world))
|
||||
}
|
||||
|
||||
val parenCount = tag.getInt(CastingVM.TAG_PAREN_COUNT)
|
||||
val escapeNext = tag.getBoolean(CastingVM.TAG_ESCAPE_NEXT)
|
||||
|
||||
CastingImage(stack, parenCount, parenthesized, escapeNext, userData)
|
||||
} catch (exn: Exception) {
|
||||
HexAPI.LOGGER.warn("error while loading a CastingImage", exn)
|
||||
CastingImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,116 +1,64 @@
|
|||
package at.petrak.hexcasting.api.casting.eval
|
||||
package at.petrak.hexcasting.api.casting.eval.vm
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray
|
||||
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.castables.Action
|
||||
import at.petrak.hexcasting.api.casting.eval.*
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.FunctionalData
|
||||
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
|
||||
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.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
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.common.lib.hex.HexIotaTypes
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Keeps track of a player casting a spell on the server.
|
||||
* It's stored as NBT on the player.
|
||||
*
|
||||
* TODO oh god this entire class needs a gigantic refactor. why are there like 6 different entrypoints for casting
|
||||
* a pattern. oh god.
|
||||
* The virtual machine! This is the glue that determines the next iteration of a [CastingImage], using a
|
||||
* [CastingEnvironment] to affect the world.
|
||||
*/
|
||||
class CastingHarness private constructor(
|
||||
var stack: MutableList<Iota>,
|
||||
var ravenmind: Iota?,
|
||||
var parenCount: Int,
|
||||
var parenthesized: List<Iota>,
|
||||
var escapeNext: Boolean,
|
||||
val ctx: CastingEnvironment,
|
||||
val prepackagedColorizer: FrozenColorizer? // for trinkets with colorizers
|
||||
) {
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
ctx: CastingEnvironment,
|
||||
prepackagedColorizer: FrozenColorizer? = null
|
||||
) : this(mutableListOf(), null, 0, mutableListOf(), false, ctx, prepackagedColorizer)
|
||||
class CastingVM(val image: CastingImage, val env: CastingEnvironment) {
|
||||
|
||||
/**
|
||||
* Execute a single iota.
|
||||
*/
|
||||
fun executeIota(iota: Iota, world: ServerLevel): ControllerInfo = executeIotas(listOf(iota), world)
|
||||
|
||||
private fun displayPatternDebug(escapeNext: Boolean, parenCount: Int, iotaRepresentation: Component) {
|
||||
if (this.ctx.debugPatterns) {
|
||||
val display = " ".repeat(parenCount).asTextComponent
|
||||
if (escapeNext)
|
||||
display.append("\\ ".asTextComponent.gold)
|
||||
display.append(iotaRepresentation)
|
||||
|
||||
this.ctx.caster.sendSystemMessage(display)
|
||||
}
|
||||
}
|
||||
fun queueAndExecuteIota(iota: Iota, world: ServerLevel): ExecutionClientView = queueAndExecuteIotas(listOf(iota), world)
|
||||
|
||||
/**
|
||||
* Given a list of iotas, execute them in sequence.
|
||||
* The main entrypoint to the VM. Given a list of iotas, execute them in sequence, and return whatever the client
|
||||
* needs to see.
|
||||
*/
|
||||
fun executeIotas(iotas: List<Iota>, world: ServerLevel): ControllerInfo {
|
||||
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
|
||||
val info = TempControllerInfo(earlyExit = false)
|
||||
var lastResolutionType = ResolvedPatternType.UNRESOLVED
|
||||
var sound = HexEvalSounds.NOTHING
|
||||
while (continuation is SpellContinuation.NotDone && !info.earlyExit) {
|
||||
// Take the top of the continuation stack...
|
||||
val next = continuation.frame
|
||||
// ...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 result = next.evaluate(continuation.next, world, this)
|
||||
val image2 = next.evaluate(continuation.next, world, this)
|
||||
// Then write all pertinent data back to the harness for the next iteration.
|
||||
if (result.newData != null) {
|
||||
this.applyFunctionalData(result.newData)
|
||||
if (image2.newData != null) {
|
||||
this.applyFunctionalData(image2.newData)
|
||||
}
|
||||
continuation = result.continuation
|
||||
lastResolutionType = result.resolutionType
|
||||
performSideEffects(info, result.sideEffects)
|
||||
info.earlyExit = info.earlyExit || !lastResolutionType.success
|
||||
sound = if (result.sound == HexEvalSounds.MISHAP) {
|
||||
HexEvalSounds.MISHAP
|
||||
} else {
|
||||
sound.greaterOf(result.sound)
|
||||
}
|
||||
}
|
||||
this.env.postExecution(image2)
|
||||
|
||||
sound.sound?.let {
|
||||
this.ctx.world.playSound(
|
||||
null, this.ctx.position.x, this.ctx.position.y, this.ctx.position.z, it,
|
||||
SoundSource.PLAYERS, 1f, 1f
|
||||
)
|
||||
// TODO: is it worth mixing in to the immut map and making our own game event with blackjack and hookers
|
||||
this.ctx.world.gameEvent(this.ctx.caster, GameEvent.ITEM_INTERACT_FINISH, this.ctx.position)
|
||||
continuation = image2.continuation
|
||||
lastResolutionType = image2.resolutionType
|
||||
performSideEffects(info, image2.sideEffects)
|
||||
info.earlyExit = info.earlyExit || !lastResolutionType.success
|
||||
}
|
||||
|
||||
if (continuation is SpellContinuation.NotDone) {
|
||||
|
@ -118,29 +66,23 @@ class CastingHarness private constructor(
|
|||
if (lastResolutionType.success) ResolvedPatternType.EVALUATED else ResolvedPatternType.ERRORED
|
||||
}
|
||||
|
||||
val (stackDescs, parenDescs, ravenmind) = generateDescs()
|
||||
val (stackDescs, ravenmind) = generateDescs()
|
||||
|
||||
return ControllerInfo(
|
||||
this.stack.isEmpty() && this.parenCount == 0 && !this.escapeNext,
|
||||
lastResolutionType,
|
||||
stackDescs,
|
||||
parenDescs,
|
||||
ravenmind,
|
||||
this.parenCount
|
||||
)
|
||||
val isStackClear = image.stack.isEmpty() && image.parenCount == 0 && !image.escapeNext
|
||||
return ExecutionClientView(isStackClear, lastResolutionType, stackDescs, ravenmind)
|
||||
}
|
||||
|
||||
/**
|
||||
* this DOES NOT THROW THINGS
|
||||
*/
|
||||
@Throws()
|
||||
fun getUpdate(iota: Iota, world: ServerLevel, continuation: SpellContinuation): CastResult {
|
||||
fun executeInner(iota: Iota, world: ServerLevel, continuation: SpellContinuation): CastResult {
|
||||
try {
|
||||
// TODO we can have a special intro/retro sound
|
||||
// ALSO TODO need to add reader macro-style things
|
||||
try {
|
||||
this.handleParentheses(iota)?.let { (data, resolutionType) ->
|
||||
return@getUpdate CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
|
||||
return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
|
||||
}
|
||||
} catch (e: MishapTooManyCloseParens) {
|
||||
// This is ridiculous and needs to be fixed
|
||||
|
@ -162,7 +104,7 @@ class CastingHarness private constructor(
|
|||
}
|
||||
|
||||
if (iota is PatternIota) {
|
||||
return updateWithPattern(iota.pattern, world, continuation)
|
||||
return executePattern(iota.pattern, world, continuation)
|
||||
} else {
|
||||
return CastResult(
|
||||
continuation,
|
||||
|
@ -202,11 +144,13 @@ class CastingHarness private constructor(
|
|||
* When the server gets a packet from the client with a new pattern,
|
||||
* handle it functionally.
|
||||
*/
|
||||
private fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
|
||||
private fun executePattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
|
||||
var castedName: Component? = null
|
||||
try {
|
||||
val lookup = PatternRegistryManifest.matchPattern(newPat, world, false)
|
||||
val lookupResult: Either<Action, List<OperatorSideEffect>> = if (lookup is Normal || lookup is PerWorld) {
|
||||
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
|
||||
|
@ -214,63 +158,33 @@ class CastingHarness private constructor(
|
|||
}
|
||||
|
||||
val reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.REQUIRES_ENLIGHTENMENT)
|
||||
val canEnlighten = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.CAN_START_ENLIGHTEN)
|
||||
|
||||
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment)
|
||||
|
||||
if (!ctx.isCasterEnlightened && reqsEnlightenment) {
|
||||
Either.right(listOf(OperatorSideEffect.RequiredEnlightenment(canEnlighten)))
|
||||
} else {
|
||||
val regiEntry = IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!
|
||||
Either.left(regiEntry.action)
|
||||
}
|
||||
IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!.action
|
||||
} else if (lookup is Special) {
|
||||
castedName = lookup.handler.name
|
||||
Either.left(lookup.handler.act())
|
||||
lookup.handler.act()
|
||||
} else if (lookup is PatternShapeMatch.Nothing) {
|
||||
throw MishapInvalidPattern()
|
||||
} else {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
// TODO: the config denylist should be handled per VM type.
|
||||
// I just removed it for now, should re-add it...
|
||||
} else throw IllegalStateException()
|
||||
|
||||
val sideEffects = mutableListOf<OperatorSideEffect>()
|
||||
var stack2: List<Iota>? = null
|
||||
var cont2 = continuation
|
||||
var ravenmind2: Iota? = null
|
||||
|
||||
if (lookupResult.left().isPresent) {
|
||||
val action = lookupResult.left().get()
|
||||
displayPatternDebug(false, 0, castedName)
|
||||
val result = action.operate(
|
||||
continuation,
|
||||
this.stack.toMutableList(),
|
||||
this.ravenmind,
|
||||
this.ctx
|
||||
)
|
||||
cont2 = result.newContinuation
|
||||
stack2 = result.newStack
|
||||
ravenmind2 = result.newRavenmind
|
||||
// TODO parens also break prescience
|
||||
sideEffects.addAll(result.sideEffects)
|
||||
} else {
|
||||
val problems = lookupResult.right().get()
|
||||
sideEffects.addAll(problems)
|
||||
}
|
||||
|
||||
// Stick a poofy particle effect at the caster position
|
||||
// TODO again this should be on the VM lalala
|
||||
if (this.ctx.spellCircle == null)
|
||||
sideEffects.add(
|
||||
OperatorSideEffect.Particles(
|
||||
ParticleSpray(
|
||||
this.ctx.position,
|
||||
Vec3(0.0, 1.0, 0.0),
|
||||
0.5, 1.0
|
||||
)
|
||||
)
|
||||
)
|
||||
val result = action.operate(
|
||||
continuation,
|
||||
this.stack.toMutableList(),
|
||||
this.ravenmind,
|
||||
this.ctx
|
||||
)
|
||||
cont2 = result.newContinuation
|
||||
stack2 = result.newStack
|
||||
ravenmind2 = result.newRavenmind
|
||||
// TODO parens also break prescience
|
||||
sideEffects.addAll(result.sideEffects)
|
||||
|
||||
val hereFd = this.getFunctionalData()
|
||||
val fd = if (stack2 != null) {
|
||||
|
@ -282,25 +196,6 @@ class CastingHarness private constructor(
|
|||
hereFd
|
||||
}
|
||||
|
||||
// TODO again this should be per VM
|
||||
var soundType = if (this.ctx.source == CastingEnvironment.CastSource.STAFF) {
|
||||
HexEvalSounds.OPERATOR
|
||||
} else {
|
||||
HexEvalSounds.NOTHING
|
||||
}
|
||||
for (se in sideEffects) {
|
||||
if (se is OperatorSideEffect.AttemptSpell) {
|
||||
soundType = if (se.hasCastingSound) {
|
||||
soundType.greaterOf(HexEvalSounds.SPELL)
|
||||
} else {
|
||||
// WITH CATLIKE TREAD
|
||||
// UPON OUR PREY WE STEAL
|
||||
HexEvalSounds.NOTHING
|
||||
}
|
||||
} else if (se is OperatorSideEffect.DoMishap) {
|
||||
soundType = HexEvalSounds.MISHAP
|
||||
}
|
||||
}
|
||||
return CastResult(
|
||||
cont2,
|
||||
fd,
|
||||
|
@ -333,11 +228,14 @@ class CastingHarness private constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun generateDescs() = Triple(
|
||||
stack.map(HexIotaTypes::serialize),
|
||||
parenthesized.map(HexIotaTypes::serialize),
|
||||
ravenmind?.let(HexIotaTypes::serialize)
|
||||
)
|
||||
fun generateDescs(): Pair<List<Component>, Component?> {
|
||||
val stackDescs = this.image.stack.map { it.display() }
|
||||
val ravenmind = if (this.image.userData.contains(HexAPI.RAVENMIND_USERDATA.toString())) {
|
||||
val tag = this.image.userData.getCompound(HexAPI.RAVENMIND_USERDATA.toString())
|
||||
IotaType.getDisplay(tag)
|
||||
} else null
|
||||
return Pair(stackDescs, ravenmind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the functional update represented by the current state (for use with `copy`)
|
||||
|
@ -478,92 +376,6 @@ class CastingHarness private constructor(
|
|||
return out
|
||||
}
|
||||
|
||||
fun getColorizer(): FrozenColorizer {
|
||||
if (this.prepackagedColorizer != null)
|
||||
return this.prepackagedColorizer
|
||||
|
||||
return IXplatAbstractions.INSTANCE.getColorizer(this.ctx.caster)
|
||||
}
|
||||
|
||||
|
||||
fun serializeToNBT() = NBTBuilder {
|
||||
TAG_STACK %= stack.serializeToNBT()
|
||||
|
||||
if (ravenmind != null)
|
||||
TAG_LOCAL %= HexIotaTypes.serialize(ravenmind!!)
|
||||
TAG_PAREN_COUNT %= parenCount
|
||||
TAG_ESCAPE_NEXT %= escapeNext
|
||||
|
||||
TAG_PARENTHESIZED %= parenthesized.serializeToNBT()
|
||||
|
||||
if (prepackagedColorizer != null)
|
||||
TAG_PREPACKAGED_COLORIZER %= prepackagedColorizer.serializeToNBT()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val TAG_STACK = "stack"
|
||||
const val TAG_LOCAL = "local"
|
||||
const val TAG_PAREN_COUNT = "open_parens"
|
||||
const val TAG_PARENTHESIZED = "parenthesized"
|
||||
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||
const val TAG_PREPACKAGED_COLORIZER = "prepackaged_colorizer"
|
||||
|
||||
init {
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.items
|
||||
.filter(::isMediaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
|
||||
}
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.armor
|
||||
.filter(::isMediaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
|
||||
}
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.offhand
|
||||
.filter(::isMediaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findMediaHolder)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromNBT(nbt: CompoundTag, ctx: CastingEnvironment): CastingHarness {
|
||||
return try {
|
||||
val stack = mutableListOf<Iota>()
|
||||
val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
|
||||
for (subtag in stackTag) {
|
||||
val datum = HexIotaTypes.deserialize(subtag.asCompound, ctx.world)
|
||||
stack.add(datum)
|
||||
}
|
||||
|
||||
val ravenmind = if (nbt.contains(TAG_LOCAL))
|
||||
HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
|
||||
else
|
||||
null
|
||||
|
||||
val parenthesized = mutableListOf<Iota>()
|
||||
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
|
||||
for (subtag in parenTag) {
|
||||
parenthesized.add(HexIotaTypes.deserialize(subtag.downcast(CompoundTag.TYPE), ctx.world))
|
||||
}
|
||||
|
||||
val parenCount = nbt.getInt(TAG_PAREN_COUNT)
|
||||
val escapeNext = nbt.getBoolean(TAG_ESCAPE_NEXT)
|
||||
|
||||
val colorizer = if (nbt.contains(TAG_PREPACKAGED_COLORIZER)) {
|
||||
FrozenColorizer.fromNBT(nbt.getCompound(TAG_PREPACKAGED_COLORIZER))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
CastingHarness(stack, ravenmind, parenCount, parenthesized, escapeNext, ctx, colorizer)
|
||||
} catch (exn: Exception) {
|
||||
CastingHarness(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class TempControllerInfo(
|
||||
var earlyExit: Boolean,
|
||||
)
|
|
@ -1,7 +1,6 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.vm
|
||||
|
||||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.utils.getList
|
||||
|
@ -31,7 +30,7 @@ sealed interface ContinuationFrame {
|
|||
* For Evaluate, this consumes one pattern; for ForEach this queues the next iteration of the outer loop.
|
||||
* @return the result of this pattern step
|
||||
*/
|
||||
fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingHarness): CastResult
|
||||
fun evaluate(continuation: SpellContinuation, level: ServerLevel, harness: CastingVM): CastResult
|
||||
|
||||
/**
|
||||
* The OpHalt instruction wants us to "jump to" the END of the nearest meta-eval.
|
||||
|
|
|
@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.casting.eval.vm
|
|||
|
||||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.utils.NBTBuilder
|
||||
|
@ -23,7 +22,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
|
|||
override fun evaluate(
|
||||
continuation: SpellContinuation,
|
||||
level: ServerLevel,
|
||||
harness: CastingHarness
|
||||
harness: CastingVM
|
||||
): CastResult {
|
||||
// If there are patterns left...
|
||||
return if (list.nonEmpty) {
|
||||
|
@ -32,7 +31,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
|
|||
continuation.pushFrame(FrameEvaluate(list.cdr, this.isMetacasting))
|
||||
} else continuation
|
||||
// ...before evaluating the first one in the list.
|
||||
val update = harness.getUpdate(list.car, level, newCont)
|
||||
val update = harness.executeInner(list.car, level, newCont)
|
||||
if (this.isMetacasting && update.sound != HexEvalSounds.MISHAP) {
|
||||
update.copy(sound = HexEvalSounds.HERMES)
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.vm
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.utils.NBTBuilder
|
||||
|
@ -20,7 +19,7 @@ object FrameFinishEval : ContinuationFrame {
|
|||
override fun evaluate(
|
||||
continuation: SpellContinuation,
|
||||
level: ServerLevel,
|
||||
harness: CastingHarness
|
||||
harness: CastingVM
|
||||
): CastResult {
|
||||
return CastResult(
|
||||
continuation,
|
||||
|
|
|
@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.casting.eval.vm
|
|||
|
||||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.iota.ListIota
|
||||
|
@ -39,7 +38,7 @@ data class FrameForEach(
|
|||
override fun evaluate(
|
||||
continuation: SpellContinuation,
|
||||
level: ServerLevel,
|
||||
harness: CastingHarness
|
||||
harness: CastingVM
|
||||
): CastResult {
|
||||
// If this isn't the very first Thoth step (i.e. no Thoth computations run yet)...
|
||||
val stack = if (baseStack == null) {
|
||||
|
|
|
@ -30,7 +30,7 @@ public abstract class Iota {
|
|||
/**
|
||||
* Serialize this under the {@code data} tag.
|
||||
* <p>
|
||||
* You probably don't want to call this directly; use {@link HexIotaTypes#serialize}.
|
||||
* You probably don't want to call this directly; use {@link IotaType#serialize}.
|
||||
*/
|
||||
abstract public @NotNull Tag serialize();
|
||||
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
package at.petrak.hexcasting.api.casting.iota;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
// Take notes from ForgeRegistryEntry
|
||||
public abstract class IotaType<T extends Iota> {
|
||||
|
||||
/**
|
||||
* Spell datums are stored as such: {@code { "type": "modid:type", "datum": a_tag }}.
|
||||
* <p>
|
||||
|
@ -41,4 +53,149 @@ public abstract class IotaType<T extends Iota> {
|
|||
return Component.translatable("hexcasting.iota." + key)
|
||||
.withStyle(style -> style.withColor(TextColor.fromRgb(color())));
|
||||
}
|
||||
|
||||
public static CompoundTag serialize(Iota iota) {
|
||||
var type = iota.getType();
|
||||
var typeId = HexIotaTypes.REGISTRY.getKey(type);
|
||||
if (typeId == null) {
|
||||
throw new IllegalStateException(
|
||||
"Tried to serialize an unregistered iota type. Iota: " + iota
|
||||
+ " ; Type" + type.getClass().getTypeName());
|
||||
}
|
||||
|
||||
// We check if it's too big on serialization; if it is we just return a garbage.
|
||||
if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
|
||||
// Garbage will never be too large so we just recurse
|
||||
return serialize(new GarbageIota());
|
||||
}
|
||||
var dataTag = iota.serialize();
|
||||
var out = new CompoundTag();
|
||||
out.putString(HexIotaTypes.KEY_TYPE, typeId.toString());
|
||||
out.put(HexIotaTypes.KEY_DATA, dataTag);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static boolean isTooLargeToSerialize(Iterable<Iota> examinee) {
|
||||
// We don't recurse here, just a work queue (or work stack, if we liked.)
|
||||
// Each element is a found sub-iota, and how deep it is.
|
||||
//
|
||||
// TODO: is it worth trying to cache the depth and size statically on a SpellList.
|
||||
var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
|
||||
int totalEltsFound = 1; // count the first list
|
||||
while (!listsToExamine.isEmpty()) {
|
||||
var iotaPair = listsToExamine.removeFirst();
|
||||
var sublist = iotaPair.getFirst();
|
||||
int depth = iotaPair.getSecond();
|
||||
for (var iota : sublist) {
|
||||
totalEltsFound++;
|
||||
if (totalEltsFound >= HexIotaTypes.MAX_SERIALIZATION_TOTAL) {
|
||||
return true; // too bad
|
||||
}
|
||||
if (iota instanceof ListIota subsublist) {
|
||||
if (depth + 1 >= HexIotaTypes.MAX_SERIALIZATION_DEPTH) {
|
||||
return true;
|
||||
}
|
||||
listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
// we made it!
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method attempts to find the type from the {@code type} key.
|
||||
* See {@link HexIotaTypes#serialize(Iota)} for the storage format.
|
||||
*
|
||||
* @return {@code null} if it cannot get the type.
|
||||
*/
|
||||
@org.jetbrains.annotations.Nullable
|
||||
public static IotaType<?> getTypeFromTag(CompoundTag tag) {
|
||||
if (!tag.contains(HexIotaTypes.KEY_TYPE, Tag.TAG_STRING)) {
|
||||
return null;
|
||||
}
|
||||
var typeKey = tag.getString(HexIotaTypes.KEY_TYPE);
|
||||
if (!ResourceLocation.isValidResourceLocation(typeKey)) {
|
||||
return null;
|
||||
}
|
||||
var typeLoc = new ResourceLocation(typeKey);
|
||||
return HexIotaTypes.REGISTRY.get(typeLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to deserialize an iota from a tag.
|
||||
* <br>
|
||||
* Iotas are saved as such:
|
||||
* <code>
|
||||
* {
|
||||
* "type": "hexcasting:atype",
|
||||
* "data": {...}
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
public static Iota deserialize(CompoundTag tag, ServerLevel world) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return new GarbageIota();
|
||||
}
|
||||
var data = tag.get(HexIotaTypes.KEY_DATA);
|
||||
if (data == null) {
|
||||
return new GarbageIota();
|
||||
}
|
||||
Iota deserialized;
|
||||
try {
|
||||
deserialized = Objects.requireNonNullElse(type.deserialize(data, world), new NullIota());
|
||||
} catch (IllegalArgumentException exn) {
|
||||
HexAPI.LOGGER.warn("Caught an exception deserializing an iota", exn);
|
||||
deserialized = new GarbageIota();
|
||||
}
|
||||
return deserialized;
|
||||
}
|
||||
|
||||
private static Component brokenIota() {
|
||||
return Component.translatable("hexcasting.spelldata.unknown")
|
||||
.withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC);
|
||||
}
|
||||
|
||||
public static Component getDisplay(CompoundTag tag) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return brokenIota();
|
||||
}
|
||||
var data = tag.get(HexIotaTypes.KEY_DATA);
|
||||
if (data == null) {
|
||||
return brokenIota();
|
||||
}
|
||||
return type.display(data);
|
||||
}
|
||||
|
||||
public static FormattedCharSequence getDisplayWithMaxWidth(CompoundTag tag, int maxWidth, Font font) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return brokenIota().getVisualOrderText();
|
||||
}
|
||||
var data = tag.get(HexIotaTypes.KEY_DATA);
|
||||
if (data == null) {
|
||||
return brokenIota().getVisualOrderText();
|
||||
}
|
||||
var display = type.display(data);
|
||||
var splitted = font.split(display, maxWidth - font.width("..."));
|
||||
if (splitted.isEmpty())
|
||||
return FormattedCharSequence.EMPTY;
|
||||
else if (splitted.size() == 1)
|
||||
return splitted.get(0);
|
||||
else {
|
||||
var first = splitted.get(0);
|
||||
return FormattedCharSequence.fromPair(first,
|
||||
Component.literal("...").withStyle(ChatFormatting.GRAY).getVisualOrderText());
|
||||
}
|
||||
}
|
||||
|
||||
public static int getColor(CompoundTag tag) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return HexUtils.ERROR_COLOR;
|
||||
}
|
||||
return type.color();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class ListIota extends Iota {
|
|||
public @NotNull Tag serialize() {
|
||||
var out = new ListTag();
|
||||
for (var subdatum : this.getList()) {
|
||||
out.add(HexIotaTypes.serialize(subdatum));
|
||||
out.add(IotaType.serialize(subdatum));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public class ListIota extends Iota {
|
|||
|
||||
for (var sub : listTag) {
|
||||
var csub = HexUtils.downcast(sub, CompoundTag.TYPE);
|
||||
var subiota = HexIotaTypes.deserialize(csub, world);
|
||||
var subiota = IotaType.deserialize(csub, world);
|
||||
if (subiota == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public class ListIota extends Iota {
|
|||
Tag sub = list.get(i);
|
||||
var csub = HexUtils.downcast(sub, CompoundTag.TYPE);
|
||||
|
||||
out.append(HexIotaTypes.getDisplay(csub));
|
||||
out.append(IotaType.getDisplay(csub));
|
||||
|
||||
if (i < list.size() - 1) {
|
||||
out.append(", ");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package at.petrak.hexcasting.api.item;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.client.ClientTickCounter;
|
||||
|
@ -45,7 +46,7 @@ public interface IotaHolderItem {
|
|||
|
||||
var tag = dh.readIotaTag(stack);
|
||||
if (tag != null) {
|
||||
return HexIotaTypes.deserialize(tag, world);
|
||||
return IotaType.deserialize(tag, world);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -83,7 +84,7 @@ public interface IotaHolderItem {
|
|||
return HexUtils.ERROR_COLOR;
|
||||
}
|
||||
|
||||
return HexIotaTypes.getColor(tag);
|
||||
return IotaType.getColor(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +101,7 @@ public interface IotaHolderItem {
|
|||
TooltipFlag flag) {
|
||||
var datumTag = self.readIotaTag(stack);
|
||||
if (datumTag != null) {
|
||||
var cmp = HexIotaTypes.getDisplay(datumTag);
|
||||
var cmp = IotaType.getDisplay(datumTag);
|
||||
components.add(Component.translatable("hexcasting.spelldata.onitem", cmp));
|
||||
|
||||
if (flag.isAdvanced()) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package at.petrak.hexcasting.api.misc;
|
|||
|
||||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -15,10 +15,11 @@ import java.util.function.Predicate;
|
|||
|
||||
public class DiscoveryHandlers {
|
||||
private static final List<Predicate<Player>> HAS_LENS_PREDICATE = new ArrayList<>();
|
||||
private static final List<Function<CastingHarness, List<ADMediaHolder>>> MEDIA_HOLDER_DISCOVERY = new ArrayList<>();
|
||||
private static final List<Function<CastingVM, List<ADMediaHolder>>> MEDIA_HOLDER_DISCOVERY = new ArrayList<>();
|
||||
private static final List<FunctionToFloat<Player>> GRID_SCALE_MODIFIERS = new ArrayList<>();
|
||||
private static final List<Function<CastingEnvironment, List<ItemStack>>> ITEM_SLOT_DISCOVERER = new ArrayList<>();
|
||||
private static final List<Function<CastingEnvironment, List<ItemStack>>> OPERATIVE_SLOT_DISCOVERER = new ArrayList<>();
|
||||
private static final List<Function<CastingEnvironment, List<ItemStack>>> OPERATIVE_SLOT_DISCOVERER =
|
||||
new ArrayList<>();
|
||||
private static final List<BiFunction<Player, String, ItemStack>> DEBUG_DISCOVERER = new ArrayList<>();
|
||||
|
||||
public static boolean hasLens(Player player) {
|
||||
|
@ -30,7 +31,7 @@ public class DiscoveryHandlers {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static List<ADMediaHolder> collectMediaHolders(CastingHarness harness) {
|
||||
public static List<ADMediaHolder> collectMediaHolders(CastingVM harness) {
|
||||
List<ADMediaHolder> holders = Lists.newArrayList();
|
||||
for (var discoverer : MEDIA_HOLDER_DISCOVERY) {
|
||||
holders.addAll(discoverer.apply(harness));
|
||||
|
@ -76,7 +77,7 @@ public class DiscoveryHandlers {
|
|||
HAS_LENS_PREDICATE.add(predicate);
|
||||
}
|
||||
|
||||
public static void addMediaHolderDiscoverer(Function<CastingHarness, List<ADMediaHolder>> discoverer) {
|
||||
public static void addMediaHolderDiscoverer(Function<CastingVM, List<ADMediaHolder>> discoverer) {
|
||||
MEDIA_HOLDER_DISCOVERY.add(discoverer);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
package at.petrak.hexcasting.api.utils
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType
|
||||
import at.petrak.hexcasting.api.casting.iota.ListIota
|
||||
import at.petrak.hexcasting.api.casting.math.HexCoord
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
|
||||
import net.minecraft.ChatFormatting
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.nbt.*
|
||||
|
@ -251,7 +251,7 @@ inline operator fun <T> WeakValue<T>.setValue(thisRef: Any?, property: KProperty
|
|||
* Returns an empty list if it's too complicated.
|
||||
*/
|
||||
fun Iterable<Iota>.serializeToNBT() =
|
||||
if (HexIotaTypes.isTooLargeToSerialize(this))
|
||||
if (IotaType.isTooLargeToSerialize(this))
|
||||
ListTag()
|
||||
else
|
||||
ListIota(this.toList()).serialize()
|
||||
|
|
|
@ -2,6 +2,7 @@ package at.petrak.hexcasting.client;
|
|||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.item.MediaHolderItem;
|
||||
|
@ -19,7 +20,6 @@ import at.petrak.hexcasting.common.items.magic.ItemPackagedHex;
|
|||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.xplat.IClientXplatAbstractions;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.ChatFormatting;
|
||||
|
@ -138,7 +138,7 @@ public class RegisterClientStuff {
|
|||
if (iotaTag == null) {
|
||||
return 0xff_ffffff;
|
||||
}
|
||||
return HexIotaTypes.getColor(iotaTag);
|
||||
return IotaType.getColor(iotaTag);
|
||||
}, HexBlocks.AKASHIC_BOOKSHELF);
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ public class RegisterClientStuff {
|
|||
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile) {
|
||||
var iotaTag = tile.getIotaTag();
|
||||
if (iotaTag != null) {
|
||||
var display = HexIotaTypes.getDisplay(iotaTag);
|
||||
var display = IotaType.getDisplay(iotaTag);
|
||||
lines.add(new Pair<>(new ItemStack(Items.BOOK), display));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
package at.petrak.hexcasting.client.gui
|
||||
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
import at.petrak.hexcasting.api.mod.HexTags
|
||||
import at.petrak.hexcasting.api.casting.eval.ControllerInfo
|
||||
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.iota.IotaType
|
||||
import at.petrak.hexcasting.api.casting.math.HexAngle
|
||||
import at.petrak.hexcasting.api.casting.math.HexCoord
|
||||
import at.petrak.hexcasting.api.casting.math.HexDir
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
import at.petrak.hexcasting.api.mod.HexTags
|
||||
import at.petrak.hexcasting.api.utils.asTranslatedComponent
|
||||
import at.petrak.hexcasting.client.*
|
||||
import at.petrak.hexcasting.client.ktxt.accumulatedScroll
|
||||
import at.petrak.hexcasting.client.sound.GridSoundInstance
|
||||
import at.petrak.hexcasting.common.lib.HexSounds
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
|
||||
import at.petrak.hexcasting.common.network.MsgNewSpellPatternSyn
|
||||
import at.petrak.hexcasting.xplat.IClientXplatAbstractions
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
|
@ -59,7 +59,7 @@ class GuiSpellcasting constructor(
|
|||
this.calculateIotaDisplays()
|
||||
}
|
||||
|
||||
fun recvServerUpdate(info: ControllerInfo, index: Int) {
|
||||
fun recvServerUpdate(info: ExecutionClientView, index: Int) {
|
||||
this.patterns.getOrNull(index)?.let {
|
||||
it.type = info.resolutionType
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class GuiSpellcasting constructor(
|
|||
val mc = Minecraft.getInstance()
|
||||
val width = (this.width * LHS_IOTAS_ALLOCATION).toInt()
|
||||
this.stackDescs =
|
||||
this.cachedStack.map { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font) }
|
||||
this.cachedStack.map { IotaType.getDisplayWithMaxWidth(it, width, mc.font) }
|
||||
.asReversed()
|
||||
// this.parenDescs = if (this.cachedParens.isNotEmpty())
|
||||
// this.cachedParens.flatMap { HexIotaTypes.getDisplayWithMaxWidth(it, width, mc.font) }
|
||||
|
@ -86,7 +86,7 @@ class GuiSpellcasting constructor(
|
|||
this.parenDescs = emptyList()
|
||||
this.ravenmind =
|
||||
this.cachedRavenmind?.let {
|
||||
HexIotaTypes.getDisplayWithMaxWidth(
|
||||
IotaType.getDisplayWithMaxWidth(
|
||||
it,
|
||||
(this.width * RHS_IOTAS_ALLOCATION).toInt(),
|
||||
mc.font
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hexcasting.common.blocks.akashic;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -57,7 +57,7 @@ public class BlockAkashicRecord extends Block {
|
|||
|
||||
var tile = (BlockEntityAkashicBookshelf) slevel.getBlockEntity(foundPos);
|
||||
var tag = tile.getIotaTag();
|
||||
return tag == null ? null : HexIotaTypes.deserialize(tag, slevel);
|
||||
return tag == null ? null : IotaType.deserialize(tag, slevel);
|
||||
}
|
||||
|
||||
// TODO get comparators working again and also cache the number of iotas somehow?
|
||||
|
|
|
@ -2,9 +2,9 @@ package at.petrak.hexcasting.common.blocks.akashic;
|
|||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
@ -38,7 +38,7 @@ public class BlockEntityAkashicBookshelf extends HexBlockEntity {
|
|||
public void setNewMapping(HexPattern pattern, Iota iota) {
|
||||
var previouslyEmpty = this.pattern == null;
|
||||
this.pattern = pattern;
|
||||
this.iotaTag = HexIotaTypes.serialize(iota);
|
||||
this.iotaTag = IotaType.serialize(iota);
|
||||
|
||||
if (previouslyEmpty) {
|
||||
var oldBs = this.getBlockState();
|
||||
|
|
|
@ -2,7 +2,7 @@ package at.petrak.hexcasting.common.casting.env;
|
|||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
import at.petrak.hexcasting.api.casting.eval.ControllerInfo;
|
||||
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;
|
||||
|
@ -74,13 +74,13 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
|
|||
|
||||
var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed());
|
||||
|
||||
ControllerInfo clientInfo;
|
||||
ExecutionClientView clientInfo;
|
||||
if (autoFail) {
|
||||
var descs = harness.generateDescs();
|
||||
clientInfo = new ControllerInfo(harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
|
||||
clientInfo = new ExecutionClientView(harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
|
||||
descs.getFirst(), descs.getSecond(), descs.getThird(), harness.getParenCount());
|
||||
} else {
|
||||
clientInfo = harness.executeIota(new PatternIota(msg.pattern()), sender.getLevel());
|
||||
clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
|
||||
}
|
||||
|
||||
if (clientInfo.isStackClear()) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package at.petrak.hexcasting.common.items;
|
||||
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
@ -30,7 +30,7 @@ public class ItemAbacus extends Item implements IotaHolderItem {
|
|||
public @Nullable
|
||||
CompoundTag readIotaTag(ItemStack stack) {
|
||||
var datum = new DoubleIota(NBTHelper.getDouble(stack, TAG_VALUE));
|
||||
return HexIotaTypes.serialize(datum);
|
||||
return IotaType.serialize(datum);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,7 +66,7 @@ public class ItemAbacus extends Item implements IotaHolderItem {
|
|||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List<Component> pTooltipComponents,
|
||||
TooltipFlag pIsAdvanced) {
|
||||
TooltipFlag pIsAdvanced) {
|
||||
IotaHolderItem.appendHoverText(this, pStack, pTooltipComponents, pIsAdvanced);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package at.petrak.hexcasting.common.items;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.casting.iota.NullIota;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -57,7 +57,7 @@ public class ItemFocus extends Item implements IotaHolderItem {
|
|||
stack.removeTagKey(TAG_DATA);
|
||||
stack.removeTagKey(TAG_SEALED);
|
||||
} else if (!isSealed(stack)) {
|
||||
NBTHelper.put(stack, TAG_DATA, HexIotaTypes.serialize(datum));
|
||||
NBTHelper.put(stack, TAG_DATA, IotaType.serialize(datum));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package at.petrak.hexcasting.common.items;
|
||||
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.casting.iota.NullIota;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
@ -42,7 +42,7 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
|
|||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip,
|
||||
TooltipFlag isAdvanced) {
|
||||
TooltipFlag isAdvanced) {
|
||||
boolean sealed = isSealed(stack);
|
||||
boolean empty = false;
|
||||
if (NBTHelper.hasNumber(stack, TAG_SELECTED_PAGE)) {
|
||||
|
@ -148,14 +148,14 @@ public class ItemSpellbook extends Item implements IotaHolderItem {
|
|||
pages.remove(key);
|
||||
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
|
||||
} else {
|
||||
pages.put(key, HexIotaTypes.serialize(datum));
|
||||
pages.put(key, IotaType.serialize(datum));
|
||||
}
|
||||
|
||||
if (pages.isEmpty()) {
|
||||
NBTHelper.remove(stack, TAG_PAGES);
|
||||
}
|
||||
} else if (datum != null) {
|
||||
NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, HexIotaTypes.serialize(datum));
|
||||
NBTHelper.getOrCreateCompound(stack, TAG_PAGES).put(key, IotaType.serialize(datum));
|
||||
} else {
|
||||
NBTHelper.remove(NBTHelper.getCompound(stack, TAG_SEALED), key);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
@ -66,7 +66,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
|
|||
var out = new ArrayList<Iota>();
|
||||
for (var patTag : patsTag) {
|
||||
CompoundTag tag = NBTHelper.getAsCompound(patTag);
|
||||
out.add(HexIotaTypes.deserialize(tag, level));
|
||||
out.add(IotaType.deserialize(tag, level));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
|
|||
public void writeHex(ItemStack stack, List<Iota> program, int media) {
|
||||
ListTag patsTag = new ListTag();
|
||||
for (Iota pat : program) {
|
||||
patsTag.add(HexIotaTypes.serialize(pat));
|
||||
patsTag.add(IotaType.serialize(pat));
|
||||
}
|
||||
|
||||
NBTHelper.putList(stack, TAG_PROGRAM, patsTag);
|
||||
|
@ -107,8 +107,8 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
|
|||
}
|
||||
var sPlayer = (ServerPlayer) player;
|
||||
var ctx = new CastingEnvironment(sPlayer, usedHand, CastingEnvironment.CastSource.PACKAGED_HEX);
|
||||
var harness = new CastingHarness(ctx);
|
||||
var info = harness.executeIotas(instrs, sPlayer.getLevel());
|
||||
var harness = new CastingVM(ctx);
|
||||
var info = harness.queueAndExecuteIotas(instrs, sPlayer.getLevel());
|
||||
|
||||
boolean broken = breakAfterDepletion() && getMedia(stack) == 0;
|
||||
|
||||
|
|
|
@ -2,22 +2,13 @@ package at.petrak.hexcasting.common.lib.hex;
|
|||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.casting.iota.*;
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.*;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
||||
|
@ -34,151 +25,6 @@ public class HexIotaTypes {
|
|||
public static final int MAX_SERIALIZATION_DEPTH = 256;
|
||||
public static final int MAX_SERIALIZATION_TOTAL = 1024;
|
||||
|
||||
public static CompoundTag serialize(Iota iota) {
|
||||
var type = iota.getType();
|
||||
var typeId = REGISTRY.getKey(type);
|
||||
if (typeId == null) {
|
||||
throw new IllegalStateException(
|
||||
"Tried to serialize an unregistered iota type. Iota: " + iota
|
||||
+ " ; Type" + type.getClass().getTypeName());
|
||||
}
|
||||
|
||||
// We check if it's too big on serialization; if it is we just return a garbage.
|
||||
if (iota instanceof ListIota listIota && isTooLargeToSerialize(listIota.getList())) {
|
||||
// Garbage will never be too large so we just recurse
|
||||
return serialize(new GarbageIota());
|
||||
}
|
||||
var dataTag = iota.serialize();
|
||||
var out = new CompoundTag();
|
||||
out.putString(KEY_TYPE, typeId.toString());
|
||||
out.put(KEY_DATA, dataTag);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static boolean isTooLargeToSerialize(Iterable<Iota> examinee) {
|
||||
// We don't recurse here, just a work queue (or work stack, if we liked.)
|
||||
// Each element is a found sub-iota, and how deep it is.
|
||||
//
|
||||
// TODO: is it worth trying to cache the depth and size statically on a SpellList.
|
||||
var listsToExamine = new ArrayDeque<>(Collections.singleton(new Pair<>(examinee, 0)));
|
||||
int totalEltsFound = 1; // count the first list
|
||||
while (!listsToExamine.isEmpty()) {
|
||||
var iotaPair = listsToExamine.removeFirst();
|
||||
var sublist = iotaPair.getFirst();
|
||||
int depth = iotaPair.getSecond();
|
||||
for (var iota : sublist) {
|
||||
totalEltsFound++;
|
||||
if (totalEltsFound >= MAX_SERIALIZATION_TOTAL) {
|
||||
return true; // too bad
|
||||
}
|
||||
if (iota instanceof ListIota subsublist) {
|
||||
if (depth + 1 >= MAX_SERIALIZATION_DEPTH) {
|
||||
return true;
|
||||
}
|
||||
listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
// we made it!
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method attempts to find the type from the {@code type} key.
|
||||
* See {@link HexIotaTypes#serialize(Iota)} for the storage format.
|
||||
*
|
||||
* @return {@code null} if it cannot get the type.
|
||||
*/
|
||||
@Nullable
|
||||
public static IotaType<?> getTypeFromTag(CompoundTag tag) {
|
||||
if (!tag.contains(KEY_TYPE, Tag.TAG_STRING)) {
|
||||
return null;
|
||||
}
|
||||
var typeKey = tag.getString(KEY_TYPE);
|
||||
if (!ResourceLocation.isValidResourceLocation(typeKey)) {
|
||||
return null;
|
||||
}
|
||||
var typeLoc = new ResourceLocation(typeKey);
|
||||
return REGISTRY.get(typeLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to deserialize an iota from a tag.
|
||||
* <br>
|
||||
* Iotas are saved as such:
|
||||
* <code>
|
||||
* {
|
||||
* "type": "hexcasting:atype",
|
||||
* "data": {...}
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
public static Iota deserialize(CompoundTag tag, ServerLevel world) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return new GarbageIota();
|
||||
}
|
||||
var data = tag.get(KEY_DATA);
|
||||
if (data == null) {
|
||||
return new GarbageIota();
|
||||
}
|
||||
Iota deserialized;
|
||||
try {
|
||||
deserialized = Objects.requireNonNullElse(type.deserialize(data, world), new NullIota());
|
||||
} catch (IllegalArgumentException exn) {
|
||||
HexAPI.LOGGER.warn("Caught an exception deserializing an iota", exn);
|
||||
deserialized = new GarbageIota();
|
||||
}
|
||||
return deserialized;
|
||||
}
|
||||
|
||||
private static Component brokenIota() {
|
||||
return Component.translatable("hexcasting.spelldata.unknown")
|
||||
.withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC);
|
||||
}
|
||||
|
||||
public static Component getDisplay(CompoundTag tag) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return brokenIota();
|
||||
}
|
||||
var data = tag.get(KEY_DATA);
|
||||
if (data == null) {
|
||||
return brokenIota();
|
||||
}
|
||||
return type.display(data);
|
||||
}
|
||||
|
||||
public static FormattedCharSequence getDisplayWithMaxWidth(CompoundTag tag, int maxWidth, Font font) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return brokenIota().getVisualOrderText();
|
||||
}
|
||||
var data = tag.get(KEY_DATA);
|
||||
if (data == null) {
|
||||
return brokenIota().getVisualOrderText();
|
||||
}
|
||||
var display = type.display(data);
|
||||
var splitted = font.split(display, maxWidth - font.width("..."));
|
||||
if (splitted.isEmpty())
|
||||
return FormattedCharSequence.EMPTY;
|
||||
else if (splitted.size() == 1)
|
||||
return splitted.get(0);
|
||||
else {
|
||||
var first = splitted.get(0);
|
||||
return FormattedCharSequence.fromPair(first,
|
||||
Component.literal("...").withStyle(ChatFormatting.GRAY).getVisualOrderText());
|
||||
}
|
||||
}
|
||||
|
||||
public static int getColor(CompoundTag tag) {
|
||||
var type = getTypeFromTag(tag);
|
||||
if (type == null) {
|
||||
return HexUtils.ERROR_COLOR;
|
||||
}
|
||||
return type.color();
|
||||
}
|
||||
|
||||
public static void registerTypes(BiConsumer<IotaType<?>, ResourceLocation> r) {
|
||||
for (var e : TYPES.entrySet()) {
|
||||
r.accept(e.getValue(), e.getKey());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.ControllerInfo;
|
||||
import at.petrak.hexcasting.api.casting.eval.ExecutionClientView;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
|
||||
import at.petrak.hexcasting.client.gui.GuiSpellcasting;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
|
@ -16,7 +16,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
|||
/**
|
||||
* Sent server->client when the player finishes casting a spell.
|
||||
*/
|
||||
public record MsgNewSpellPatternAck(ControllerInfo info, int index) implements IMessage {
|
||||
public record MsgNewSpellPatternAck(ExecutionClientView info, int index) implements IMessage {
|
||||
public static final ResourceLocation ID = modLoc("pat_sc");
|
||||
|
||||
@Override
|
||||
|
@ -38,7 +38,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info, int index) implements I
|
|||
var parenCount = buf.readVarInt();
|
||||
|
||||
return new MsgNewSpellPatternAck(
|
||||
new ControllerInfo(isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
|
||||
new ExecutionClientView(isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.items.ItemAbacus;
|
||||
import at.petrak.hexcasting.common.items.ItemSpellbook;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
@ -144,7 +144,7 @@ public record MsgShiftScrollSyn(double mainHandDelta, double offHandDelta, boole
|
|||
|
||||
var datumTag = HexItems.ABACUS.readIotaTag(stack);
|
||||
if (datumTag != null) {
|
||||
var popup = HexIotaTypes.getDisplay(datumTag);
|
||||
var popup = IotaType.getDisplay(datumTag);
|
||||
sender.displayClientMessage(
|
||||
Component.translatable("hexcasting.tooltip.abacus", popup).withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
|||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.player.FlightAbility;
|
||||
|
@ -79,7 +79,7 @@ public interface IXplatAbstractions {
|
|||
|
||||
void setFlight(ServerPlayer target, FlightAbility flight);
|
||||
|
||||
void setHarness(ServerPlayer target, @Nullable CastingHarness harness);
|
||||
void setHarness(ServerPlayer target, @Nullable CastingVM harness);
|
||||
|
||||
void setPatterns(ServerPlayer target, List<ResolvedPattern> patterns);
|
||||
|
||||
|
@ -91,7 +91,7 @@ public interface IXplatAbstractions {
|
|||
|
||||
Sentinel getSentinel(Player player);
|
||||
|
||||
CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand);
|
||||
CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand);
|
||||
|
||||
List<ResolvedPattern> getPatternsSavedInUi(ServerPlayer player);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.fabric.cc;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import dev.onyxstudios.cca.api.v3.component.Component;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
@ -18,16 +18,16 @@ public class CCHarness implements Component {
|
|||
this.owner = owner;
|
||||
}
|
||||
|
||||
public CastingHarness getHarness(InteractionHand hand) {
|
||||
public CastingVM getHarness(InteractionHand hand) {
|
||||
var ctx = new CastingEnvironment(this.owner, hand, CastingEnvironment.CastSource.STAFF);
|
||||
if (this.lazyLoadedTag.isEmpty()) {
|
||||
return new CastingHarness(ctx);
|
||||
return new CastingVM(ctx);
|
||||
} else {
|
||||
return CastingHarness.fromNBT(this.lazyLoadedTag, ctx);
|
||||
return CastingVM.fromNBT(this.lazyLoadedTag, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHarness(@Nullable CastingHarness harness) {
|
||||
public void setHarness(@Nullable CastingVM harness) {
|
||||
this.lazyLoadedTag = harness == null ? new CompoundTag() : harness.serializeToNBT();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hexcasting.fabric.cc.adimpl;
|
||||
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.fabric.cc.HexCardinalComponents;
|
||||
import dev.onyxstudios.cca.api.v3.item.ItemComponent;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
@ -56,7 +56,7 @@ public abstract class CCItemIotaHolder extends ItemComponent implements CCIotaHo
|
|||
@Override
|
||||
public @Nullable CompoundTag readIotaTag() {
|
||||
var iota = this.provider.apply(this.stack);
|
||||
return iota == null ? null : HexIotaTypes.serialize(iota);
|
||||
return iota == null ? null : IotaType.serialize(iota);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,9 +6,9 @@ import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
|||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
|
@ -165,7 +165,7 @@ public class FabricXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setHarness(ServerPlayer target, CastingHarness harness) {
|
||||
public void setHarness(ServerPlayer target, CastingVM harness) {
|
||||
var cc = HexCardinalComponents.HARNESS.get(target);
|
||||
cc.setHarness(harness);
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ public class FabricXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand) {
|
||||
public CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand) {
|
||||
var cc = HexCardinalComponents.HARNESS.get(player);
|
||||
return cc.getHarness(hand);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package at.petrak.hexcasting.forge.cap.adimpl;
|
|||
|
||||
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -17,7 +17,7 @@ public record CapStaticIotaHolder(Function<ItemStack, Iota> provider,
|
|||
public @Nullable
|
||||
CompoundTag readIotaTag() {
|
||||
var iota = provider.apply(stack);
|
||||
return iota == null ? null : HexIotaTypes.serialize(iota);
|
||||
return iota == null ? null : IotaType.serialize(iota);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,9 +8,9 @@ import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
|||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.mod.HexTags;
|
||||
|
@ -183,7 +183,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setHarness(ServerPlayer player, CastingHarness harness) {
|
||||
public void setHarness(ServerPlayer player, CastingVM harness) {
|
||||
player.getPersistentData().put(TAG_HARNESS, harness == null ? new CompoundTag() : harness.serializeToNBT());
|
||||
}
|
||||
|
||||
|
@ -237,10 +237,10 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CastingHarness getStaffHarness(ServerPlayer player, InteractionHand hand) {
|
||||
public CastingVM getStaffHarness(ServerPlayer player, InteractionHand hand) {
|
||||
// This is always from a staff because we don't need to load the harness when casting from item
|
||||
var ctx = new CastingEnvironment(player, hand, CastingEnvironment.CastSource.STAFF);
|
||||
return CastingHarness.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
|
||||
return CastingVM.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue