Merge branch 'master' into 1.20.1

This commit is contained in:
DBotThePony 2023-12-31 20:25:03 +07:00
commit 5453c8c793
Signed by: DBot
GPG Key ID: DCC23B5715498507
16 changed files with 229 additions and 427 deletions

View File

@ -1,15 +1,12 @@
package ru.dbotthepony.mc.otm.datagen.advancements
import net.minecraft.advancements.Advancement
import net.minecraft.advancements.AdvancementHolder
import net.minecraft.advancements.AdvancementRequirements
import net.minecraft.advancements.AdvancementRewards
import net.minecraft.advancements.FrameType
import net.minecraft.advancements.AdvancementRequirements.Strategy
import net.minecraft.advancements.critereon.InventoryChangeTrigger
import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
import net.minecraftforge.common.data.ExistingFileHelper
import ru.dbotthepony.mc.otm.core.registryName
import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider
import ru.dbotthepony.mc.otm.datagen.modLocation

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.datagen.advancements
import net.minecraft.advancements.Advancement
import net.minecraft.advancements.AdvancementHolder
import net.minecraft.advancements.AdvancementRequirements.Strategy
import net.minecraft.advancements.AdvancementRewards
@ -11,7 +10,6 @@ import net.minecraft.advancements.critereon.MinMaxBounds.Doubles
import net.minecraft.world.entity.EntityType
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.common.data.ExistingFileHelper
import ru.dbotthepony.mc.otm.datagen.DataGen
import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider
import ru.dbotthepony.mc.otm.datagen.modLocation
@ -158,7 +156,7 @@ fun addAndroidAdvancements(serializer: Consumer<AdvancementHolder>, lang: Matter
russian("Исследуйте что либо за андроида")
},
)
.addCriterion("research_anything", AndroidResearchTrigger.Instance(null).criterion())
.addCriterion("research_anything", AndroidResearchTrigger.Instance(Optional.empty(), Optional.empty()).criterion())
.save(serializer, modLocation("android/research_anything"))
AdvancementBuilder()

View File

@ -297,6 +297,13 @@ fun <T> Iterator<T>.toList(expectedSize: Int = 16): MutableList<T> {
return result
}
fun <T> Iterator<T>.toImmutableList(expectedSize: Int = 16): List<T> {
if (!hasNext())
return emptyList()
return toList(expectedSize)
}
fun <T : Any> Iterator<T>.find(): Optional<T> {
if (hasNext()) {
return Optional.of(next())

View File

@ -1,100 +0,0 @@
package ru.dbotthepony.mc.otm.data
import com.google.gson.JsonObject
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import com.mojang.serialization.MapCodec
import com.mojang.serialization.MapLike
import com.mojang.serialization.RecordBuilder
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate
import ru.dbotthepony.mc.otm.core.fromJsonStrict
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict
import java.util.Optional
import java.util.stream.Stream
class Codec2TriggerSerializer<S : Any>(supplier: (Codec2TriggerSerializer<S>.Context) -> Codec<S>) : Codec<S> {
private val codec = supplier.invoke(Context())
override fun <T : Any> encode(input: S, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return codec.encode(input, ops, prefix)
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<S, T>> {
return codec.decode(ops, input)
}
fun fromJson(element: JsonObject, player: Optional<ContextAwarePredicate>, context: DeserializationContext): S {
try {
Companion.context.get().addLast(ThreadContext(player, context))
return codec.fromJsonStrict(element)
} finally {
Companion.context.get().removeLast()
}
}
fun toJson(value: S): JsonObject {
return codec.toJsonStrict(value) as JsonObject
}
inner class Context {
val playerPredicate: MapCodec<Optional<ContextAwarePredicate>>
get() = CAPPredicate
val awareContextCodec: Codec<ContextAwarePredicate>
get() = ActualCAPPredicate
}
private data class ThreadContext(val player: Optional<ContextAwarePredicate>, val context: DeserializationContext)
private object ActualCAPPredicate : Codec<ContextAwarePredicate> {
override fun <T : Any> encode(input: ContextAwarePredicate, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return try {
DataResult.success(JsonOps.INSTANCE.convertTo(ops, input.toJson()))
} catch (err: Exception) {
DataResult.error { "Failed to serialize ContextAwarePredicate: " + err.message }
}
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<ContextAwarePredicate, T>> {
val context = context.get().lastOrNull() ?: return DataResult.error { "Not current deserializing trigger instance" }
return try {
DataResult.success(Pair(EntityPredicate.fromJson(JsonObject().also { it["a"] = ops.convertTo(JsonOps.INSTANCE, input) }, "a", context.context).get(), ops.empty()))
} catch (err: Exception) {
DataResult.error { "Failed to deserialize ContextAwarePredicate: " + err.message }
}
}
}
private object CAPPredicate : MapCodec<Optional<ContextAwarePredicate>>() {
override fun <T : Any> keys(ops: DynamicOps<T>): Stream<T> {
return Stream.of(ops.createString("player"))
}
override fun <T : Any> decode(ops: DynamicOps<T>, input: MapLike<T>): DataResult<Optional<ContextAwarePredicate>> {
return DataResult.success(context.get().lastOrNull()?.player ?: return DataResult.error { "Not currently deserializing trigger instance" })
}
override fun <T : Any> encode(input: Optional<ContextAwarePredicate>, ops: DynamicOps<T>, prefix: RecordBuilder<T>): RecordBuilder<T> {
if (input.isPresent) {
return prefix.add("player", JsonOps.INSTANCE.convertTo(ops, input.get().toJson()))
} else {
return prefix
}
}
}
companion object {
private val context = object : ThreadLocal<ArrayDeque<ThreadContext>>() {
override fun initialValue(): ArrayDeque<ThreadContext> {
return ArrayDeque()
}
}
}
}

View File

@ -1,50 +1,33 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.*
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.android.AndroidResearchType
import ru.dbotthepony.mc.otm.core.set
import java.util.Optional
import java.util.*
import java.util.function.Predicate
object AndroidResearchTrigger : SimpleCriterionTrigger<AndroidResearchTrigger.Instance>() {
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research")
override fun createInstance(
p_66248_: JsonObject,
p_286603_: Optional<ContextAwarePredicate>,
p_66250_: DeserializationContext
): Instance {
return Instance(
p_66248_["research"]?.asString?.let(::ResourceLocation)
)
}
object AndroidResearchTrigger : MCriterionTrigger<AndroidResearchTrigger.Instance>(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research")) {
fun trigger(player: ServerPlayer, research: AndroidResearchType) {
trigger(player) {
it.test(research)
}
trigger(player) { it.test(research) }
}
class Instance(val research: ResourceLocation?) : AbstractCriterionTriggerInstance(Optional.empty()), Predicate<AndroidResearchType> {
constructor(research: AndroidResearchType) : this(research.id)
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
ResourceLocation.CODEC.optionalFieldOf("research").forGetter(Instance::research),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
class Instance(val research: Optional<ResourceLocation>, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(player), Predicate<AndroidResearchType> {
constructor(research: ResourceLocation) : this(Optional.of(research))
constructor(research: AndroidResearchType) : this(Optional.of(research.id))
override fun test(t: AndroidResearchType): Boolean {
return research == null || t.id == research
return research.map { it == t.id }.orElse(true)
}
override fun serializeToJson(): JsonObject {
return super.serializeToJson().also {
if (research != null)
it["research"] = JsonPrimitive(research.toString())
}
}
fun criterion() = Criterion(AndroidResearchTrigger, this)
}
}

View File

@ -1,48 +1,25 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.google.gson.JsonPrimitive
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.*
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict
import ru.dbotthepony.mc.otm.data.Codec2TriggerSerializer
import ru.dbotthepony.mc.otm.data.minRange
import java.util.Optional
object AndroidTravelUnderwater : SimpleCriterionTrigger<AndroidTravelUnderwater.Instance>() {
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "android_walk_underwater")
override fun createInstance(pJson: JsonObject, pPlayer: Optional<ContextAwarePredicate>, pContext: DeserializationContext): Instance {
return codec.fromJson(pJson, pPlayer, pContext)
}
import java.util.*
object AndroidTravelUnderwater : MCriterionTrigger<AndroidTravelUnderwater.Instance>(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_walk_underwater")) {
fun trigger(player: ServerPlayer, travelled: Double) {
trigger(player) {
it.distanceToTravel <= travelled
}
trigger(player) { it.distanceToTravel <= travelled }
}
val codec = Codec2TriggerSerializer<Instance> { p ->
RecordCodecBuilder.create {
it.group(
Codec.DOUBLE.minRange(0.0).fieldOf("distanceToTravel").forGetter(Instance::distanceToTravel),
p.playerPredicate.forGetter { it.playerPredicate() }
).apply(it, ::Instance)
}
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
Codec.DOUBLE.minRange(0.0).fieldOf("distanceToTravel").forGetter(Instance::distanceToTravel),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
class Instance(val distanceToTravel: Double, playerPredicate: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractCriterionTriggerInstance(playerPredicate) {
override fun serializeToJson(): JsonObject {
return codec.toJson(this)
}
fun criterion() = Criterion(AndroidTravelUnderwater, this)
}
class Instance(val distanceToTravel: Double, playerPredicate: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(playerPredicate)
}

View File

@ -1,19 +1,12 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.fromJsonStrict
import ru.dbotthepony.mc.otm.core.toJsonStrict
import java.util.Optional
import java.util.*
val ExopackObtainedTrigger = SingletonTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "exopack_obtained"))
val ExopackGainedCraftingTrigger = SingletonTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "exopack_gained_crafting"))
@ -22,13 +15,12 @@ val ExopackGainedEnderAccessTrigger = SingletonTrigger(ResourceLocation(Overdriv
val ExopackBatterySlotTrigger = ItemTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "exopack_battery_slot"))
object ExopackSlotsExpandedTrigger : SimpleCriterionTrigger<ExopackSlotsExpandedTrigger.Instance>() {
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "exopack_expanded")
val codec: Codec<Instance> = RecordCodecBuilder.create {
object ExopackSlotsExpandedTrigger : MCriterionTrigger<ExopackSlotsExpandedTrigger.Instance>(ResourceLocation(OverdriveThatMatters.MOD_ID, "exopack_expanded")) {
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
Codec.intRange(0, Int.MAX_VALUE).optionalFieldOf("minGained", 0).forGetter(Instance::minGained),
Codec.intRange(0, Int.MAX_VALUE).optionalFieldOf("minTotal", 0).forGetter(Instance::minTotal),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
@ -36,20 +28,10 @@ object ExopackSlotsExpandedTrigger : SimpleCriterionTrigger<ExopackSlotsExpanded
trigger(player) { it.minGained <= gained && it.minTotal <= total }
}
override fun createInstance(p_66248_: JsonObject, p_286603_: Optional<ContextAwarePredicate>, p_66250_: DeserializationContext): Instance {
return codec.fromJsonStrict(p_66248_)
}
data class Instance(val minGained: Int = 0, val minTotal: Int = 0) : AbstractCriterionTriggerInstance(Optional.empty()) {
class Instance(val minGained: Int = 0, val minTotal: Int = 0, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(player) {
init {
require(minGained >= 0) { "Invalid minGained $minGained" }
require(minTotal >= 0) { "Invalid minTotal $minTotal" }
}
override fun serializeToJson(): JsonObject {
return codec.toJsonStrict(this) as JsonObject
}
fun criterion() = Criterion(ExopackSlotsExpandedTrigger, this)
}
}

View File

@ -1,29 +1,19 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.*
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DamagePredicate
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.LivingEntity
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.data.Codec2TriggerSerializer
import ru.dbotthepony.mc.otm.data.DamagePredicateCodec
import java.util.Optional
abstract class HurtTrigger : SimpleCriterionTrigger<HurtTrigger.Instance>() {
abstract val id: ResourceLocation
final override fun createInstance(
p_66248_: JsonObject,
p_286603_: Optional<ContextAwarePredicate>,
p_66250_: DeserializationContext
): Instance {
return codec.fromJson(p_66248_, p_286603_, p_66250_)
}
import java.util.*
class HurtTrigger(id: ResourceLocation) : MCriterionTrigger<HurtTrigger.Instance>(id) {
fun trigger(player: ServerPlayer, entity: LivingEntity, damage: Float, damageSource: DamageSource) {
val context = EntityPredicate.createContext(player, entity)
@ -32,14 +22,12 @@ abstract class HurtTrigger : SimpleCriterionTrigger<HurtTrigger.Instance>() {
}
}
val codec = Codec2TriggerSerializer<Instance> { p ->
RecordCodecBuilder.create {
it.group(
p.awareContextCodec.optionalFieldOf("predicate").forGetter(Instance::predicate),
DamagePredicateCodec.optionalFieldOf("damagePredicate").forGetter(Instance::damagePredicate),
p.playerPredicate.forGetter { it.playerPredicate() }
).apply(it, ::Instance)
}
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
predicateCodec.optionalFieldOf("predicate").forGetter(Instance::predicate),
DamagePredicateCodec.optionalFieldOf("damagePredicate").forGetter(Instance::damagePredicate),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
inner class Instance(
@ -52,11 +40,5 @@ abstract class HurtTrigger : SimpleCriterionTrigger<HurtTrigger.Instance>() {
Optional.empty()
)),
player: Optional<ContextAwarePredicate> = Optional.empty()
) : AbstractCriterionTriggerInstance(player) {
override fun serializeToJson(): JsonObject {
return codec.toJson(this)
}
fun criterion() = Criterion(this@HurtTrigger, this)
}
) : AbstractInstance(player)
}

View File

@ -1,44 +1,26 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.data.Codec2TriggerSerializer
import java.util.Optional
import java.util.*
class ItemTrigger(val id: ResourceLocation) : SimpleCriterionTrigger<ItemTrigger.Instance>() {
class ItemTrigger(id: ResourceLocation) : MCriterionTrigger<ItemTrigger.Instance>(id) {
fun trigger(player: ServerPlayer, item: ItemStack) {
trigger(player) { if (it.invert) !it.predicate.matches(item) else it.predicate.matches(item) }
}
val codec = Codec2TriggerSerializer<Instance> { p ->
RecordCodecBuilder.create {
it.group(
ItemPredicate.CODEC.fieldOf("predicate").forGetter(Instance::predicate),
Codec.BOOL.optionalFieldOf("invert", false).forGetter(Instance::invert),
p.playerPredicate.forGetter { it.playerPredicate() }
).apply(it, ::Instance)
}
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
ItemPredicate.CODEC.fieldOf("predicate").forGetter(Instance::predicate),
Codec.BOOL.optionalFieldOf("invert", false).forGetter(Instance::invert),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
override fun createInstance(p_66248_: JsonObject, p_286603_: Optional<ContextAwarePredicate>, p_66250_: DeserializationContext): Instance {
return codec.fromJson(p_66248_, p_286603_, p_66250_)
}
inner class Instance(val predicate: ItemPredicate, val invert: Boolean = false, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractCriterionTriggerInstance(player) {
override fun serializeToJson(): JsonObject {
return codec.toJson(this)
}
fun criterion() = Criterion(this@ItemTrigger, this)
}
inner class Instance(val predicate: ItemPredicate, val invert: Boolean = false, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(player)
}

View File

@ -1,66 +1,67 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.common.collect.ImmutableList
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import net.minecraft.advancements.Criterion
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.critereon.*
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.StringRepresentable
import net.minecraft.world.entity.monster.ElderGuardian
import net.minecraftforge.event.entity.living.LivingDeathEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.core.asIterable
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.stream
import ru.dbotthepony.mc.otm.data.SingletonCodec
import ru.dbotthepony.mc.otm.registry.MRegistry
import java.util.Optional
import java.util.function.Predicate
object KillAsAndroidTrigger : SimpleCriterionTrigger<KillAsAndroidTrigger.Instance>() {
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "kill_as_android")
object KillAsAndroidTrigger : MCriterionTrigger<KillAsAndroidTrigger.Instance>(ResourceLocation(OverdriveThatMatters.MOD_ID, "kill_as_android")) {
val FEATURE_CODEC: Codec<FeaturePredicate> = StringRepresentable.fromEnum(PredicateType::values).dispatch({ it.type }, { it.codec })
override fun createInstance(pJson: JsonObject, pPlayer: Optional<ContextAwarePredicate>, pContext: DeserializationContext): Instance {
return Instance(
predicate = EntityPredicate.fromJson(pJson, "predicate", pContext),
playerPredicate = pPlayer,
featurePredicate = (pJson["feature_predicate"] as? JsonObject)?.let(PredicateType::from) ?: throw JsonSyntaxException("Invalid 'feature_predicate': ${pJson["feature_predicate"]}")
)
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
predicateCodec.optionalFieldOf("entityPredicate").forGetter(Instance::predicate),
FEATURE_CODEC.fieldOf("featurePredicate").forGetter(Instance::featurePredicate),
playerPredicateCodec.forGetter(Instance::playerPredicate)
).apply(it, ::Instance)
}
enum class PredicateType(val factory: (JsonObject) -> FeaturePredicate) {
ALWAYS({ Always }),
HAS(::Has),
NOT(::Not),
AND(::And),
OR(::OrPredicate);
companion object {
fun from(json: JsonObject): FeaturePredicate {
return when (val type = json["type"]?.asString?.lowercase()) {
null -> throw JsonSyntaxException("Missing feature predicate 'type'")
"has" -> HAS.factory(json)
"and" -> AND.factory(json)
"not" -> NOT.factory(json)
"or" -> OR.factory(json)
"always" -> ALWAYS.factory(json)
else -> throw JsonSyntaxException("Unknown feature predicate $type")
}
enum class PredicateType(codec: Lazy<Codec<out FeaturePredicate>>) : StringRepresentable {
ALWAYS(lazy {
SingletonCodec(Always)
}),
HAS(lazy {
RecordCodecBuilder.create<Has> {
it.group(ResourceLocation.CODEC.fieldOf("name").forGetter(Has::name)).apply(it, ::Has)
}
}),
NOT(lazy {
RecordCodecBuilder.create<Not> {
it.group(FEATURE_CODEC.fieldOf("parent").forGetter(Not::parent)).apply(it, ::Not)
}
}),
AND(lazy {
RecordCodecBuilder.create<And> {
it.group(FEATURE_CODEC.listOf().fieldOf("children").forGetter(And::children)).apply(it, ::And)
}
}),
OR(lazy {
RecordCodecBuilder.create<OrPredicate> {
it.group(FEATURE_CODEC.listOf().fieldOf("children").forGetter(OrPredicate::children)).apply(it, ::OrPredicate)
}
});
val codec by codec
override fun getSerializedName(): String {
return name.lowercase()
}
}
abstract class FeaturePredicate : Predicate<MatteryPlayerCapability> {
abstract val type: PredicateType
open fun toJson(): JsonObject {
return JsonObject().also {
it["type"] = type.name.lowercase()
}
}
}
object Always : FeaturePredicate() {
@ -72,17 +73,8 @@ object KillAsAndroidTrigger : SimpleCriterionTrigger<KillAsAndroidTrigger.Instan
}
}
class Has : FeaturePredicate {
class Has(val name: ResourceLocation) : FeaturePredicate() {
private val resolved by lazy { MRegistry.ANDROID_FEATURES.getValue(name) }
val name: ResourceLocation
constructor(name: ResourceLocation) {
this.name = name
}
constructor(value: JsonObject) {
this.name = ResourceLocation.tryParse(value["name"]?.asString ?: throw JsonSyntaxException("Invalid android feature name: ${value["name"]}")) ?: throw JsonSyntaxException("Invalid android feature name ${value["name"]}")
}
override val type: PredicateType
get() = PredicateType.HAS
@ -90,34 +82,18 @@ object KillAsAndroidTrigger : SimpleCriterionTrigger<KillAsAndroidTrigger.Instan
override fun test(t: MatteryPlayerCapability): Boolean {
return t.hasFeature(resolved ?: return false)
}
override fun toJson(): JsonObject {
return super.toJson().also {
it["name"] = name.toString()
}
}
}
class Not(val parent: FeaturePredicate) : FeaturePredicate() {
constructor(input: JsonObject) : this(PredicateType.from(input["parent"] as? JsonObject ?: throw JsonSyntaxException("Invalid parent")))
override val type: PredicateType
get() = PredicateType.NOT
override fun test(t: MatteryPlayerCapability): Boolean {
return !parent.test(t)
}
override fun toJson(): JsonObject {
return super.toJson().also {
it["parent"] = parent.toJson()
}
}
}
class And(children: Iterable<FeaturePredicate>) : FeaturePredicate() {
constructor(input: JsonObject) : this((input["children"] as? JsonArray)?.stream()?.map { PredicateType.from(it as? JsonObject ?: throw JsonSyntaxException("Invalid element in children list: $it")) }?.asIterable() ?: throw JsonSyntaxException("Invalid children list"))
val children: ImmutableList<FeaturePredicate> = ImmutableList.copyOf(children)
override val type: PredicateType
@ -126,21 +102,9 @@ object KillAsAndroidTrigger : SimpleCriterionTrigger<KillAsAndroidTrigger.Instan
override fun test(t: MatteryPlayerCapability): Boolean {
return children.all { it.test(t) }
}
override fun toJson(): JsonObject {
return super.toJson().also {
it["children"] = JsonArray().also {
for (child in children) {
it.add(child.toJson())
}
}
}
}
}
class OrPredicate(children: Iterable<FeaturePredicate>) : FeaturePredicate() {
constructor(input: JsonObject) : this((input["children"] as? JsonArray)?.stream()?.map { PredicateType.from(it as? JsonObject ?: throw JsonSyntaxException("Invalid element in children list: $it")) }?.asIterable() ?: throw JsonSyntaxException("Invalid children list"))
val children: ImmutableList<FeaturePredicate> = ImmutableList.copyOf(children)
override val type: PredicateType
@ -149,32 +113,13 @@ object KillAsAndroidTrigger : SimpleCriterionTrigger<KillAsAndroidTrigger.Instan
override fun test(t: MatteryPlayerCapability): Boolean {
return children.any { it.test(t) }
}
override fun toJson(): JsonObject {
return super.toJson().also {
it["children"] = JsonArray().also {
for (child in children) {
it.add(child.toJson())
}
}
}
}
}
class Instance(
val predicate: Optional<ContextAwarePredicate> = Optional.empty(),
val featurePredicate: FeaturePredicate = Always,
playerPredicate: Optional<ContextAwarePredicate> = Optional.empty(),
) : AbstractCriterionTriggerInstance(playerPredicate) {
override fun serializeToJson(): JsonObject {
return super.serializeToJson().also {
predicate.ifPresent { p -> it["predicate"] = p.toJson() }
it["feature_predicate"] = featurePredicate.toJson()
}
}
fun criterion() = Criterion(KillAsAndroidTrigger, this)
}
) : AbstractInstance(playerPredicate)
fun onKill(event: LivingDeathEvent) {
if (event.entity is ElderGuardian) {

View File

@ -0,0 +1,105 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.google.gson.JsonParseException
import com.mojang.datafixers.util.Pair
import com.mojang.serialization.Codec
import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import com.mojang.serialization.MapCodec
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import it.unimi.dsi.fastutil.objects.Reference2ObjectFunction
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.CriterionTrigger
import net.minecraft.advancements.CriterionTriggerInstance
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.EntityPredicate
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.PlayerAdvancements
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.toImmutableList
import ru.dbotthepony.mc.otm.core.set
import ru.dbotthepony.mc.otm.core.toJsonStrict
import java.util.Optional
import java.util.function.Predicate
// allows to support both 1.20.1 and 1.20.2 with ease
// and has slightly less memory footprint than vanilla SimpleCriterionTrigger
abstract class MCriterionTrigger<T : MCriterionTrigger<T>.AbstractInstance>(val id: ResourceLocation) : CriterionTrigger<T> {
private val listeners = Reference2ObjectOpenHashMap<PlayerAdvancements, ObjectOpenHashSet<CriterionTrigger.Listener<T>>>()
override fun addPlayerListener(advancements: PlayerAdvancements, listener: CriterionTrigger.Listener<T>) {
listeners.computeIfAbsent(advancements, Reference2ObjectFunction { ObjectOpenHashSet() }).add(listener)
}
override fun removePlayerListener(advancements: PlayerAdvancements, listener: CriterionTrigger.Listener<T>) {
listeners[advancements]?.remove(listener)
}
override fun removePlayerListeners(advancements: PlayerAdvancements) {
listeners.remove(advancements)
}
protected abstract val codec: Codec<T>
override fun createInstance(data: JsonObject, context: DeserializationContext): T {
return try {
deserializationContext.get().addLast(context)
codec.decode(JsonOps.INSTANCE, data).getOrThrow(false) { throw JsonParseException("Failed to decode data: $it") }.first
} finally {
deserializationContext.get().removeLast()
}
}
fun trigger(player: ServerPlayer, predicate: Predicate<T> = Predicate { true }) {
val advancements = player.advancements
val listeners = listeners[advancements] ?: return
val context = EntityPredicate.createContext(player, player)
listeners.iterator()
.filter { predicate.test(it.trigger) && it.trigger.playerPredicate.map { it.matches(context) }.orElse(true) }
.toImmutableList()
.forEach { it.run(advancements) }
}
abstract inner class AbstractInstance(val playerPredicate: Optional<ContextAwarePredicate>) : CriterionTriggerInstance {
fun criterion() = Criterion(this@MCriterionTrigger, this as T)
final override fun serializeToJson(): JsonObject {
return codec.toJsonStrict(this as T) as JsonObject
}
}
companion object {
protected val deserializationContext = object : ThreadLocal<ArrayDeque<DeserializationContext>>() {
override fun initialValue(): ArrayDeque<DeserializationContext> {
return ArrayDeque()
}
}
@JvmStatic
protected val predicateCodec: Codec<ContextAwarePredicate> = object : Codec<ContextAwarePredicate> {
override fun <T : Any?> encode(input: ContextAwarePredicate, ops: DynamicOps<T>, prefix: T): DataResult<T> {
return DataResult.success(JsonOps.INSTANCE.convertTo(ops, input.toJson()))
}
override fun <T : Any?> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<ContextAwarePredicate, T>> {
val context = deserializationContext.get().lastOrNull() ?: return DataResult.error { "Not current deserializing trigger instance" }
return try {
DataResult.success(Pair.of(EntityPredicate.fromJson(JsonObject().also { it["a"] = ops.convertTo(JsonOps.INSTANCE, input) }, "a", context).get(), ops.empty()))
} catch (err: Exception) {
DataResult.error { "Failed to deserialize ContextAwarePredicate: " + err.message }
}
}
}
@JvmStatic
protected val playerPredicateCodec: MapCodec<Optional<ContextAwarePredicate>> = predicateCodec.optionalFieldOf("player")
}
}

View File

@ -1,8 +0,0 @@
package ru.dbotthepony.mc.otm.triggers
import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.OverdriveThatMatters
object NailedEntityTrigger : HurtTrigger() {
override val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "hammer_nail_damage")
}

View File

@ -1,45 +1,25 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.*
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.MinMaxBounds.Doubles
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.data.Codec2TriggerSerializer
import java.util.Optional
import java.util.*
object NanobotsArmorTrigger : SimpleCriterionTrigger<NanobotsArmorTrigger.Instance>() {
val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor")
override fun createInstance(
p_66248_: JsonObject,
p_286603_: Optional<ContextAwarePredicate>,
p_66250_: DeserializationContext
): Instance {
return codec.fromJson(p_66248_, p_286603_, p_66250_)
}
val codec = Codec2TriggerSerializer<Instance> { p ->
RecordCodecBuilder.create {
it.group(
Doubles.CODEC.fieldOf("predicate").forGetter(Instance::predicate),
p.playerPredicate.forGetter { it.playerPredicate() },
).apply(it, ::Instance)
}
object NanobotsArmorTrigger : MCriterionTrigger<NanobotsArmorTrigger.Instance>(ResourceLocation(OverdriveThatMatters.MOD_ID, "nanobots_armor")) {
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(
Doubles.CODEC.fieldOf("predicate").forGetter(Instance::predicate),
playerPredicateCodec.forGetter(Instance::playerPredicate),
).apply(it, ::Instance)
}
fun trigger(player: ServerPlayer, damageAbsorbed: Double) {
trigger(player) { it.predicate.matches(damageAbsorbed) }
}
class Instance(val predicate: Doubles, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractCriterionTriggerInstance(player) {
override fun serializeToJson(): JsonObject {
return codec.toJson((this))
}
fun criterion() = Criterion(NanobotsArmorTrigger, this)
}
class Instance(val predicate: Doubles, player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(player)
}

View File

@ -1,8 +0,0 @@
package ru.dbotthepony.mc.otm.triggers
import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.OverdriveThatMatters
object ShockwaveDamageMobTrigger : HurtTrigger() {
override val id = ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave_damage_mob")
}

View File

@ -15,3 +15,6 @@ val BecomeHumaneTrigger = SingletonTrigger(ResourceLocation(OverdriveThatMatters
val AndroidBatteryTrigger = ItemTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_battery"))
val TakeItemOutOfReplicatorTrigger = ItemTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "take_item_out_of_replicator"))
val NailedEntityTrigger = HurtTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "hammer_nail_damage"))
val ShockwaveDamageMobTrigger = HurtTrigger(ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave_damage_mob"))

View File

@ -1,42 +1,19 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.Criterion
import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.SimpleCriterionTrigger
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import ru.dbotthepony.mc.otm.data.Codec2TriggerSerializer
import java.util.Optional
import java.util.*
class SingletonTrigger(val id: ResourceLocation) : SimpleCriterionTrigger<AbstractCriterionTriggerInstance>() {
override fun createInstance(p_66248_: JsonObject, p_286603_: Optional<ContextAwarePredicate>, p_66250_: DeserializationContext): AbstractCriterionTriggerInstance {
return codec.fromJson(p_66248_, p_286603_, p_66250_)
}
fun trigger(player: ServerPlayer) {
trigger(player) { true }
}
val codec = Codec2TriggerSerializer<Instance> { p ->
RecordCodecBuilder.create {
it.group(
p.playerPredicate.forGetter { it.playerPredicate() }
).apply(it, ::Instance)
}
class SingletonTrigger(id: ResourceLocation) : MCriterionTrigger<SingletonTrigger.Instance>(id) {
override val codec: Codec<Instance> = RecordCodecBuilder.create {
it.group(playerPredicateCodec.forGetter(Instance::playerPredicate)).apply(it, ::Instance)
}
val empty = Instance()
val criterion = Criterion(this@SingletonTrigger, empty)
inner class Instance(player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractCriterionTriggerInstance(player) {
override fun serializeToJson(): JsonObject {
return codec.toJson(this)
}
fun criterion() = Criterion(this@SingletonTrigger, this)
}
inner class Instance(player: Optional<ContextAwarePredicate> = Optional.empty()) : AbstractInstance(player)
}