Make Exopack slot changes trigger advancements, and optimize them

This commit is contained in:
DBotThePony 2023-08-04 22:50:59 +07:00
parent 7662c21f44
commit f27f21e3de
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 440 additions and 13 deletions

View File

@ -0,0 +1,22 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import ru.dbotthepony.mc.otm.triggers.MatteryInventoryChangeTrigger;
@Mixin(InventoryChangeTrigger.class)
public abstract class InventoryChangeTriggerMixin {
@Overwrite
public void trigger(ServerPlayer p_43150_, Inventory p_43151_, ItemStack p_43152_) {
MatteryInventoryChangeTrigger.INSTANCE.trigger(p_43150_, p_43151_, p_43152_);
}
@Overwrite
private void trigger(ServerPlayer p_43154_, Inventory p_43155_, ItemStack p_43156_, int p_43157_, int p_43158_, int p_43159_) {
MatteryInventoryChangeTrigger.INSTANCE.trigger(p_43154_, p_43155_, p_43156_, p_43157_, p_43158_, p_43159_);
}
}

View File

@ -0,0 +1,54 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.advancements.CriterionTrigger;
import net.minecraft.advancements.CriterionTriggerInstance;
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
import net.minecraft.advancements.critereon.SimpleCriterionTrigger;
import net.minecraft.server.PlayerAdvancements;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import ru.dbotthepony.mc.otm.triggers.MatteryInventoryChangeTrigger;
// i tried to mixin into InventoryChangeTrigger with extends SimpleCriterionTrigger and @Overwrite+@Override
// while also defining SimpleCriterionTrigger methods non final in accesstransfoer
// it didn't work.
@Mixin(SimpleCriterionTrigger.class)
public abstract class SimpleCriterionTriggerMixin implements CriterionTrigger<CriterionTriggerInstance> {
@Inject(
method = "removePlayerListener(Lnet/minecraft/server/PlayerAdvancements;Lnet/minecraft/advancements/CriterionTrigger$Listener;)V",
at = @At("HEAD"),
cancellable = true
)
public void removePlayerListener(PlayerAdvancements p_66254_, CriterionTrigger.Listener p_66255_, CallbackInfo info) {
if (((Object) this) instanceof InventoryChangeTrigger) {
MatteryInventoryChangeTrigger.INSTANCE.removePlayerListener(p_66254_, p_66255_);
info.cancel();
}
}
@Inject(
method = "addPlayerListener(Lnet/minecraft/server/PlayerAdvancements;Lnet/minecraft/advancements/CriterionTrigger$Listener;)V",
at = @At("HEAD"),
cancellable = true
)
public void addPlayerListener(PlayerAdvancements p_66254_, CriterionTrigger.Listener p_66255_, CallbackInfo info) {
if (((Object) this) instanceof InventoryChangeTrigger) {
MatteryInventoryChangeTrigger.INSTANCE.addPlayerListener(p_66254_, p_66255_);
info.cancel();
}
}
@Inject(
method = "removePlayerListeners(Lnet/minecraft/server/PlayerAdvancements;)V",
at = @At("HEAD"),
cancellable = true
)
public void removePlayerListeners(PlayerAdvancements p_66254_, CallbackInfo info) {
if (((Object) this) instanceof InventoryChangeTrigger) {
MatteryInventoryChangeTrigger.INSTANCE.removePlayerListeners(p_66254_);
info.cancel();
}
}
}

View File

@ -79,6 +79,7 @@ import ru.dbotthepony.mc.otm.capability.energy.receiveEnergyExact
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.config.ExopackConfig
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.iterator
@ -122,6 +123,7 @@ import ru.dbotthepony.mc.otm.triggers.ExopackGainedEnderAccessTrigger
import ru.dbotthepony.mc.otm.triggers.ExopackGainedSmeltingTrigger
import ru.dbotthepony.mc.otm.triggers.ExopackObtainedTrigger
import ru.dbotthepony.mc.otm.triggers.ExopackSlotsExpandedTrigger
import ru.dbotthepony.mc.otm.triggers.MatteryInventoryChangeTrigger
import java.util.*
import java.util.stream.Stream
import kotlin.collections.ArrayDeque
@ -170,12 +172,13 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
private inner class PlayerMatteryContainer(size: Int) : MatteryContainer(size) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
// TODO: Minecraft has hard-coded inventory triggers to Inventory class
// when nothing use Inventory class directly.
if (ply is ServerPlayer) {
val item = new.copy()
// Why?
// CriteriaTriggers.INVENTORY_CHANGED.trigger(this, this.getInventory(), p_143468_)
tickList.once {
MatteryInventoryChangeTrigger.trigger(ply, combinedInventory, item)
}
}
}
}
@ -300,6 +303,18 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
value.deserializeNBT(field.serializeNBT())
value.addFilterSynchronizer(synchronizer)
field = value
_combinedInventory = CombinedContainer(ply.inventory, exopackContainer)
}
private var _combinedInventory: CombinedContainer? = null
val combinedInventory: CombinedContainer
get() {
if (_combinedInventory == null)
_combinedInventory = CombinedContainer(ply.inventory, exopackContainer)
return _combinedInventory!!
}
/**
@ -1065,6 +1080,8 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
ticksIExist++
tickList.tick()
if (willBecomeAndroid) {
if (ply.isSleeping && ply.sleepTimer > SLEEP_TICKS_LIMIT) {
becomeAndroid()

View File

@ -12,6 +12,10 @@ import net.minecraft.world.Container
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.flatMap
import ru.dbotthepony.mc.otm.core.collect.map
import ru.dbotthepony.mc.otm.core.isNotEmpty
import ru.dbotthepony.mc.otm.core.stream
import java.util.LinkedList
import java.util.stream.Stream
@ -162,6 +166,13 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterator<Int>>>) : Co
return true
}
fun optimizedIterator(): Iterator<ItemStack> {
return listOf(
fullCoverage.iterator().flatMap { it.iterator() },
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.filter { it.isNotEmpty }
).iterator().flatMap { it }
}
class Builder {
private var built = false
private val values = LinkedList<Pair<Container, Iterator<Int>>>()

View File

@ -47,6 +47,9 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
private val nonEmptyFlags = BitSet()
private var nonEmptyIndices = IntArrayList()
private var indicesReferenced = false
private data class Update(val slot: Int, val new: ItemStack, val old: ItemStack)
private val queuedUpdates = ArrayList<Update>()
private var queueUpdates = false
private fun cowIndices() {
if (indicesReferenced) {
@ -224,6 +227,22 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
}
}
private fun internalSetChanged(slot: Int, new: ItemStack, old: ItemStack) {
if (queueUpdates) {
queuedUpdates.add(Update(slot, new, old))
} else {
setChanged(slot, new, old)
}
}
private fun runUpdates() {
for ((slot, new, old) in queuedUpdates) {
setChanged(slot, new, old)
}
queuedUpdates.clear()
}
protected open fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
watcher.run()
}
@ -297,7 +316,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
slotStack.count = newCount
trackedSlots[slot] = slotStack.copy()
changeset++
setChanged(slot, slotStack, old)
internalSetChanged(slot, slotStack, old)
if (popTime != null) {
slotStack.popTime = popTime
@ -326,7 +345,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
trackedSlots[slot] = copyToPut.copy()
updateEmptyFlag(slot)
changeset++
setChanged(slot, copyToPut, ItemStack.EMPTY)
internalSetChanged(slot, copyToPut, ItemStack.EMPTY)
if (popTime != null) {
copyToPut.popTime = popTime
@ -430,7 +449,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
trackedSlots[slot] = slots[slot].copy()
changeset++
updateEmptyFlag(slot)
setChanged(slot, if (slots[slot].isEmpty) ItemStack.EMPTY else slots[slot], old)
internalSetChanged(slot, if (slots[slot].isEmpty) ItemStack.EMPTY else slots[slot], old)
return split
}
@ -458,12 +477,21 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
updateEmptyFlag(slot)
changeset++
setChanged(slot, stack, old)
internalSetChanged(slot, stack, old)
}
final override fun setChanged() {
for (slot in 0 until size) {
setChanged(slot)
queueUpdates = true
try {
for (slot in 0 until size) {
setChanged(slot)
}
runUpdates()
} finally {
queuedUpdates.clear()
queueUpdates = false
}
}
@ -472,7 +500,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
trackedSlots[slot] = slots[slot].copy()
updateEmptyFlag(slot)
changeset++
setChanged(slot, slots[slot], trackedSlots[slot])
internalSetChanged(slot, slots[slot], trackedSlots[slot])
// mojang соси))0)0))0)))))0)
}
}
@ -507,7 +535,7 @@ open class MatteryContainer(protected val watcher: Runnable, private val size: I
if (!slots[slot].isEmpty) {
val old = slots[slot]
slots[slot] = ItemStack.EMPTY
setChanged(slot, ItemStack.EMPTY, old)
internalSetChanged(slot, ItemStack.EMPTY, old)
}
}
}

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.core.collect
import ru.dbotthepony.mc.otm.core.addAll
import java.util.Optional
import java.util.function.BinaryOperator
import java.util.function.Predicate
@ -348,6 +349,12 @@ fun <T, A, R> Iterator<T>.collect(collector: Collector<T, A, R>): R {
return collector.finisher().apply(instance)
}
fun <T> Iterator<T>.toList(): List<T> {
val result = ArrayList<T>()
result.addAll(this)
return result
}
fun <T : Any> Iterator<T>.findFirst(): Optional<T> {
if (hasNext()) {
return Optional.of(next())

View File

@ -0,0 +1,269 @@
package ru.dbotthepony.mc.otm.triggers
import com.google.gson.JsonObject
import it.unimi.dsi.fastutil.Hash.Strategy
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ObjectArraySet
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.CriteriaTriggers
import net.minecraft.advancements.CriterionTrigger
import net.minecraft.advancements.critereon.DeserializationContext
import net.minecraft.advancements.critereon.InventoryChangeTrigger
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.PlayerAdvancements
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.container.CombinedContainer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.iterator
import ru.dbotthepony.mc.otm.core.collect.flatMap
import ru.dbotthepony.mc.otm.core.collect.toList
import ru.dbotthepony.mc.otm.core.isNotEmpty
import java.util.stream.Collectors
/**
* This object detours all necessary InventoryChangeTrigger methods
*
* Reason behind this is to support arbitrary containers (not just [Inventory], done for Exopack) and to improve performance by using search tree
*/
object MatteryInventoryChangeTrigger : CriterionTrigger<InventoryChangeTrigger.TriggerInstance> {
private object BoundsStrategy : Strategy<MinMaxBounds.Ints?> {
override fun equals(a: MinMaxBounds.Ints?, b: MinMaxBounds.Ints?): Boolean {
return a?.min == b?.min && a?.max == b?.max
}
override fun hashCode(o: MinMaxBounds.Ints?): Int {
return o?.let { Integer.rotateLeft(it.min.hashCode(), 16) xor it.max.hashCode() } ?: 0
}
}
private object DefaultStrategy : Strategy<Any?> {
override fun equals(a: Any?, b: Any?): Boolean {
return a == b
}
override fun hashCode(o: Any?): Int {
return o.hashCode()
}
}
private fun interface Tester<T> {
fun test(value: T, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int): Boolean
}
private fun interface Hint<T> {
fun getHints(inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int): Collection<T>?
}
private class Node<T>(
val strategy: Strategy<in T?>,
private val getter: InventoryChangeTrigger.TriggerInstance.() -> Collection<T>,
val test: Tester<T>,
val hint: Hint<T>? = null
) {
fun getValues(instance: InventoryChangeTrigger.TriggerInstance): Set<T> {
val result = ObjectArraySet<T>()
result.addAll(getter.invoke(instance))
return result
}
}
private val nodes = ArrayList<Node<*>>()
init {
nodes.add(Node(BoundsStrategy, { listOf(slotsOccupied) }, { v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v.matches(slotsOccupied) }))
nodes.add(Node(BoundsStrategy, { listOf(slotsFull) }, { v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v.matches(slotsFull) }))
nodes.add(Node(BoundsStrategy, { listOf(slotsEmpty) }, { v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v.matches(slotsEmpty) }))
nodes.add(Node(
DefaultStrategy,
{ predicates.iterator().flatMap { (it.items ?: listOf(null)).iterator() }.toList() },
{ v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v == null || item.item == v },
{ inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> mutableListOf(item.item) }))
nodes.add(Node(
DefaultStrategy,
{ predicates.map { it.tag } },
{ v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v == null || item.`is`(v) },
{ inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> item.tags.collect(Collectors.toCollection(::ArrayList)) }))
nodes.add(Node(BoundsStrategy, { predicates.map { it.count } }, { v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v.matches(item.count) }))
nodes.add(Node(
BoundsStrategy,
{ predicates.map { it.durability } },
{ v, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int -> v.isAny || item.isDamageableItem && v.matches(item.maxDamage - item.damageValue) }))
}
private class ListenerTree(private val advancements: PlayerAdvancements) {
private val set = ObjectOpenHashSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>()
private val tree = Object2ObjectOpenCustomHashMap<Any?, Any>(nodes.first().strategy as Strategy<in Any?>)
private fun search(instance: InventoryChangeTrigger.TriggerInstance, tree: MutableMap<Any?, Any>, nodeId: Int): Collection<MutableSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>> {
val node = nodes[nodeId]
if (nodeId + 1 != nodes.size) {
val result = ArrayList<MutableSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>>()
for (v in node.getValues(instance)) {
result.addAll(
search(
instance,
tree.computeIfAbsent(v, Object2ObjectFunction { Object2ObjectOpenCustomHashMap<Any, Any>(nodes[nodeId + 1].strategy as Strategy<in Any?>) }) as MutableMap<Any?, Any>,
nodeId + 1))
}
return result
} else {
val result = ArrayList<MutableSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>>()
for (v in node.getValues(instance)) {
result.add(tree.computeIfAbsent(v, Object2ObjectFunction { ObjectOpenHashSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>() }) as MutableSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>)
}
return result
}
}
fun add(listener: CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>) {
if (set.add(listener)) {
search(listener.triggerInstance, tree, 0).forEach { it.add(listener) }
}
}
fun remove(listener: CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>) {
if (set.remove(listener)) {
search(listener.triggerInstance, tree, 0).forEach { it.remove(listener) }
}
}
private fun addNull(input: Collection<Any?>): Collection<Any?> {
return if (null in input)
input
else if (input is ArrayList) {
input.add(null)
input
} else {
ArrayList(input).also { it.add(null) }
}
}
private fun trigger(inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int, tree: MutableMap<Any?, Any>, nodeId: Int, set: MutableSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>) {
val node = nodes[nodeId] as Node<Any?>
val keys = node.hint?.getHints(inventory, item, slotsFull, slotsEmpty, slotsOccupied)?.let(::addNull) ?: tree.keys
for (k in keys) {
val v = tree[k] ?: continue
if (node.test.test(k, inventory, item, slotsFull, slotsEmpty, slotsOccupied)) {
if (nodeId + 1 == nodes.size) {
for (l in v as Set<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>) {
// переделываем matches у InventoryTriggerInstance
with (l.triggerInstance) {
if (
this.slotsFull.matches(slotsFull) &&
this.slotsEmpty.matches(slotsEmpty) &&
this.slotsOccupied.matches(slotsOccupied)
) {
if (this.predicates.isEmpty() || this.predicates.size == 1 && item.isNotEmpty && this.predicates[0].matches(item)) {
set.add(l)
} else if (this.predicates.size > 1) {
val unsatisfied = ObjectArrayList(this.predicates)
unsatisfied.removeIf { it.matches(ItemStack.EMPTY) }
val iterator = if (inventory is CombinedContainer) inventory.optimizedIterator() else inventory.iterator()
for (inventoryItem in iterator) {
unsatisfied.removeIf { it.matches(inventoryItem) }
if (unsatisfied.isEmpty) break
}
if (unsatisfied.isEmpty) {
set.add(l)
}
}
}
}
}
} else {
trigger(inventory, item, slotsFull, slotsEmpty, slotsOccupied, v as MutableMap<Any?, Any>, nodeId + 1, set)
}
}
}
}
fun trigger(inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int) {
val matches = ObjectOpenHashSet<CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>>()
trigger(inventory, item, slotsFull, slotsEmpty, slotsOccupied, tree, 0, matches)
for (l in matches) {
l.run(advancements)
}
}
}
private val listeners = Reference2ObjectOpenHashMap<PlayerAdvancements, ListenerTree>()
override fun getId(): ResourceLocation {
return CriteriaTriggers.INVENTORY_CHANGED.id
}
override fun addPlayerListener(advancements: PlayerAdvancements, instance: CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>) {
listeners.computeIfAbsent(advancements, Reference2ObjectFunction { ListenerTree(it as PlayerAdvancements) }).add(instance)
}
override fun removePlayerListener(advancements: PlayerAdvancements, instance: CriterionTrigger.Listener<InventoryChangeTrigger.TriggerInstance>) {
listeners[advancements]?.remove(instance)
}
override fun removePlayerListeners(advancements: PlayerAdvancements) {
listeners.remove(advancements)
}
override fun createInstance(data: JsonObject, context: DeserializationContext): InventoryChangeTrigger.TriggerInstance {
return CriteriaTriggers.INVENTORY_CHANGED.createInstance(data, context)
}
// реплицирует ванильный метод
fun trigger(player: ServerPlayer, inventory: Container, item: ItemStack) {
if (inventory === player.inventory) {
val mattery = player.matteryPlayer
if (mattery != null) {
return trigger(player, mattery.combinedInventory, item)
}
}
var slotsFull = 0
var slotsEmpty = 0
var slotsOccupied = 0
for (slot in 0 until inventory.containerSize) {
val slotItem = inventory[slot]
if (slotItem.isEmpty)
slotsEmpty++
else {
slotsOccupied++
if (slotItem.count >= inventory.maxStackSize)
slotsFull++
}
}
trigger(player, inventory, item, slotsFull, slotsEmpty, slotsOccupied)
}
// реплицирует ванильный метод
fun trigger(player: ServerPlayer, inventory: Container, item: ItemStack, slotsFull: Int, slotsEmpty: Int, slotsOccupied: Int) {
listeners[player.advancements]?.trigger(inventory, item, slotsFull, slotsEmpty, slotsOccupied)
}
}

View File

@ -186,3 +186,20 @@ public net.minecraft.world.item.crafting.RecipeManager m_44054_(Lnet/minecraft/w
public net.minecraft.world.entity.boss.wither.WitherBoss f_31432_ # TARGETING_CONDITIONS
public-f net.minecraft.world.entity.boss.wither.WitherBoss f_31431_ # LIVING_ENTITY_SELECTOR
public net.minecraft.world.entity.ai.targeting.TargetingConditions f_26879_ # selector
public net.minecraft.advancements.critereon.InventoryChangeTrigger$TriggerInstance f_43179_
public net.minecraft.advancements.critereon.InventoryChangeTrigger$TriggerInstance f_43178_
public net.minecraft.advancements.critereon.InventoryChangeTrigger$TriggerInstance f_43177_
public net.minecraft.advancements.critereon.InventoryChangeTrigger$TriggerInstance f_43176_
#public-f net.minecraft.advancements.critereon.SimpleCriterionTrigger m_6467_(Lnet/minecraft/server/PlayerAdvancements;Lnet/minecraft/advancements/CriterionTrigger$Listener;)V # addPlayerListener
#public-f net.minecraft.advancements.critereon.SimpleCriterionTrigger m_6468_(Lnet/minecraft/server/PlayerAdvancements;Lnet/minecraft/advancements/CriterionTrigger$Listener;)V # removePlayerListener
#public-f net.minecraft.advancements.critereon.SimpleCriterionTrigger m_5656_(Lnet/minecraft/server/PlayerAdvancements;)V # removePlayerListeners
public net.minecraft.advancements.critereon.ItemPredicate f_45031_
public net.minecraft.advancements.critereon.ItemPredicate f_45032_
public net.minecraft.advancements.critereon.ItemPredicate f_45033_
public net.minecraft.advancements.critereon.ItemPredicate f_151427_
public net.minecraft.advancements.critereon.ItemPredicate f_45036_
public net.minecraft.advancements.critereon.ItemPredicate f_45035_
public net.minecraft.advancements.critereon.ItemPredicate f_45034_
public net.minecraft.advancements.critereon.ItemPredicate f_45029_

View File

@ -13,6 +13,8 @@
"MixinAnvilBlock",
"MixinInventory",
"MixinAbstractHurtingProjectile",
"SimpleCriterionTriggerMixin",
"InventoryChangeTriggerMixin",
"MixinPlayer"
],
"client": [