Skip to content

Commit 286c3ad

Browse files
authored
Add recipe caching to AutoCrafter (#69)
1 parent a10dc8c commit 286c3ad

3 files changed

Lines changed: 77 additions & 39 deletions

File tree

dependencies.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
dependencies {
44
api("com.github.GTNewHorizons:ForgeMultipart:1.7.2:dev")
55
api("com.github.GTNewHorizons:ForgeRelocation:0.3.4:dev")
6-
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.48-GTNH:dev")
6+
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.60-GTNH:dev")
77

8-
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.17-GTNH:dev") {transitive = false}
9-
compileOnly("com.github.GTNewHorizons:StorageDrawers:2.2.5-GTNH:dev") {transitive = false}
8+
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.14.27-GTNH:dev") {transitive = false}
9+
compileOnly("com.github.GTNewHorizons:StorageDrawers:2.2.9-GTNH:dev") {transitive = false}
1010

1111
compileOnly("curse.maven:cofh-core-69162:2388751")
1212
compileOnly("curse.maven:computercraft-67504:2269339") // https://www.curseforge.com/minecraft/mc-mods/computercraft/files/2269339

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pluginManagement {
1717
}
1818

1919
plugins {
20-
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.12'
20+
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.19'
2121
}
2222

2323

src/main/scala/mrtjp/projectred/expansion/TileAutoCrafter.scala

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package mrtjp.projectred.expansion
77

88
import java.util.{List => JList}
9-
109
import codechicken.lib.data.MCDataInput
1110
import codechicken.lib.gui.GuiDraw
1211
import codechicken.lib.render.uv.{MultiIconTransformation, UVTransformation}
@@ -24,7 +23,7 @@ import net.minecraft.client.renderer.texture.IIconRegister
2423
import net.minecraft.client.resources.I18n
2524
import net.minecraft.entity.player.EntityPlayer
2625
import net.minecraft.inventory.{ICrafting, ISidedInventory, InventoryCrafting}
27-
import net.minecraft.item.ItemStack
26+
import net.minecraft.item.{Item, ItemStack}
2827
import net.minecraft.item.crafting.{CraftingManager, IRecipe}
2928
import net.minecraft.nbt.NBTTagCompound
3029
import net.minecraft.util.IIcon
@@ -51,6 +50,11 @@ class TileAutoCrafter
5150
private var cycleTimer1 = getUnpoweredCycleTimer
5251
private var cycleTimer2 = getPoweredCycleTimer
5352

53+
private var recipeEquality: ItemEquality = _
54+
private val recipeCache = new java.util.HashMap[String, IRecipe]()
55+
private lazy val allRecipes =
56+
CraftingManager.getInstance().getRecipeList.asInstanceOf[JList[IRecipe]]
57+
5458
override def save(tag: NBTTagCompound) {
5559
super.save(tag)
5660
saveInv(tag)
@@ -132,22 +136,38 @@ class TileAutoCrafter
132136
currentRecipe = null
133137
currentInputs.clear()
134138
currentOutput = null
139+
recipeEquality = null
135140

136141
val plan = getStackInSlot(planSlot)
137-
if (plan != null && ItemPlan.hasRecipeInside(plan)) {
138-
val inputs = ItemPlan.loadPlanInputs(plan)
139-
for (i <- 0 until 9) invCrafting.setInventorySlotContents(i, inputs(i))
140-
val recipes =
141-
CraftingManager.getInstance().getRecipeList.asInstanceOf[JList[IRecipe]]
142-
currentRecipe = recipes.find(_.matches(invCrafting, world)).orNull
143-
if (currentRecipe != null) {
144-
inputs
145-
.map { ItemKey.getOrNull }
146-
.filter(_ != null)
147-
.foreach(currentInputs.add(_, 1))
148-
currentOutput =
149-
ItemKeyStack.getOrNull(currentRecipe.getCraftingResult(invCrafting))
150-
}
142+
if (plan == null || !ItemPlan.hasRecipeInside(plan)) return
143+
144+
val inputs = ItemPlan.loadPlanInputs(plan)
145+
for (i <- 0 until 9) invCrafting.setInventorySlotContents(i, inputs(i))
146+
147+
val key = planKey(inputs)
148+
currentRecipe = recipeCache.get(key)
149+
150+
if (currentRecipe == null) {
151+
currentRecipe = allRecipes.find(_.matches(invCrafting, world)).orNull
152+
if (currentRecipe != null)
153+
recipeCache.put(key, currentRecipe)
154+
}
155+
156+
if (currentRecipe != null) {
157+
inputs
158+
.map(ItemKey.getOrNull)
159+
.filter(_ != null)
160+
.foreach(currentInputs.add(_, 1))
161+
162+
currentOutput =
163+
ItemKeyStack.getOrNull(currentRecipe.getCraftingResult(invCrafting))
164+
165+
recipeEquality = new ItemEquality
166+
recipeEquality.matchNBT = true
167+
recipeEquality.matchMeta =
168+
!currentOutput.key.makeStack(0).isItemStackDamageable
169+
recipeEquality.matchOre = currentRecipe.isInstanceOf[ShapedOreRecipe] ||
170+
currentRecipe.isInstanceOf[ShapelessOreRecipe]
151171
}
152172
}
153173

@@ -156,17 +176,34 @@ class TileAutoCrafter
156176
recipeNeedsRefresh = true
157177
}
158178

179+
private def buildStorageMap(): Map[ItemKey, Int] = {
180+
val map = scala.collection.mutable.Map.empty[ItemKey, Int]
181+
for (i <- 9 until 27) {
182+
val s = getStackInSlot(i)
183+
if (s != null) {
184+
val k = ItemKey.get(s)
185+
map(k) = map.getOrElse(k, 0) + s.stackSize
186+
}
187+
}
188+
map.toMap
189+
}
190+
159191
def tryCraft(): Boolean = {
160-
if (currentRecipe != null && checkSpaceForOutput)
161-
if (
162-
currentInputs.result.forall(p => containsEnoughResource(p._1, p._2))
163-
) {
164-
for ((item, amount) <- currentInputs.result)
165-
eatResource(item, amount)
166-
produceOutput()
167-
return true
192+
if (currentRecipe == null || !checkSpaceForOutput)
193+
return false
194+
195+
val storage = buildStorageMap()
196+
if (
197+
!currentInputs.result.forall { case (k, amt) =>
198+
storage.getOrElse(k, 0) >= amt
168199
}
169-
false
200+
) return false
201+
202+
for ((item, amount) <- currentInputs.result)
203+
eatResource(item, amount)
204+
205+
produceOutput()
206+
true
170207
}
171208

172209
def containsEnoughResource(item: ItemKey, amount: Int): Boolean = {
@@ -200,16 +237,10 @@ class TileAutoCrafter
200237
}
201238

202239
def eatResource(item: ItemKey, amount: Int) {
203-
val eq = new ItemEquality
204-
eq.matchMeta = !item.makeStack(0).isItemStackDamageable
205-
eq.matchNBT = true
206-
eq.matchOre = currentRecipe.isInstanceOf[ShapedOreRecipe] || currentRecipe
207-
.isInstanceOf[ShapelessOreRecipe]
208-
209240
var left = amount
210-
for (i <- 9 until 27) {
241+
for (i <- 9 until 27 if left > 0) {
211242
val s = getStackInSlot(i)
212-
if (s != null && eq.matches(item, ItemKey.get(s))) {
243+
if (s != null && recipeEquality.matches(item, ItemKey.get(s))) {
213244
if (s.getItem.hasContainerItem(s)) {
214245
val cStack = s.getItem.getContainerItem(s)
215246
setInventorySlotContents(i, cStack)
@@ -221,8 +252,6 @@ class TileAutoCrafter
221252
if (s.stackSize <= 0) setInventorySlotContents(i, null)
222253
else markDirty()
223254
}
224-
225-
if (left <= 0) return
226255
}
227256
}
228257
}
@@ -238,6 +267,15 @@ class TileAutoCrafter
238267

239268
override def createContainer(player: EntityPlayer) =
240269
new ContainerAutoCrafter(player, this)
270+
271+
def planKey(inputs: Array[ItemStack]): String =
272+
inputs
273+
.map {
274+
case null => "null"
275+
case s => Item.getIdFromItem(s.getItem) + ":" + s.getItemDamage
276+
}
277+
.mkString("|")
278+
241279
}
242280

243281
class ContainerAutoCrafter(player: EntityPlayer, tile: TileAutoCrafter)

0 commit comments

Comments
 (0)