Inventory filters preview

This commit is contained in:
DBotThePony 2023-03-16 07:56:48 +07:00
parent 3354c698c4
commit 30f07e2fed
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 615 additions and 263 deletions

View File

@ -0,0 +1,49 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.ForgeRegistries;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
@Mixin(Inventory.class)
public class MixinInventory {
@Shadow Player player;
@Inject(
method = "add(ILnet/minecraft/world/item/ItemStack;)Z",
at = @At("HEAD"),
cancellable = true
)
private void add(int pSlot, ItemStack pStack, CallbackInfoReturnable<Boolean> hook) {
if (pStack.isEmpty()) {
return;
}
if (pSlot == -1) {
this.player.getCapability(MatteryCapability.MATTERY_PLAYER).ifPresent(it -> {
try {
hook.setReturnValue(it.inventoryAddImpl(pStack));
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Adding item to inventory (Overdrive That Matters detour)");
CrashReportCategory crashreportcategory = crashreport.addCategory("Item being added");
crashreportcategory.setDetail("Registry Name", () -> String.valueOf(ForgeRegistries.ITEMS.getKey(pStack.getItem())));
crashreportcategory.setDetail("Item Class", () -> pStack.getItem().getClass().getName());
crashreportcategory.setDetail("Item ID", Item.getId(pStack.getItem()));
crashreportcategory.setDetail("Item data", pStack.getDamageValue());
crashreportcategory.setDetail("Item name", () -> pStack.getHoverName().getString());
throw new ReportedException(crashreport);
}
});
}
}
}

View File

@ -1,10 +1,12 @@
package ru.dbotthepony.mc.otm.capability
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.ChatFormatting
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NumericTag
import net.minecraft.nbt.StringTag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
@ -18,7 +20,9 @@ import net.minecraft.world.entity.boss.wither.WitherBoss
import net.minecraft.world.entity.monster.Phantom
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.ProjectileWeaponItem
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasVanishingCurse
import net.minecraft.world.level.Level
@ -58,11 +62,15 @@ import ru.dbotthepony.mc.otm.core.collect.nonEmpty
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.nbt.getCompoundList
import ru.dbotthepony.mc.otm.core.nbt.getStringList
import ru.dbotthepony.mc.otm.core.nbt.map
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.util.IntValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemStackValueCodec
import ru.dbotthepony.mc.otm.core.util.ItemValueCodec
import ru.dbotthepony.mc.otm.core.util.Savetables
import ru.dbotthepony.mc.otm.core.util.UUIDValueCodec
import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
import ru.dbotthepony.mc.otm.network.*
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
@ -108,6 +116,19 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
val level: Level get() = capability.ply.level
}
/**
* This event is fired on main event bus when [Inventory.add] was called and entire [ItemStack] can not be stored
* (both in [Inventory] and [MatteryPlayerCapability] exopack due to inventory filters or no free space).
*/
data class ItemStackLeftoverEvent(val stack: ItemStack, val capability: MatteryPlayerCapability) : Event() {
override fun isCancelable() = false
override fun hasResult() = false
override fun setResult(value: Result) {}
val player get() = capability.ply
val level: Level get() = capability.ply.level
}
private inner class PlayerMatteryContainer(size: Int) : MatteryContainer(size) {
override fun setChanged(slot: Int, new: ItemStack, old: ItemStack) {
super.setChanged(slot, new, old)
@ -181,6 +202,16 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
}
}, backingMap = this.exoPackSlotModifierMap)
val regularSlotFilters = immutableList(Inventory.INVENTORY_SIZE) {
synchronizer.Field(null, ItemValueCodec.nullable)
}
val exoPackSlotFilters by synchronizer.Map(
keyCodec = VarIntValueCodec,
valueCodec = ItemValueCodec,
backingMap = Int2ObjectOpenHashMap()
)
/**
* Current slot count of Exopack
*
@ -291,15 +322,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
private var wasInLiquid = false
private var lastDimension = ResourceLocation("overworld")
init {
savetables.int(::ticksIExist)
savetables.int(::iteration)
savetables.bool(::shouldSendIteration)
savetables.bool(::wasInLiquid)
savetables.vector(::lastOutsideLiquid)
savetables.location(::lastDimension)
}
/**
* Whenever player should become an Android once transformation conditions are met (e.g. player dies or sleeps in bed)
*/
@ -324,6 +346,26 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
*/
val androidEnergy = AndroidPowerSource(ply, synchronizer, AndroidConfig.ANDROID_MAX_ENERGY, AndroidConfig.ANDROID_MAX_ENERGY)
init {
savetables.int(::ticksIExist)
savetables.int(::iteration)
savetables.bool(::shouldSendIteration)
savetables.bool(::wasInLiquid)
savetables.bool(::isAndroid)
savetables.bool(::willBecomeAndroid)
savetables.bool(::hasExoPack, "hasExoSuit")
savetables.bool(::displayExoPack, "displayExoSuit")
savetables.bool(::isExoPackCraftingUpgraded, "isExoSuitCraftingUpgraded")
savetables.vector(::lastOutsideLiquid)
savetables.location(::lastDimension)
savetables.stateful(::exoPackSlotModifier, "exoSuitSlotCountModifiers")
savetables.stateful(::exoPackContainer, "exoSuitContainer")
savetables.stateful(::androidEnergy)
}
fun invalidateNetworkState() {
synchronizer.invalidate()
publicSynchronizer.invalidate()
@ -641,36 +683,36 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
}
}
// exosuit
tag["hasExoSuit"] = hasExoPack
tag["displayExoSuit"] = displayExoPack
tag["exoSuitContainer"] = exoPackContainer.serializeNBT()
tag["isExoSuitCraftingUpgraded"] = isExoPackCraftingUpgraded
tag["exoSuitSlotCountModifiers"] = exoPackSlotModifier.serializeNBT()
// android
tag["androidEnergy"] = androidEnergy.serializeNBT()
tag["isAndroid"] = isAndroid
tag["willBecomeAndroid"] = willBecomeAndroid
val featureList = ListTag()
val researchList = ListTag()
for (feature in featureMap.values) {
featureList.add(feature.serializeNBT().also {
it["id"] = feature.type.registryName!!.toString()
})
tag["features"] = ListTag().also {
for (feature in featureMap.values) {
it.add(feature.serializeNBT().also {
it["id"] = feature.type.registryName!!.toString()
})
}
}
for ((type, instance) in research) {
researchList.add(instance.serializeNBT().also {
it["id"] = type.id.toString()
})
tag["research"] = ListTag().also {
for ((type, instance) in research) {
it.add(instance.serializeNBT().also {
it["id"] = type.id.toString()
})
}
}
tag["features"] = featureList
tag["research"] = researchList
tag["exoPackSlotFilters"] = ListTag().also {
for ((slot, filter) in exoPackSlotFilters) {
it.add(CompoundTag().also {
it["slot"] = slot
it["filter"] = filter.registryName!!.toString()
})
}
}
tag["regularSlotFilters"] = ListTag().also {
for (filter in regularSlotFilters) {
it.add(StringTag.valueOf(filter.value?.registryName?.toString() ?: ""))
}
}
return tag
}
@ -678,6 +720,29 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
override fun deserializeNBT(tag: CompoundTag) {
savetables.deserializeNBT(tag)
for (filter in regularSlotFilters) {
filter.value = null
}
exoPackSlotFilters.clear()
for (elem in tag.getCompoundList("exoPackSlotFilters")) {
val slot = (elem["slot"] as? NumericTag)?.asInt ?: continue
val filter = (elem["filter"] as? StringTag)?.asString ?: continue
if (slot >= 0) {
exoPackSlotFilters[slot] = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(filter) ?: continue) ?: Items.AIR
}
}
val regularSlotFilters = tag.getStringList("regularSlotFilters")
for (i in 0 until regularSlotFilters.size.coerceAtMost(this.regularSlotFilters.size)) {
val path = regularSlotFilters[i].asString
if (path == "") continue
this.regularSlotFilters[i].value = ForgeRegistries.ITEMS.getValue(ResourceLocation.tryParse(path) ?: continue) ?: Items.AIR
}
// iterations
deathLog.clear()
@ -689,20 +754,6 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
}
}
// exosuit
hasExoPack = tag.getBoolean("hasExoSuit")
displayExoPack = tag.getBoolean("displayExoSuit")
tag.map("exoSuitSlotCountModifiers", exoPackSlotModifier::deserializeNBT)
exoPackContainer.deserializeNBT(tag["exoSuitContainer"])
isExoPackCraftingUpgraded = tag.getBoolean("isExoSuitCraftingUpgraded")
// android
isAndroid = tag.getBoolean("isAndroid")
willBecomeAndroid = tag.getBoolean("willBecomeAndroid")
tag.map("androidEnergy", androidEnergy::deserializeNBT)
featureMap.clear()
research.clear()
@ -977,8 +1028,141 @@ class MatteryPlayerCapability(val ply: Player) : ICapabilityProvider, INBTSerial
} else LazyOptional.empty()
}
/**
* This re-implement [Inventory.add] logic (original method is redirected to this)
*/
fun inventoryAddImpl(stack: ItemStack): Boolean {
val items = ply.inventory.items
val exoPackContainer = exoPackContainer
if (!stack.isStackable) {
for (i in items.indices) {
if (items[i].isEmpty && (regularSlotFilters[i].value === null || regularSlotFilters[i].value === stack.item)) {
items[i] = stack.copy()
items[i].popTime = 5
stack.count = 0
return true
}
}
for (i in 0 until exoPackContainer.containerSize) {
if (exoPackContainer[i].isEmpty && (exoPackSlotFilters[i] === null || exoPackSlotFilters[i] === stack.item)) {
exoPackContainer[i] = stack.copy()
exoPackContainer[i].popTime = 5
stack.count = 0
return true
}
}
MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this))
if (ply.abilities.instabuild) {
stack.count = 0
}
return !stack.isEmpty
}
// двигаем в существующие слоты
items[ply.inventory.selected].also { stackStacks(it, stack) }
if (stack.isEmpty) return true
ply.inventory.offhand[0].also { stackStacks(it, stack) }
if (stack.isEmpty) return true
for (i in items.indices) {
if (stackStacks(items[i], stack) && stack.isEmpty) {
return true
}
}
for (i in 0 until exoPackContainer.containerSize) {
if (stackStacks(exoPackContainer[i], stack)) {
exoPackContainer.setChanged(i)
if (stack.isEmpty) {
return true
}
}
}
// двигаем в пустые слоты
// двигаем в отфильтрованные слоты
for (i in items.indices) {
if (items[i].isEmpty && regularSlotFilters[i].value === stack.item) {
items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
items[i].popTime = 5
if (stack.isEmpty) {
return true
}
}
}
for (i in 0 until exoPackContainer.containerSize) {
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === stack.item) {
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
exoPackContainer[i].popTime = 5
if (stack.isEmpty) {
return true
}
}
}
// двигаем в обычные слоты
for (i in items.indices) {
if (items[i].isEmpty && regularSlotFilters[i].value === null) {
items[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
items[i].popTime = 5
if (stack.isEmpty) {
return true
}
}
}
for (i in 0 until exoPackContainer.containerSize) {
if (exoPackContainer[i].isEmpty && exoPackSlotFilters[i] === null) {
exoPackContainer[i] = stack.split(stack.count.coerceAtMost(stack.maxStackSize))
exoPackContainer[i].popTime = 5
if (stack.isEmpty) {
return true
}
}
}
MinecraftForge.EVENT_BUS.post(ItemStackLeftoverEvent(stack, this))
if (ply.abilities.instabuild) {
stack.count = 0
}
return !stack.isEmpty
}
@Suppress("unused")
companion object {
private fun stackStacks(it: ItemStack, stack: ItemStack): Boolean {
if (
!it.isEmpty &&
it.maxStackSize > it.count &&
it.isStackable &&
ItemStack.isSameItemSameTags(it, stack)
) {
val new = (it.count + stack.count).coerceAtMost(it.maxStackSize)
val diff = new - it.count
it.count = new
stack.count -= diff
it.popTime = 5
return true
}
return false
}
init {
WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true })
WitherBoss.LIVING_ENTITY_SELECTOR = WitherBoss.LIVING_ENTITY_SELECTOR.and { it.matteryPlayer?.isAndroid != true }

View File

@ -23,7 +23,7 @@ class AndroidPowerSource(
synchronizer: FieldSynchronizer,
initialCharge: Decimal,
maxCharge: Decimal
) : IMatteryEnergyStorage, INBTSerializable<CompoundTag> {
) : IMatteryEnergyStorage, INBTSerializable<CompoundTag?> {
override val energyFlow: FlowDirection
get() = FlowDirection.INPUT
@ -46,7 +46,8 @@ class AndroidPowerSource(
}
}
override fun deserializeNBT(tag: CompoundTag) {
override fun deserializeNBT(tag: CompoundTag?) {
tag ?: return
battery = tag.getDecimal("battery")
maxBattery = tag.getDecimal("maxBattery")
item = tag.getItemStack("item")

View File

@ -10,8 +10,8 @@ import ru.dbotthepony.mc.otm.client.render.sprite
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.BackgroundPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.setMousePos
import ru.dbotthepony.mc.otm.client.shouldOpenVanillaInventory
@ -48,10 +48,10 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
val frame = FramePanel(this, width = 200f, height = FRAME_BASE_HEIGHT + inventoryRows * AbstractSlotPanel.SIZE, title = this.title)
val toolbeltLine = EditablePanel(this, frame, height = 18f)
toolbeltLine.dock = Dock.BOTTOM
val hotbarStrip = EditablePanel(this, frame, height = 18f)
hotbarStrip.dock = Dock.BOTTOM
toolbeltLine.setDockMargin(top = 3f)
hotbarStrip.setDockMargin(top = 3f)
scrollPanel = DiscreteScrollBarPanel(this, null, maxScroll = { integerDivisionDown(menu.playerCombinedInventorySlots.size, 9) },
scrollCallback = {
@ -83,11 +83,12 @@ class ExoPackInventoryScreen(menu: ExoPackInventoryMenu) : MatteryScreen<ExoPack
mainInventoryLine.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) {
val panel = SlotPanel(this, toolbeltLine, slot)
panel.dock = Dock.LEFT
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
it.dock = Dock.LEFT
}
}
val offhand = SlotPanel(this, toolbeltLine, menu.offhandSlot)
val offhand = SlotPanel(this, hotbarStrip, menu.offhandSlot)
offhand.dock = Dock.RIGHT
for (i in scrollPanel.scroll until scrollPanel.scroll + inventoryRows) {

View File

@ -11,25 +11,31 @@ import net.minecraft.client.renderer.entity.ItemRenderer
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Background
import net.minecraftforge.client.event.ContainerScreenEvent.Render.Foreground
import net.minecraftforge.common.MinecraftForge
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL13
import ru.dbotthepony.mc.otm.capability.matteryPlayer
import ru.dbotthepony.mc.otm.config.ClientConfig
import ru.dbotthepony.mc.otm.client.moveMousePosScaled
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FilteredSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.FoldableSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.SlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.compat.cos.CosmeticToggleButton
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import ru.dbotthepony.mc.otm.menu.PlayerSlot
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket
import java.util.Collections
/**
@ -163,7 +169,9 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
hotbarStrip.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) {
SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT }
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
it.dock = Dock.LEFT
}
}
val canvas = EditablePanel(this, inventoryFrame)
@ -211,7 +219,9 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
hotbarStrip.dock = Dock.BOTTOM
for (slot in menu.playerHotbarSlots) {
SlotPanel(this, hotbarStrip, slot).also { it.dock = Dock.LEFT }
FilteredSlotPanel.of(this, hotbarStrip, slot, filter = slot.filter!!).also {
it.dock = Dock.LEFT
}
}
inventoryScrollbar.parent = slotListCanvas
@ -250,13 +260,19 @@ abstract class MatteryScreen<T : MatteryMenu>(menu: T, inventory: Inventory, tit
}
for (i in 0 .. (8).coerceAtMost(menu.playerCombinedInventorySlots.size - offset - 1)) {
val slot = object : SlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, menu.playerCombinedInventorySlots[offset + i]) {
val slot = menu.playerCombinedInventorySlots[offset + i]
object : FilteredSlotPanel<MatteryScreen<*>, Slot>(this@MatteryScreen, canvas, slot) {
init {
dock = Dock.LEFT
}
override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
return false
}
}
slot.dock = Dock.LEFT
override var slotFilter: Item? by slot.filter!!
}
}
return@Int2ObjectFunction canvas

View File

@ -59,7 +59,7 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
if (!itemstack.isEmpty) {
// val font = RenderProperties.get(itemstack).getFont(itemstack)
val font = (itemstack.item.renderPropertiesInternal as? IClientItemExtensions)?.getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP)
val font = IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP)
// TODO: WHERE????????????
// GuiUtils.preItemToolTip(itemstack);
@ -84,5 +84,6 @@ abstract class AbstractSlotPanel<out S : MatteryScreen<*>> @JvmOverloads constru
val SLOT_HIGHLIGHT_DRAG = RGBAColor(200, 200, 200, 150)
const val SIZE = 18f
val SLOT_BACKGROUND = WidgetLocation.MISC.sprite(0f, 0f, SIZE, SIZE)
val FILTERED_SLOT_BACKGROUND = WidgetLocation.MISC.sprite(46f, 0f, SIZE, SIZE)
}
}

View File

@ -0,0 +1,113 @@
package ru.dbotthepony.mc.otm.client.screen.panels.slot
import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.client.extensions.common.IClientItemExtensions
import ru.dbotthepony.mc.otm.client.isCtrlDown
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.client.playGuiClickSound
import ru.dbotthepony.mc.otm.client.render.MatterySprite
import ru.dbotthepony.mc.otm.client.render.drawColor
import ru.dbotthepony.mc.otm.client.render.drawRect
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.math.RGBAColor
abstract class FilteredSlotPanel<out S : MatteryScreen<*>, out T : Slot>(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
noItemIcon: MatterySprite? = null
) : SlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
abstract var slotFilter: Item?
override fun renderSlotBackground(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float) {
super.renderSlotBackground(stack, mouseX, mouseY, partialTick)
if (slotFilter != null) {
if (slotFilter !== Items.AIR) {
val itemStack = ItemStack(slotFilter!!, 1)
screen.renderItemStack(absoluteX, absoluteY, itemStack, null)
clearDepth(stack)
}
drawColor = SLOT_FILTER_COLOR
drawRect(stack, 0f, 0f, width, height)
}
}
override fun innerRenderTooltips(stack: PoseStack, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
if (isHovered && slotFilter != null && slotFilter !== Items.AIR && itemStack.isEmpty) {
val itemstack = ItemStack(slotFilter!!, 1)
screen.renderComponentTooltip(
stack,
getItemStackTooltip(itemstack),
mouseX.toInt(),
mouseY.toInt(),
IClientItemExtensions.of(itemstack).getFont(itemstack, IClientItemExtensions.FontContext.TOOLTIP) ?: screen.font,
itemstack
)
}
return super.innerRenderTooltips(stack, mouseX, mouseY, partialTick)
}
override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
if (slotFilter === null) {
if (screen.menu.carried.isEmpty) {
slotFilter = slot.item.item
} else {
slotFilter = screen.menu.carried.item
}
} else {
slotFilter = null
}
playGuiClickSound()
return true
} else {
return super.mouseClickedInner(x, y, button)
}
}
override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.window.isCtrlDown) {
return true
}
return super.mouseReleasedInner(x, y, button)
}
companion object {
val SLOT_FILTER_COLOR = RGBAColor(85, 113, 216, 150)
fun <S : MatteryScreen<*>, T : Slot> of(
screen: S,
parent: EditablePanel<*>?,
slot: T,
x: Float = 0f,
y: Float = 0f,
width: Float = SIZE,
height: Float = SIZE,
noItemIcon: MatterySprite? = null,
filter: GetterSetter<Item?>
): FilteredSlotPanel<S, T> {
return object : FilteredSlotPanel<S, T>(screen, parent, slot, x, y, width, height, noItemIcon) {
override var slotFilter: Item? by filter
}
}
}
}

View File

@ -11,6 +11,7 @@ import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.chat.ComponentContents
import net.minecraft.network.chat.contents.TranslatableContents
@ -18,6 +19,7 @@ import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.crafting.Ingredient
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.block.state.BlockState
@ -32,7 +34,11 @@ import net.minecraftforge.registries.ForgeRegistry
import net.minecraftforge.registries.IForgeRegistry
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.util.readInt
import ru.dbotthepony.mc.otm.core.util.readVarIntLE
import ru.dbotthepony.mc.otm.core.util.writeInt
import ru.dbotthepony.mc.otm.core.util.writeVarIntLE
import java.io.InputStream
import java.io.OutputStream
import java.lang.ref.Reference
import java.math.BigInteger
import java.util.Arrays
@ -167,12 +173,16 @@ fun FriendlyByteBuf.writeItemType(value: Item) {
writeInt(ForgeRegistries.ITEMS.getID(value))
}
fun FriendlyByteBuf.readItemType(): Item? {
return ForgeRegistries.ITEMS.getValue(readInt())
fun OutputStream.writeItemType(value: Item) {
writeVarIntLE(ForgeRegistries.ITEMS.getID(value))
}
fun InputStream.readItemType(): Item? {
return ForgeRegistries.ITEMS.getValue(readInt())
fun FriendlyByteBuf.readItemType(): Item {
return ForgeRegistries.ITEMS.getValue(readInt()) ?: Items.AIR
}
fun InputStream.readItemType(sizeLimit: NbtAccounter? = null): Item {
return ForgeRegistries.ITEMS.getValue(readVarIntLE(sizeLimit)) ?: Items.AIR
}
operator fun <T : Comparable<T>> StateHolder<*, *>.get(property: Property<T>): T {

View File

@ -10,6 +10,8 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.immutableMap
import ru.dbotthepony.mc.otm.core.math.readDecimal
import ru.dbotthepony.mc.otm.core.math.writeDecimal
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType
import java.io.DataInput
import java.io.DataInputStream
import java.io.DataOutput
@ -60,6 +62,30 @@ class StreamCodec<V>(
comparator: ((a: V, b: V) -> Boolean) = { a, b -> a == b }
) : this({ stream, sizeLimit -> sizeLimit.accountBytes(payloadSize); reader.invoke(stream) }, writer, copier, comparator)
val nullable = object : IStreamCodec<V?> {
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V? {
sizeLimit.accountBytes(1L)
return if (stream.read() == 0) null else reader.invoke(stream, sizeLimit)
}
override fun write(stream: DataOutputStream, value: V?) {
if (value === null) stream.write(0) else {
stream.write(1)
writer.invoke(stream, value)
}
}
override fun copy(value: V?): V? {
return if (value === null) null else copier.invoke(value)
}
override fun compare(a: V?, b: V?): Boolean {
if (a === null && b === null) return true
if (a === null || b === null) return false
return comparator.invoke(a, b)
}
}
override fun read(stream: DataInputStream, sizeLimit: NbtAccounter): V {
return reader.invoke(stream, sizeLimit)
}
@ -86,6 +112,7 @@ val LongValueCodec = StreamCodec(DataInputStream::readLong, 8L, DataOutputStream
val FloatValueCodec = StreamCodec(DataInputStream::readFloat, 4L, DataOutputStream::writeFloat)
val DoubleValueCodec = StreamCodec(DataInputStream::readDouble, 8L, DataOutputStream::writeDouble)
val ItemStackValueCodec = StreamCodec(DataInputStream::readItem, DataOutputStream::writeItem, ItemStack::copy) { a, b -> a.equals(b, true) }
val ItemValueCodec = StreamCodec(DataInputStream::readItemType, DataOutputStream::writeItemType)
val DecimalValueCodec = StreamCodec(DataInputStream::readDecimal, DataOutputStream::writeDecimal)
val BigDecimalValueCodec = StreamCodec(DataInputStream::readBigDecimal, DataOutputStream::writeBigDecimal)
val UUIDValueCodec = StreamCodec({ s, a -> a.accountBytes(8L); UUID(s.readLong(), s.readLong()) }, { s, v -> s.writeLong(v.mostSignificantBits); s.writeLong(v.leastSignificantBits) })
@ -331,7 +358,7 @@ fun OutputStream.writeBinaryString(input: String) {
private data class IndexedStreamCodec<T>(
val condition: Predicate<Any?>,
val id: Int,
val codec: StreamCodec<T>
val codec: IStreamCodec<T>
) {
fun read(stream: DataInputStream, sizeLimit: NbtAccounter = NbtAccounter(1L shl 18 /* 256 KiB */)): T {
return codec.read(stream, sizeLimit)

View File

@ -14,6 +14,7 @@ import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.*
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.enchantment.EnchantmentHelper.hasBindingCurse
import net.minecraft.world.level.block.entity.BlockEntity
@ -26,6 +27,7 @@ import ru.dbotthepony.mc.otm.compat.curios.curiosSlots
import ru.dbotthepony.mc.otm.compat.curios.isCurioSlot
import ru.dbotthepony.mc.otm.container.ItemFilter
import ru.dbotthepony.mc.otm.container.ItemFilterNetworkSlot
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.util.BigDecimalValueCodec
import ru.dbotthepony.mc.otm.core.util.BinaryStringCodec
import ru.dbotthepony.mc.otm.core.util.BooleanValueCodec
@ -35,8 +37,10 @@ import ru.dbotthepony.mc.otm.core.util.VarIntValueCodec
import ru.dbotthepony.mc.otm.menu.widget.AbstractWidget
import ru.dbotthepony.mc.otm.network.FieldSynchronizer
import ru.dbotthepony.mc.otm.network.MatteryPacket
import ru.dbotthepony.mc.otm.network.MatteryPlayerNetworkChannel
import ru.dbotthepony.mc.otm.network.MenuFieldPacket
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
import ru.dbotthepony.mc.otm.network.SetInventoryFilterPacket
import ru.dbotthepony.mc.otm.network.enqueueWork
import ru.dbotthepony.mc.otm.network.packetHandled
import ru.dbotthepony.mc.otm.network.sender
@ -70,12 +74,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
private val _matteryWidgets = ArrayList<AbstractWidget>()
val matteryWidgets: List<AbstractWidget> = Collections.unmodifiableList(_matteryWidgets)
private val _playerInventorySlots = ArrayList<MatterySlot>()
private val _playerInventorySlots2 = ArrayList<MatterySlot>()
private val _playerHotbarSlots = ArrayList<MatterySlot>()
private val _playerMainSlots = ArrayList<MatterySlot>()
private val _playerExoSuitSlots = ArrayList<MatterySlot>()
private val _playerCombinedInventorySlots = ArrayList<MatterySlot>()
private val _playerInventorySlots = ArrayList<InventorySlot>()
private val _playerInventorySlots2 = ArrayList<InventorySlot>()
private val _playerHotbarSlots = ArrayList<InventorySlot>()
private val _playerMainSlots = ArrayList<InventorySlot>()
private val _playerExoSuitSlots = ArrayList<InventorySlot>()
private val _playerCombinedInventorySlots = ArrayList<InventorySlot>()
private val playerInputs = ArrayList<PlayerInput<*>>()
@ -155,32 +159,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
/**
* inventory + exosuit + hotbar (in this order)
*/
val playerInventorySlots: List<MatterySlot> = Collections.unmodifiableList(_playerInventorySlots)
val playerInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots)
/**
* hotbar + inventory + exosuit (in this order)
*/
val playerInventorySlots2: List<MatterySlot> = Collections.unmodifiableList(_playerInventorySlots2)
val playerInventorySlots2: List<InventorySlot> = Collections.unmodifiableList(_playerInventorySlots2)
/**
* hotbar only
*/
val playerHotbarSlots: List<MatterySlot> = Collections.unmodifiableList(_playerHotbarSlots)
val playerHotbarSlots: List<InventorySlot> = Collections.unmodifiableList(_playerHotbarSlots)
/**
* inventory only
*/
val playerMainSlots: List<MatterySlot> = Collections.unmodifiableList(_playerMainSlots)
val playerMainSlots: List<InventorySlot> = Collections.unmodifiableList(_playerMainSlots)
/**
* exosuit only
*/
val playerExoSuitSlots: List<MatterySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
val playerExoSuitSlots: List<InventorySlot> = Collections.unmodifiableList(_playerExoSuitSlots)
/**
* inventory + exosuit (in this order)
*/
val playerCombinedInventorySlots: List<MatterySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
val playerCombinedInventorySlots: List<InventorySlot> = Collections.unmodifiableList(_playerCombinedInventorySlots)
var autoCreateInventoryFrame = true
private set
@ -250,6 +254,32 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
return super.isSameInventory(other)
}
// фильтр существует только для автоматизации
// игрок всё равно может класть предмет вручную, даже если он не соответствует фильтру
internal val filter: GetterSetter<Item?>?
init {
val mattery = ply.matteryPlayer
if (mattery != null) {
if (container === inventory && slotIndex in mattery.regularSlotFilters.indices) {
filter = GetterSetter.of(
getter = { mattery.regularSlotFilters[slotIndex].value },
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.INVENTORY, slotIndex, it)) }
)
} else if (container === mattery.exoPackContainer) {
filter = GetterSetter.of(
getter = { mattery.exoPackSlotFilters[slotIndex] },
setter = { MatteryPlayerNetworkChannel.sendToServer(SetInventoryFilterPacket(SetInventoryFilterPacket.Type.EXOPACK, slotIndex, it)) }
)
} else {
filter = null
}
} else {
filter = null
}
}
}
open inner class EquipmentSlot(container: Container, index: Int, val type: net.minecraft.world.entity.EquipmentSlot, x: Int = 0, y: Int = 0) : InventorySlot(container, index, x, y) {
@ -458,6 +488,18 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
val copy = slot.item.copy()
var any = false
if (target.any { it.any { it is InventorySlot && it.filter != null } }) {
for (collection in target) {
if (moveItemStackTo(slot, collection, onlyFiltered = true)) {
any = true
if (!slot.hasItem()) {
return copy
}
}
}
}
for (collection in target) {
if (moveItemStackTo(slot, collection)) {
any = true
@ -506,25 +548,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
return true
}
fun moveItemStackTo(
item: ItemStack,
slots: Collection<Slot>
): Boolean {
val remainder = moveItemStackToSlots(item, slots)
if (remainder.count == item.count) {
return false
}
item.count = remainder.count
return true
}
fun moveItemStackTo(
source: Slot,
slots: Collection<Slot>
slots: Collection<Slot>,
onlyFiltered: Boolean = false
): Boolean {
val remainder = moveItemStackToSlots(source.item, slots)
val remainder = moveItemStackToSlots(source.item, slots, onlyFiltered = onlyFiltered)
if (remainder.count == source.item.count) {
return false
@ -540,15 +569,25 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
return true
}
fun moveItemStackToSlots(item: ItemStack, slots: Collection<Slot>, simulate: Boolean = false): ItemStack {
fun moveItemStackToSlots(item: ItemStack, slots: Collection<Slot>, simulate: Boolean = false, onlyFiltered: Boolean = false): ItemStack {
if (item.isEmpty) {
return ItemStack.EMPTY
}
val copy = item.copy()
// first pass - stack with existing slots
if (copy.isStackable) {
for (slot in slots) {
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
continue
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
continue
}
val limit = slot.getMaxStackSize(copy)
if (limit > slot.item.count && ItemStack.isSameItemSameTags(slot.item, copy) && slot.mayPlace(item)) {
if (limit > slot.item.count && slot.mayPlace(item) && ItemStack.isSameItemSameTags(slot.item, copy)) {
val newCount = (slot.item.count + copy.count).coerceAtMost(limit)
val diff = newCount - slot.item.count
copy.count -= diff
@ -567,6 +606,12 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
// second pass - drop stack into first free slot
for (slot in slots) {
if (onlyFiltered && (slot !is InventorySlot || slot.filter == null || slot.filter.get() != item.item)) {
continue
} else if (!onlyFiltered && slot is InventorySlot && slot.filter != null && slot.filter.get() != null && slot.filter.get() != item.item) {
continue
}
val limit = slot.getMaxStackSize(copy)
if (!slot.hasItem() && slot.mayPlace(item)) {
@ -597,9 +642,19 @@ abstract class MatteryMenu @JvmOverloads protected constructor(
require(finalSlot < slots.size) { "Final slot $finalSlot is bigger than total size of array of ${slots.size}" }
val slots = ArrayList<Slot>(finalSlot - initialSlot + 1)
var filters = false
for (i in (if (inverse) finalSlot downTo initialSlot else initialSlot .. finalSlot)) {
slots.add(slots[i])
val slot = slots[i]
slots.add(slot)
if (slot is InventorySlot && slot.filter != null) {
filters = true
}
}
if (filters) {
return moveItemStackToSlots(moveItemStackToSlots(item, slots, simulate, onlyFiltered = true), slots, simulate, onlyFiltered = false)
}
return moveItemStackToSlots(item, slots, simulate)

View File

@ -6,7 +6,9 @@ import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.game.ClientboundSetCarriedItemPacket
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraftforge.network.NetworkDirection.PLAY_TO_CLIENT
import net.minecraftforge.network.NetworkDirection.PLAY_TO_SERVER
import net.minecraftforge.network.NetworkEvent
@ -27,6 +29,8 @@ import ru.dbotthepony.mc.otm.client.render.ShockwaveRenderer
import ru.dbotthepony.mc.otm.container.get
import ru.dbotthepony.mc.otm.container.set
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.readItemType
import ru.dbotthepony.mc.otm.core.writeItemType
import ru.dbotthepony.mc.otm.menu.tech.AndroidStationMenu
import ru.dbotthepony.mc.otm.menu.ExoPackInventoryMenu
import ru.dbotthepony.mc.otm.registry.AndroidFeatures
@ -510,8 +514,56 @@ object HideExosuitPacket : MatteryPacket {
}
}
class SetInventoryFilterPacket(val type: Type, val slot: Int, val item: Item?) : MatteryPacket {
enum class Type {
INVENTORY,
EXOPACK
}
override fun write(buff: FriendlyByteBuf) {
buff.writeEnum(type)
buff.writeVarInt(slot)
if (item == null) {
buff.writeByte(0)
} else {
buff.writeByte(1)
buff.writeItemType(item)
}
}
override fun play(context: Supplier<NetworkEvent.Context>) {
context.packetHandled = true
val player = context.sender?.matteryPlayer ?: return
when (type) {
Type.INVENTORY -> {
if (slot in 0 until player.regularSlotFilters.size) {
player.regularSlotFilters[slot].value = item
}
}
Type.EXOPACK -> {
if (slot in 0 until player.exoPackSlotCount) {
if (item == null) {
player.exoPackSlotFilters.remove(slot)
} else {
player.exoPackSlotFilters[slot] = item
}
}
}
}
}
companion object {
fun read(buff: FriendlyByteBuf): SetInventoryFilterPacket {
return SetInventoryFilterPacket(buff.readEnum(Type::class.java), buff.readVarInt(), if (buff.readByte() > 0) buff.readItemType() else null)
}
}
}
object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
version = "2",
version = "3",
name = "player"
) {
fun register() {
@ -543,5 +595,7 @@ object MatteryPlayerNetworkChannel : MatteryNetworkChannel(
add(DisplayExosuitPacket::class, { DisplayExosuitPacket }, PLAY_TO_SERVER)
add(HideExosuitPacket::class, { HideExosuitPacket }, PLAY_TO_SERVER)
add(SetInventoryFilterPacket::class, SetInventoryFilterPacket.Companion::read, PLAY_TO_SERVER)
}
}

View File

@ -2,6 +2,8 @@
public-f net.minecraft.client.gui.screens.Screen f_96539_ # title
public net.minecraft.server.MinecraftServer f_129744_ # storageSource
public net.minecraft.world.entity.player.Inventory f_150070_ # SELECTION_SIZE
# for accessing and setting from MatteryScreen class
public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169600_
public net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_169605_

View File

@ -533,46 +533,6 @@ function injectAtTail(path, instructions) {
})
}
function injectInventoryInsertHook(
instructions,
determinedOffset,
hookName,
hookDesc,
pre
) {
var last = instructions.get(determinedOffset)
// print('Patching Inventory#add at instruction ' + determinedOffset + ' to add ' + hookName + hookDesc)
// loading this (Inventory) to stack
if (pre != undefined) {
instructions.insert(last, pre)
last = pre
}
var next = new VarInsnNode(opcodesRemapped.aload, 0)
instructions.insert(last, next)
last = next
// loading itemstack to stack
next = new VarInsnNode(opcodesRemapped.aload, 2)
instructions.insert(last, next)
last = next
// dispatching hook method
next = new MethodInsnNode(
opcodesRemapped.invokestatic,
'ru/dbotthepony/mc/otm/capability/MatteryPlayerCapability',
hookName,
hookDesc,
false
)
instructions.insert(last, next)
}
function patchBlendFunc(node) {
var last = new MethodInsnNode(
opcodesRemapped.invokestatic,
@ -795,128 +755,6 @@ function initializeCoreMod() {
return node
}),
'Inventory#add patch': {
'target': {
'type': 'METHOD',
'class': 'net.minecraft.world.entity.player.Inventory',
'methodName': ASMAPI.mapMethod('m_36040_'), // add
'methodDesc': '(ILnet/minecraft/world/item/ItemStack;)Z'
},
'transformer': function(node) {
// If item is not "damaged":
// 113: invokevirtual #144 // Method addResource:(ILnet/minecraft/world/item/ItemStack;)I
// 116: invokevirtual #158 // Method net/minecraft/world/item/ItemStack.setCount:(I)V
// 119: aload_2
// 120: invokevirtual #57 // Method net/minecraft/world/item/ItemStack.isEmpty:()Z
// 123: ifne 134
// 126: aload_2
// 127: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
// 130: iload_3
// 131: if_icmplt 87
// <-- our target
// 134: aload_2
// 135: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
// 138: iload_3
// 139: if_icmpne 162
// 142: aload_0
// If item returned that it is damaged:
// 10: invokevirtual #97 // Method net/minecraft/world/item/ItemStack.isDamaged:()Z
// 13: ifeq 87
// 16: iload_1
// 17: iconst_m1
// 18: if_icmpne 26
// 21: aload_0
// 22: invokevirtual #86 // Method getFreeSlot:()I
// 25: istore_1
// <-- our target
// 26: iload_1
// 27: iflt 65
// 30: aload_0
// 31: getfield #19 // Field items:Lnet/minecraft/core/NonNullList;
// 34: iload_1
// 35: aload_2
var i
// 83: iconst_1
// 84: ireturn
// 85: iconst_0
// 86: ireturn
// 87: aload_2
// 88: invokevirtual #68 // Method net/minecraft/world/item/ItemStack.getCount:()I
// patch "not damaged" before loop
for (i = 0; i < node.instructions.size(); i++) {
var determinedOffset = test([
opcodesRemapped.iconst_1,
opcodesRemapped.ireturn,
opcodesRemapped.iconst_0,
opcodesRemapped.ireturn,
opcodesRemapped.aload,
opcodesRemapped.invokevirtual,
], node.instructions, i)
if (determinedOffset != -1) {
injectInventoryInsertHook(node.instructions, determinedOffset - 1,
'inventoryAddItemHookPre',
'(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V')
break
}
}
// patch "not damaged" after loop
for (i = 0; i < node.instructions.size(); i++) {
var determinedOffset = test([
opcodesRemapped.invokevirtual,
opcodesRemapped.invokevirtual,
opcodesRemapped.aload,
opcodesRemapped.invokevirtual,
opcodesRemapped.ifne,
opcodesRemapped.aload,
opcodesRemapped.invokevirtual,
opcodesRemapped.iload,
opcodesRemapped.if_icmplt,
], node.instructions, i)
if (determinedOffset != -1) {
injectInventoryInsertHook(node.instructions, determinedOffset,
'inventoryAddItemHook',
'(Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V')
break
}
}
// patch "damaged"
for (i = 0; i < node.instructions.size(); i++) {
var determinedOffset = test([
opcodesRemapped.invokevirtual,
opcodesRemapped.ifeq,
opcodesRemapped.iload,
opcodesRemapped.iconst_m1,
opcodesRemapped.if_icmpne,
opcodesRemapped.aload,
opcodesRemapped.invokevirtual,
opcodesRemapped.istore,
], node.instructions, i)
if (determinedOffset != -1) {
injectInventoryInsertHook(node.instructions, determinedOffset,
'inventoryAddDamagedItemHook',
'(ILnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/world/item/ItemStack;)V',
new VarInsnNode(opcodesRemapped.iload, node.instructions.get(determinedOffset - 1)['var']))
break
}
}
return node
}
},
'GameRenderer#render hook': {
'target': {
'type': 'METHOD',

View File

@ -9,6 +9,7 @@
"MixinPatchProjectileFinder",
"MixinLivingEntity",
"MixinAnvilBlock",
"MixinInventory",
"MixinAbstractHurtingProjectile"
]
}