mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-10-26 22:33:52 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cced6fa21b | ||
|
|
30eec77a20 | ||
|
|
75e480f0c7 | ||
|
|
6ae40a1b36 | ||
|
|
540caeaae3 | ||
|
|
502a688a75 | ||
|
|
cbaef9cee0 | ||
|
|
a40289ca65 | ||
|
|
9ce2aa40bb |
No files matched your search
@@ -29,6 +29,7 @@ public class AutoCraftingManager {
|
||||
final List<BookmarkItem> initialItems = prepareInitialItems(math, getInventoryItems(guiContainer));
|
||||
boolean processed = false;
|
||||
boolean changed = false;
|
||||
final CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
|
||||
StackInfo.pauseItemDamageSound(true);
|
||||
|
||||
@@ -48,9 +49,19 @@ public class AutoCraftingManager {
|
||||
if (handler != null && handler.canCraft(guiContainer)) {
|
||||
long multiplier = entry.getValue();
|
||||
|
||||
while (multiplier > 0 && !interrupted(guiContainer)
|
||||
&& handler.craft(guiContainer, (int) Math.min(64, multiplier))) {
|
||||
multiplier -= 64;
|
||||
while (multiplier > 0 && !interrupted(guiContainer)) {
|
||||
final CraftRampThrottle.Tick tick = throttle.next(entry.getKey());
|
||||
sleepInterruptibly(tick.delayMs, guiContainer);
|
||||
if (interrupted(guiContainer)) break;
|
||||
|
||||
boolean crafted = false;
|
||||
for (int i = 0; i < tick.crafts && multiplier > 0 && !interrupted(guiContainer); i++) {
|
||||
if (!handler.craft(guiContainer, 1)) break;
|
||||
multiplier -= 1;
|
||||
crafted = true;
|
||||
}
|
||||
|
||||
if (!crafted) break; // output couldn't be taken (e.g. inventory full)
|
||||
}
|
||||
|
||||
craft = multiplier != entry.getValue();
|
||||
@@ -86,6 +97,19 @@ public class AutoCraftingManager {
|
||||
return interrupted() || guiContainer != NEIClientUtils.getGuiContainer();
|
||||
}
|
||||
|
||||
private void sleepInterruptibly(long delayMs, GuiContainer guiContainer) {
|
||||
long remaining = delayMs;
|
||||
while (remaining > 0 && !interrupted(guiContainer)) {
|
||||
final long chunk = Math.min(20L, remaining);
|
||||
try {
|
||||
Thread.sleep(chunk);
|
||||
} catch (InterruptedException ignored) {
|
||||
return;
|
||||
}
|
||||
remaining -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
private List<BookmarkItem> prepareInitialItems(RecipeChainMath math, ItemStackAmount inventory) {
|
||||
final List<BookmarkItem> initialItems = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
/**
|
||||
* Per-craft delay ramp for auto-crafting. Crafting a recipe repeatedly speeds up (delay decays geometrically toward a
|
||||
* floor). Momentum is global: switching to a different recipe reduces speed by a penalty but does not reset it, so
|
||||
* going back to a recipe resumes with most of its momentum. At floor (max) speed crafts are batched. Pure logic; does
|
||||
* not sleep.
|
||||
*/
|
||||
public class CraftRampThrottle {
|
||||
|
||||
public static final long START_DELAY_MS = 300L; // delay before the 1st craft
|
||||
public static final long FLOOR_DELAY_MS = 100L; // fastest allowed (cap)
|
||||
public static final double DECAY = 0.88D; // per-craft speedup
|
||||
public static final double SWITCH_PENALTY = 2.0D; // momentum lost on recipe change
|
||||
public static final int BULK_CRAFTS = 4; // crafts per tick once maxed out
|
||||
|
||||
/** Delay to wait before a craft, and how many crafts that tick covers. */
|
||||
public static final class Tick {
|
||||
|
||||
public final long delayMs;
|
||||
public final int crafts;
|
||||
|
||||
Tick(long delayMs, int crafts) {
|
||||
this.delayMs = delayMs;
|
||||
this.crafts = crafts;
|
||||
}
|
||||
}
|
||||
|
||||
private RecipeId current;
|
||||
private long delayMs = START_DELAY_MS;
|
||||
|
||||
/** Next tick for {@code id}: decays on repeat, slows (but keeps momentum) on a recipe change. */
|
||||
public Tick next(RecipeId id) {
|
||||
if (!Objects.equals(id, this.current)) {
|
||||
if (this.current != null) {
|
||||
this.delayMs = Math.min(START_DELAY_MS, Math.round(this.delayMs * SWITCH_PENALTY));
|
||||
}
|
||||
this.current = id;
|
||||
}
|
||||
|
||||
final long delay = this.delayMs;
|
||||
this.delayMs = Math.max(FLOOR_DELAY_MS, Math.round(delay * DECAY));
|
||||
return new Tick(delay, delay <= FLOOR_DELAY_MS ? BULK_CRAFTS : 1);
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,12 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
|
||||
if (craftingSlot.getHasStack() && craftingSlot.canTakeStack(firstGui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(firstGui, craftingSlot.slotNumber, 0, 1);
|
||||
|
||||
// Output still present means it wasn't taken (e.g. inventory full); stop.
|
||||
if (craftingSlot.getHasStack()) {
|
||||
break;
|
||||
}
|
||||
|
||||
craft = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import codechicken.nei.recipe.CraftRampThrottle.Tick;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
|
||||
class CraftRampThrottleTest {
|
||||
|
||||
private static Tick rampToFloor(CraftRampThrottle throttle, RecipeId id) {
|
||||
Tick tick = null;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
tick = throttle.next(id);
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("first craft of a recipe uses the start delay and is single")
|
||||
void firstCraftUsesStartDelay() {
|
||||
CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
RecipeId id = mock(RecipeId.class);
|
||||
|
||||
Tick tick = throttle.next(id);
|
||||
assertEquals(CraftRampThrottle.START_DELAY_MS, tick.delayMs);
|
||||
assertEquals(1, tick.crafts);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("same recipe decays monotonically and clamps at floor")
|
||||
void rampDecaysToFloor() {
|
||||
CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
RecipeId id = mock(RecipeId.class);
|
||||
|
||||
long prev = Long.MAX_VALUE;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
long delay = throttle.next(id).delayMs;
|
||||
assertTrue(delay <= prev, "delay should not increase while ramping");
|
||||
assertTrue(delay >= CraftRampThrottle.FLOOR_DELAY_MS, "delay should not drop below floor");
|
||||
prev = delay;
|
||||
}
|
||||
|
||||
assertEquals(CraftRampThrottle.FLOOR_DELAY_MS, rampToFloor(throttle, id).delayMs);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("at floor speed, crafts happen in bulk")
|
||||
void bulkAtFloorSpeed() {
|
||||
CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
RecipeId id = mock(RecipeId.class);
|
||||
|
||||
Tick tick = rampToFloor(throttle, id);
|
||||
assertEquals(CraftRampThrottle.FLOOR_DELAY_MS, tick.delayMs);
|
||||
assertEquals(CraftRampThrottle.BULK_CRAFTS, tick.crafts);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("crafts stay single while still ramping")
|
||||
void singleWhileRamping() {
|
||||
CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
RecipeId id = mock(RecipeId.class);
|
||||
|
||||
assertEquals(1, throttle.next(id).crafts);
|
||||
assertEquals(1, throttle.next(id).crafts);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("recipe change reduces momentum but does not fully reset")
|
||||
void recipeChangeReducesMomentum() {
|
||||
CraftRampThrottle throttle = new CraftRampThrottle();
|
||||
RecipeId a = mock(RecipeId.class);
|
||||
RecipeId b = mock(RecipeId.class);
|
||||
|
||||
// Ramp A to full speed (floor).
|
||||
assertEquals(CraftRampThrottle.FLOOR_DELAY_MS, rampToFloor(throttle, a).delayMs);
|
||||
|
||||
// Switching to B slows down, but keeps momentum: slower than floor, faster than a cold start.
|
||||
long afterSwitch = throttle.next(b).delayMs;
|
||||
assertTrue(afterSwitch > CraftRampThrottle.FLOOR_DELAY_MS, "switch should reduce momentum");
|
||||
assertTrue(afterSwitch < CraftRampThrottle.START_DELAY_MS, "switch should not fully reset");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user