Skip to content

Commit

Permalink
implement codecs on serializable classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Master-Bw3 committed Aug 29, 2024
1 parent bafb3bd commit 5148240
Show file tree
Hide file tree
Showing 57 changed files with 723 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.utils.HexUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;
Expand All @@ -14,7 +15,7 @@ public interface ADIotaHolder {
default Iota readIota(ServerLevel world) {
var tag = readIotaTag();
if (tag != null) {
return IotaType.deserialize(tag, world);
return HexUtils.deserializeWithCodec(tag, Iota.getCodec(world));
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ package at.petrak.hexcasting.api.casting

import at.petrak.hexcasting.api.casting.iota.*
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.MishapEntityNotFound
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import com.mojang.datafixers.util.Either
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
Expand All @@ -32,10 +34,10 @@ fun List<Iota>.getDouble(idx: Int, argc: Int = 0): Double {
}
}

fun List<Iota>.getEntity(idx: Int, argc: Int = 0): Entity {
fun List<Iota>.getEntity(idx: Int, argc: Int = 0, world: ServerLevel): Entity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
return x.entity
return x.getEntity(world)?: throw MishapEntityNotFound(x, if (argc == 0) idx else argc - (idx + 1))
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity")
}
Expand Down Expand Up @@ -79,40 +81,40 @@ fun List<Iota>.getBool(idx: Int, argc: Int = 0): Boolean {

// Helpers

fun List<Iota>.getItemEntity(idx: Int, argc: Int = 0): ItemEntity {
fun List<Iota>.getItemEntity(idx: Int, argc: Int = 0, world: ServerLevel): ItemEntity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
val e = x.getEntity(world)?: throw MishapEntityNotFound(x, if (argc == 0) idx else argc - (idx + 1))
if (e is ItemEntity)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.item")
}

fun List<Iota>.getPlayer(idx: Int, argc: Int = 0): ServerPlayer {
fun List<Iota>.getPlayer(idx: Int, argc: Int = 0, world: ServerLevel): ServerPlayer {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
val e = x.getEntity(world)?: throw MishapEntityNotFound(x, if (argc == 0) idx else argc - (idx + 1))
if (e is ServerPlayer)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.player")
}

fun List<Iota>.getMob(idx: Int, argc: Int = 0): Mob {
fun List<Iota>.getMob(idx: Int, argc: Int = 0, world: ServerLevel): Mob {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
val e = x.getEntity(world)?: throw MishapEntityNotFound(x, if (argc == 0) idx else argc - (idx + 1))
if (e is Mob)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.mob")
}

fun List<Iota>.getLivingEntityButNotArmorStand(idx: Int, argc: Int = 0): LivingEntity {
fun List<Iota>.getLivingEntityButNotArmorStand(idx: Int, argc: Int = 0, world: ServerLevel): LivingEntity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
val e = x.getEntity(world)?: throw MishapEntityNotFound(x, if (argc == 0) idx else argc - (idx + 1))
if (e is LivingEntity && e !is ArmorStand)
return e
}
Expand Down
11 changes: 11 additions & 0 deletions Common/src/main/java/at/petrak/hexcasting/api/casting/SpellList.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package at.petrak.hexcasting.api.casting

import at.petrak.hexcasting.api.casting.iota.Iota
import com.mojang.serialization.Codec
import net.minecraft.server.level.ServerLevel
import java.util.Collections

/**
* Restricted interface for functional lists.
Expand Down Expand Up @@ -90,4 +93,12 @@ sealed class SpellList : Iterable<Iota> {
return car
}
}

companion object {
@JvmStatic
fun getCodec(): Codec<SpellList> = Iota.getCodec().listOf().xmap(SpellList::LList, SpellList::toMutableList)

@JvmStatic
fun getCodec(world: ServerLevel): Codec<SpellList> = Iota.getCodec(world).listOf().xmap(SpellList::LList, SpellList::toMutableList)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,26 @@ package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.casting.math.HexCoord
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.utils.NBTBuilder
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.CompoundTag
import java.util.*


data class ResolvedPattern(val pattern: HexPattern, val origin: HexCoord, var type: ResolvedPatternType) {
constructor(pattern: HexPattern, originQ: Int, originR: Int, type: ResolvedPatternType) : this(
pattern,
HexCoord(originQ, originR),
type
)

val originQ: Int
get() = origin.q

val originR: Int
get() = origin.r

@Deprecated("Use the CODEC instead.")
fun serializeToNBT() = NBTBuilder {
"Pattern" %= pattern.serializeToNBT()
"OriginQ" %= origin.q
Expand All @@ -16,6 +31,19 @@ data class ResolvedPattern(val pattern: HexPattern, val origin: HexCoord, var ty
}

companion object {
@JvmField
val CODEC: Codec<ResolvedPattern> = RecordCodecBuilder.create {
it.group(
HexPattern.CODEC.fieldOf("Pattern").forGetter(ResolvedPattern::pattern),
Codec.INT.fieldOf("OriginQ").forGetter(ResolvedPattern::originQ),
Codec.INT.fieldOf("OriginR").forGetter(ResolvedPattern::originR),
Codec.STRING.fieldOf("Valid")
.xmap(ResolvedPatternType::fromString) { type -> type.name.lowercase(Locale.ROOT) }
.forGetter(ResolvedPattern::type)
).apply(it, ::ResolvedPattern)
}

@Deprecated("Use the CODEC instead.")
@JvmStatic
fun fromNBT(tag: CompoundTag): ResolvedPattern {
val pattern = HexPattern.fromNBT(tag.getCompound("Pattern"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package at.petrak.hexcasting.api.casting.eval.vm

import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.deserializeWithCodec
import at.petrak.hexcasting.common.lib.hex.HexContinuationTypes
import com.google.common.base.Suppliers
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
import net.minecraft.nbt.NbtOps
import net.minecraft.server.level.ServerLevel
import java.util.function.Supplier

/**
* A single frame of evaluation during the execution of a spell.
Expand Down Expand Up @@ -50,59 +54,59 @@ interface ContinuationFrame {
val type: Type<*>

interface Type<U : ContinuationFrame> {
fun deserializeFromNBT(tag: CompoundTag, world: ServerLevel): U?
fun getCodec(): Codec<U>

fun getCodec(world: ServerLevel): Codec<U>

@Deprecated(
"Use the CODEC instead.",
replaceWith = ReplaceWith("tag.deserializeWithCodec(getCodec())")
)
fun deserializeFromNBT(tag: CompoundTag, world: ServerLevel): U? =
tag.deserializeWithCodec(getCodec())
}

companion object {
@JvmStatic
fun getCodec(): Codec<ContinuationFrame> =
HexContinuationTypes.REGISTRY.byNameCodec().dispatchMap(
HexContinuationTypes.KEY_TYPE,
ContinuationFrame::type
) { continuationType ->
continuationType.getCodec().fieldOf(HexContinuationTypes.KEY_DATA).codec()
}.codec()

@JvmStatic
fun getCodec(world: ServerLevel): Codec<ContinuationFrame> =
HexContinuationTypes.REGISTRY.byNameCodec().dispatchMap(
HexContinuationTypes.KEY_TYPE,
ContinuationFrame::type
) { continuationType ->
continuationType.getCodec(world).fieldOf(HexContinuationTypes.KEY_DATA).codec()
}.codec()

/**
* Takes a tag containing the ContinuationFrame.Type resourcelocation and the serialized continuation frame, and returns
* the deserialized continuation frame.
*/
@Deprecated(
"Use the codec instead.",
replaceWith = ReplaceWith("tag.deserializeWithCodec(ContinuationFrame.getCodec(world))")
)
@JvmStatic
fun fromNBT(tag: CompoundTag, world: ServerLevel): ContinuationFrame {
val type = getTypeFromTag(tag) ?: return FrameEvaluate(SpellList.LList(0, listOf()), false)

return (tag.get(HexContinuationTypes.KEY_DATA) as? CompoundTag)?.let { type.deserializeFromNBT(it, world) }
?: FrameEvaluate(SpellList.LList(0, listOf()), false)
}
fun fromNBT(tag: CompoundTag, world: ServerLevel): ContinuationFrame =
getCodec(world).parse(NbtOps.INSTANCE, tag).resultOrPartial(HexAPI.LOGGER::error).orElseThrow()

/**
* Takes a continuation frame and serializes it along with its type.
*/
@Deprecated(
"Use the codec instead.",
replaceWith = ReplaceWith("serializeWithCodec(ContinuationFrame.getCodec())")
)
@JvmStatic
fun toNBT(frame: ContinuationFrame): CompoundTag {
val type = frame.type
val typeId = HexContinuationTypes.REGISTRY.getKey(type)
?: throw IllegalStateException(
"Tried to serialize an unregistered continuation type. Continuation: " + frame
+ " ; Type" + type.javaClass.typeName)

val data = frame.serializeToNBT()

val out = CompoundTag()
out.putString(HexContinuationTypes.KEY_TYPE, typeId.toString())
out.put(HexContinuationTypes.KEY_DATA, data)
return out
}

/**
* This method attempts to find the type from the `type` key.
* See [ContinuationFrame.serializeToNBT] for the storage format.
*
* @return `null` if it cannot get the type.
*/
private fun getTypeFromTag(tag: CompoundTag): Type<*>? {
if (!tag.contains(HexContinuationTypes.KEY_TYPE, Tag.TAG_STRING.toInt())) {
return null
}

val typeKey = tag.getString(HexContinuationTypes.KEY_TYPE)
if (!ResourceLocation.isValidResourceLocation(typeKey)) {
return null
}

val typeLoc = ResourceLocation(typeKey)
return HexContinuationTypes.REGISTRY[typeLoc]
}
fun toNBT(frame: ContinuationFrame): CompoundTag =
getCodec().encodeStart(NbtOps.INSTANCE, frame).resultOrPartial(HexAPI.LOGGER::error)
.orElseThrow() as CompoundTag
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package at.petrak.hexcasting.api.casting.eval.vm

import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.serializeToNBT
import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.server.level.ServerLevel

Expand Down Expand Up @@ -44,14 +46,24 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
}
} else {
// If there are no patterns (e.g. empty Hermes), just return OK.
CastResult(ListIota(list), continuation, null, listOf(), ResolvedPatternType.EVALUATED, HexEvalSounds.HERMES)
CastResult(
ListIota(list),
continuation,
null,
listOf(),
ResolvedPatternType.EVALUATED,
HexEvalSounds.HERMES
)
}
}

override fun serializeToNBT() = NBTBuilder {
"patterns" %= list.serializeToNBT()
"isMetacasting" %= isMetacasting
}
@Deprecated(
"Use the CODEC instead.",
replaceWith = ReplaceWith("serializeWithCodec(FrameEvaluate.TYPE.getCodec())")
)
override fun serializeToNBT() =
TYPE.getCodec().encodeStart(NbtOps.INSTANCE, this).resultOrPartial(HexAPI.LOGGER::error)
.orElseThrow() as CompoundTag

override fun size() = list.size()

Expand All @@ -60,15 +72,19 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
companion object {
@JvmField
val TYPE: ContinuationFrame.Type<FrameEvaluate> = object : ContinuationFrame.Type<FrameEvaluate> {
override fun deserializeFromNBT(tag: CompoundTag, world: ServerLevel): FrameEvaluate {
return FrameEvaluate(
HexIotaTypes.LIST.deserialize(
tag.getList("patterns", Tag.TAG_COMPOUND),
world
)!!.list,
tag.getBoolean("isMetacasting"))
override fun getCodec(): Codec<FrameEvaluate> = RecordCodecBuilder.create {
it.group(
SpellList.getCodec().fieldOf("patterns").forGetter(FrameEvaluate::list),
Codec.BOOL.fieldOf("isMetacasting").forGetter(FrameEvaluate::isMetacasting)
).apply(it, ::FrameEvaluate)
}

override fun getCodec(world: ServerLevel): Codec<FrameEvaluate> = RecordCodecBuilder.create {
it.group(
SpellList.getCodec(world).fieldOf("patterns").forGetter(FrameEvaluate::list),
Codec.BOOL.fieldOf("isMetacasting").forGetter(FrameEvaluate::isMetacasting)
).apply(it, ::FrameEvaluate)
}
}
}
}
Loading

0 comments on commit 5148240

Please sign in to comment.