Skip to content

Commit

Permalink
feat: Improve Inventory Cleaner item count constraints interface (#3796)
Browse files Browse the repository at this point in the history
* Improved item limits

* Fix detekt
  • Loading branch information
superblaubeere27 authored Aug 26, 2024
1 parent c537513 commit f13d200
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import net.ccbluex.liquidbounce.utils.item.isNothing
class CleanupPlanGenerator(
private val template: CleanupPlanPlacementTemplate,
private val availableItems: List<ItemSlot>,
) {
) : ItemPacker.ItemAmountContraintProvider {
private val hotbarSwaps: ArrayList<InventorySwap> = ArrayList()

private val packer = ItemPacker()

private val currentLimit = HashMap<ItemNumberContraintGroup, Int>()

// TODO Implement greedy check
/**
* Keeps track of where a specific type of item should be placed. e.g. BLOCK -> [Hotbar 7, Hotbar 8]
Expand Down Expand Up @@ -72,13 +74,6 @@ class CleanupPlanGenerator(
category: ItemCategory,
availableItems: List<ItemFacet>,
) {
val maxItemCount =
if (category.type.oneIsSufficient) {
if (this.packer.alreadyProviededFunctions.contains(category.type.providedFunction)) 0 else 1
} else {
template.itemLimitPerCategory[category] ?: Int.MAX_VALUE
}

val hotbarSlotsToFill = this.categoryToSlotsMap[category]

// We need to fill all hotbar slots with this item type.
Expand All @@ -91,9 +86,8 @@ class CleanupPlanGenerator(
this.packer.packItems(
itemsToFillIn = prioritizedItemList,
hotbarSlotsToFill = hotbarSlotsToFill,
maxItemCount = maxItemCount,
contraintProvider = this,
forbiddenSlots = this.template.forbiddenSlots,
requiredStackCount = hotbarSlotsToFill?.size ?: 0,
)

this.hotbarSwaps.addAll(requiredMoves)
Expand All @@ -120,6 +114,34 @@ class CleanupPlanGenerator(

return itemsByType
}

override fun getSatisfactionStatus(item: ItemFacet): ItemPacker.ItemAmountContraintProvider.SatisfactionStatus {
val constraints = this.template.itemAmountConstraintProvider(item)

constraints.sortBy { it.group.priority }

for (constraintInfo in constraints) {
val currentCount = this.currentLimit[constraintInfo.group] ?: 0

if (currentCount > constraintInfo.group.acceptableRange.last) {
return ItemPacker.ItemAmountContraintProvider.SatisfactionStatus.OVERSATURATED
} else if (currentCount < constraintInfo.group.acceptableRange.first) {
return ItemPacker.ItemAmountContraintProvider.SatisfactionStatus.NOT_SATISFIED
}
}

return ItemPacker.ItemAmountContraintProvider.SatisfactionStatus.SATISFIED
}

override fun addItem(item: ItemFacet) {
val constraints = this.template.itemAmountConstraintProvider(item)

for (constraintInfo in constraints) {
val current = this.currentLimit.getOrDefault(constraintInfo.group, 0)

this.currentLimit[constraintInfo.group] = current + constraintInfo.amountAddedByItem
}
}
}

class CleanupPlanPlacementTemplate(
Expand All @@ -128,10 +150,10 @@ class CleanupPlanPlacementTemplate(
*/
val slotContentMap: Map<ItemSlot, ItemSortChoice>,
/**
* Contains an item limit for each category. e.g. BLOCK -> 128 will cause every stack above two to be thrown out.
* If an item is not in this map, there is no limit.
* A function which provides constraint groups for each item category and the number which the item counts against
* the given constraint. More info on how constraints work at [ItemNumberContraintGroup].
*/
val itemLimitPerCategory: Map<ItemCategory, Int>,
val itemAmountConstraintProvider: (ItemFacet) -> ArrayList<ItemConstraintInfo>,
/**
* If false, slots which also contains items of that category, those items are not replaced with other items.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package net.ccbluex.liquidbounce.features.module.modules.player.invcleaner

/**
* Defines an item constraint group.
*
* For example if we had two constraints:
* - `BLOCKS` -> `128..`
* - `TNT` -> `..64`
*
* Imagine a situation where the player has 125 TNT:
* - If the TNT was processed first it would be thrown out since the TNT limit says that we have too much TNT.
* - If the BLOCKS constraint was processed first, the TNT would be kept since the BLOCKS constraint is not yet
* satisfied.
*/
abstract class ItemNumberContraintGroup(
/**
* The range of desired item amounts (which might be raw item counts, food saturation, etc.):
* - The lower limit defines the desired amount of items (=> any more items *might* be thrown out)
* - The upper limit defines the maximum amount of items (=> any more items *will* be thrown out)
*/
val acceptableRange: IntRange,
/**
* The priority of this constraint group. Lower values are processed first.
* Affects the order in which items are processed.
*/
val priority: Int,
) {
abstract override fun hashCode(): Int
abstract override fun equals(other: Any?): Boolean
}

class ItemCategoryConstraintGroup(
acceptableRange: IntRange,
priority: Int,
val category: ItemCategory,
) : ItemNumberContraintGroup(acceptableRange, priority) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as ItemCategoryConstraintGroup

if (category != other.category) return false

return true
}

override fun hashCode(): Int {
return category.hashCode()
}
}

class ItemFunctionCategoryConstraintGroup(
acceptableRange: IntRange,
priority: Int,
val function: ItemFunction,
) : ItemNumberContraintGroup(acceptableRange, priority) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as ItemFunctionCategoryConstraintGroup

if (function != other.function) return false

return true
}

override fun hashCode(): Int {
return function.hashCode()
}
}

class ItemConstraintInfo(
val group: ItemNumberContraintGroup,
val amountAddedByItem: Int
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.ccbluex.liquidbounce.features.module.modules.player.invcleaner

import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.ItemPacker.ItemAmountContraintProvider.SatisfactionStatus.OVERSATURATED
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.ItemPacker.ItemAmountContraintProvider.SatisfactionStatus.SATISFIED
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.items.ItemFacet
import net.minecraft.item.ItemStack

Expand All @@ -21,8 +23,6 @@ class ItemPacker {
*/
val usefulItems = HashSet<ItemSlot>()

val alreadyProviededFunctions = HashSet<ItemFunction>()

/**
* Takes items from the [itemsToFillIn] list until it collected [maxItemCount] items is and [requiredStackCount]
* stacks. The items are marked as useful and fills in hotbar slots if there are still slots to fill.
Expand All @@ -33,23 +33,24 @@ class ItemPacker {
itemsToFillIn: List<ItemFacet>,
hotbarSlotsToFill: List<ItemSlot>?,
forbiddenSlots: Set<ItemSlot>,
maxItemCount: Int,
requiredStackCount: Int,
contraintProvider: ItemAmountContraintProvider
): List<InventorySwap> {
val moves = ArrayList<InventorySwap>()

val requriedStackCount = hotbarSlotsToFill?.size ?: 0

var currentStackCount = 0
var currentItemCount = 0

// The iterator of hotbar slots that still need filling.
val leftHotbarSlotIterator = hotbarSlotsToFill?.iterator()

for (filledInItem in itemsToFillIn) {
val maxCountReached = currentItemCount >= maxItemCount
val allStacksFilled = currentStackCount >= requiredStackCount
val constraintsSatisfied = contraintProvider.getSatisfactionStatus(filledInItem)
val allStacksFilled = currentStackCount >= requriedStackCount

if (maxCountReached && allStacksFilled) {
break
if (allStacksFilled && constraintsSatisfied == SATISFIED || constraintsSatisfied == OVERSATURATED) {
continue
}

val filledInItemSlot = filledInItem.itemSlot
Expand All @@ -61,7 +62,7 @@ class ItemPacker {

usefulItems.add(filledInItemSlot)

alreadyProviededFunctions.addAll(filledInItem.providedItemFunctions)
contraintProvider.addItem(filledInItem)

currentItemCount += filledInItem.itemStack.count
currentStackCount++
Expand Down Expand Up @@ -114,6 +115,7 @@ class ItemPacker {

return null
}

areStacksSame -> {
// We mark the slot as used to prevent it being used for another slot.
alreadyAllocatedItems.add(hotbarSlotToFill)
Expand All @@ -135,4 +137,26 @@ class ItemPacker {
// We found no target slot
return null
}

interface ItemAmountContraintProvider {
fun getSatisfactionStatus(item: ItemFacet): SatisfactionStatus
fun addItem(item: ItemFacet)

enum class SatisfactionStatus {
/**
* Keep the item
*/
NOT_SATISFIED,

/**
* The item is not needed - except for filling slots.
*/
SATISFIED,

/**
* The item shouldn't be kept - even if there are still slots to fill.
*/
OVERSATURATED,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import net.ccbluex.liquidbounce.event.events.ScheduleInventoryActionEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.Module
import net.ccbluex.liquidbounce.features.module.modules.player.invcleaner.items.ItemFacet
import net.ccbluex.liquidbounce.utils.inventory.ClickInventoryAction
import net.ccbluex.liquidbounce.utils.inventory.PlayerInventoryConstraints
import net.ccbluex.liquidbounce.utils.inventory.findNonEmptySlotsInInventory
import net.minecraft.screen.slot.SlotActionType
import java.util.HashMap

/**
* InventoryCleaner module
Expand Down Expand Up @@ -79,14 +79,17 @@ object ModuleInventoryCleaner : Module("InventoryCleaner", Category.PLAYER) {
forbiddenSlots.add(ArmorItemSlot(armorSlot))
}

return CleanupPlanPlacementTemplate(
slotTargets,
itemLimitPerCategory =
val constraintProvider = AmountConstraintProvider(
hashMapOf(
Pair(ItemSortChoice.BLOCK.category!!, maxBlocks),
Pair(ItemSortChoice.THROWABLES.category!!, maxThrowables),
Pair(ItemCategory(ItemType.ARROW, 0), maxArrows),
),
)
)

return CleanupPlanPlacementTemplate(
slotTargets,
itemAmountConstraintProvider = constraintProvider::getConstraints,
forbiddenSlots = forbiddenSlots,
isGreedy = isGreedy,
)
Expand Down Expand Up @@ -139,4 +142,43 @@ object ModuleInventoryCleaner : Module("InventoryCleaner", Category.PLAYER) {
itemsInInv: List<ItemSlot>,
) = itemsInInv.filter { it !in cleanupPlan.usefulItems }

private class AmountConstraintProvider(
val maxItemsPerCategory: HashMap<ItemCategory, Int>
) {
fun getConstraints(facet: ItemFacet): ArrayList<ItemConstraintInfo> {
val constraints = ArrayList<ItemConstraintInfo>()

if (facet.providedItemFunctions.isEmpty()) {
val defaultMin = if (facet.category.type.oneIsSufficient) 1 else Integer.MAX_VALUE
val minValue = this.maxItemsPerCategory[facet.category] ?: defaultMin

val info = ItemConstraintInfo(
group = ItemCategoryConstraintGroup(
minValue..Integer.MAX_VALUE,
10,
facet.category
),
amountAddedByItem = facet.itemStack.count
)

constraints.add(info)
} else {
for (function in facet.providedItemFunctions) {
val info = ItemConstraintInfo(
group = ItemFunctionCategoryConstraintGroup(
1..Integer.MAX_VALUE,
10,
function
),
amountAddedByItem = facet.itemStack.count
)

constraints.add(info)
}
}

return constraints
}
}

}

0 comments on commit f13d200

Please sign in to comment.