mirror of
https://github.com/GTNewHorizons/Applied-Energistics-2-Unofficial.git
synced 2026-10-25 20:22:23 +00:00
Merge remote-tracking branch 'origin/master' into mu
This commit is contained in:
33 files changed
+633
-144
No files matched your search
@@ -24,6 +24,7 @@ import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import com.ruling_0.materiallib.api.MaterialLibAPI;
|
||||
|
||||
import appeng.api.networking.crafting.ICraftingPatternDetails;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
import appeng.crafting.v2.CraftingJobV2;
|
||||
import appeng.crafting.v2.CraftingRequest;
|
||||
@@ -714,4 +715,99 @@ public class CraftingV2Tests {
|
||||
assertEquals(1, goldDustRequests.get(0).stack.getStackSize());
|
||||
assertEquals(0, goldDustRequests.get(0).remainingToProcess);
|
||||
}
|
||||
|
||||
private void addEqualPriorityPlateRecipes(MockAESystem aeSystem) {
|
||||
aeSystem.newProcessingPattern().addInput(withSize(ironDust, 2)) //
|
||||
.addOutput(withSize(ironIngot, 1)) //
|
||||
.buildAndAdd();
|
||||
aeSystem.newProcessingPattern().addInput(withSize(ironIngot, 1)) //
|
||||
.addOutput(withSize(ironPlate, 1)) //
|
||||
.buildAndAdd();
|
||||
aeSystem.newProcessingPattern().addInput(withSize(goldDust, 1)) //
|
||||
.addOutput(withSize(ironPlate, 1)) //
|
||||
.buildAndAdd();
|
||||
}
|
||||
|
||||
@Test
|
||||
void competingRecipeSamePriority() {
|
||||
MockAESystem aeSystem = new MockAESystem(dummyWorld);
|
||||
aeSystem.addStoredItem(withSize(ironDust, 4));
|
||||
aeSystem.addStoredItem(withSize(goldDust, 1));
|
||||
addEqualPriorityPlateRecipes(aeSystem);
|
||||
|
||||
final CraftingJobV2 job = aeSystem.makeCraftingJob(withSize(ironPlate, 3));
|
||||
simulateJobAndCheck(job, MAX_SIMULATION_STEPS);
|
||||
assertFalse(job.isSimulation());
|
||||
assertEquals(job.getOutput(), AEItemStack.create(withSize(ironPlate, 3)));
|
||||
assertJobPlanEquals(
|
||||
job,
|
||||
AEItemStack.create(withSize(ironDust, 4)),
|
||||
AEItemStack.create(withSize(goldDust, 1)),
|
||||
AEItemStack.create(withSize(ironIngot, 0)).setCountRequestable(2),
|
||||
AEItemStack.create(withSize(ironPlate, 0)).setCountRequestable(3));
|
||||
|
||||
final CraftingJobV2 jobFailed = aeSystem.makeCraftingJob(withSize(ironPlate, 4));
|
||||
simulateJobAndCheck(jobFailed, MAX_SIMULATION_STEPS);
|
||||
assertTrue(jobFailed.isSimulation());
|
||||
assertJobPlanEquals(
|
||||
jobFailed,
|
||||
AEItemStack.create(withSize(ironDust, 6)),
|
||||
AEItemStack.create(withSize(goldDust, 1)),
|
||||
AEItemStack.create(withSize(ironIngot, 0)).setCountRequestable(3),
|
||||
AEItemStack.create(withSize(ironPlate, 0)).setCountRequestable(4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void competingRecipeSamePriorityIronDustOnly() {
|
||||
MockAESystem aeSystem = new MockAESystem(dummyWorld);
|
||||
aeSystem.addStoredItem(withSize(ironDust, 2));
|
||||
addEqualPriorityPlateRecipes(aeSystem);
|
||||
|
||||
final CraftingJobV2 job = aeSystem.makeCraftingJob(withSize(ironPlate, 1));
|
||||
simulateJobAndCheck(job, MAX_SIMULATION_STEPS);
|
||||
assertFalse(job.isSimulation());
|
||||
assertJobPlanEquals(
|
||||
job,
|
||||
AEItemStack.create(withSize(ironDust, 2)),
|
||||
AEItemStack.create(withSize(ironIngot, 0)).setCountRequestable(1),
|
||||
AEItemStack.create(withSize(ironPlate, 0)).setCountRequestable(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void competingRecipeSamePriorityGoldDustOnly() {
|
||||
MockAESystem aeSystem = new MockAESystem(dummyWorld);
|
||||
aeSystem.addStoredItem(withSize(goldDust, 1));
|
||||
addEqualPriorityPlateRecipes(aeSystem);
|
||||
|
||||
final CraftingJobV2 job = aeSystem.makeCraftingJob(withSize(ironPlate, 1));
|
||||
simulateJobAndCheck(job, MAX_SIMULATION_STEPS);
|
||||
assertFalse(job.isSimulation());
|
||||
assertJobPlanEquals(
|
||||
job,
|
||||
AEItemStack.create(withSize(goldDust, 1)),
|
||||
AEItemStack.create(withSize(ironPlate, 0)).setCountRequestable(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void competingRecipePatternsListedByPriority() {
|
||||
MockAESystem aeSystem = new MockAESystem(dummyWorld);
|
||||
addEqualPriorityPlateRecipes(aeSystem);
|
||||
aeSystem.newProcessingPattern().addInput(withSize(goldIngot, 1)) //
|
||||
.addOutput(withSize(ironPlate, 1)) //
|
||||
.setPriority(1) //
|
||||
.buildAndAdd();
|
||||
aeSystem.newProcessingPattern().addInput(withSize(ironDust, 1)) //
|
||||
.addOutput(withSize(ironPlate, 1)) //
|
||||
.setPriority(-1) //
|
||||
.buildAndAdd();
|
||||
aeSystem.cgCache.setMockPatternsFromMethods();
|
||||
|
||||
final List<ICraftingPatternDetails> platePatterns = new ArrayList<>(
|
||||
aeSystem.cgCache.getCraftingFor(AEItemStack.create(withSize(ironPlate, 1)), null, 0, dummyWorld));
|
||||
assertEquals(4, platePatterns.size());
|
||||
assertEquals(1, platePatterns.get(0).getPriority());
|
||||
assertEquals(0, platePatterns.get(1).getPriority());
|
||||
assertEquals(0, platePatterns.get(2).getPriority());
|
||||
assertEquals(-1, platePatterns.get(3).getPriority());
|
||||
}
|
||||
}
|
||||
+71
-1
@@ -2,6 +2,7 @@ package appeng.test.me.crafting;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@@ -24,9 +25,12 @@ import appeng.api.networking.crafting.ICraftingGrid;
|
||||
import appeng.api.networking.crafting.ICraftingMedium;
|
||||
import appeng.api.networking.crafting.ICraftingPatternDetails;
|
||||
import appeng.api.networking.energy.IEnergyGrid;
|
||||
import appeng.api.networking.security.BaseActionSource;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
import appeng.api.storage.data.IAEStack;
|
||||
import appeng.api.util.WorldCoord;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.core.features.AEFeature;
|
||||
import appeng.helpers.PatternHelper;
|
||||
import appeng.me.cache.CraftingGridCache;
|
||||
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
||||
@@ -169,10 +173,68 @@ public class CraftingCPUClusterFunctionalTest {
|
||||
assertEquals(2, cpu.core.dirtyNotifications, "Notifications outside the update must remain immediate");
|
||||
}
|
||||
|
||||
@Test
|
||||
void craftingLogDoesNotBreakJobCompletion() {
|
||||
cpu.prepare(pattern, 64, 128);
|
||||
cpu.trackFinalOutput(64);
|
||||
cpu.setStartItemCount(7);
|
||||
final long[] reportedCount = { -1 };
|
||||
cpu.addOnCompleteListener((output, count, elapsedTime) -> reportedCount[0] = count);
|
||||
tick();
|
||||
|
||||
withCraftingLog(
|
||||
() -> cpu.injectItems(
|
||||
AEItemStack.create(new ItemStack(Blocks.stone)).setStackSize(512),
|
||||
Actionable.MODULATE,
|
||||
new BaseActionSource()));
|
||||
|
||||
assertAll(
|
||||
() -> assertTrue(cpu.completed()),
|
||||
() -> assertEquals(512, reportedCount[0], "The crafting log must not change the reported output"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void craftingLogDoesNotBreakFakeCraftingCompletion() {
|
||||
cpu.prepare(pattern, 64, 128);
|
||||
cpu.enableFakeCrafting(64);
|
||||
|
||||
withCraftingLog(this::tick);
|
||||
|
||||
assertTrue(cpu.completed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void breakingClusterWithInvalidatedCoreKeepsJob() {
|
||||
cpu.prepare(pattern, 64, 128);
|
||||
cpu.core.updateStatus(cpu);
|
||||
cpu.core.invalidate();
|
||||
|
||||
cpu.breakCluster();
|
||||
|
||||
assertAll(
|
||||
() -> assertTrue(cpu.isDestroyed()),
|
||||
() -> assertFalse(cpu.completed(), "The job must not be cancelled"),
|
||||
() -> assertEquals(512, cpu.storedInputs(), "The inventory must not be spilled"),
|
||||
() -> assertEquals(
|
||||
cpu.getInventory().writeInventory(),
|
||||
cpu.core.getPreviousState().getTag("inventory"),
|
||||
"The core must keep the job"));
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
cpu.updateCraftingLogic(grid, energy, cache);
|
||||
}
|
||||
|
||||
private static void withCraftingLog(Runnable action) {
|
||||
final boolean wasEnabled = AEConfig.instance.isFeatureEnabled(AEFeature.CraftingLog);
|
||||
AEConfig.instance.featureFlags.add(AEFeature.CraftingLog);
|
||||
try {
|
||||
action.run();
|
||||
} finally {
|
||||
if (!wasEnabled) AEConfig.instance.featureFlags.remove(AEFeature.CraftingLog);
|
||||
}
|
||||
}
|
||||
|
||||
private static ICraftingPatternDetails processingPattern() {
|
||||
final ItemStack encoded = AEApi.instance().definitions().items().encodedPattern().maybeStack(1).get();
|
||||
final NBTTagCompound tag = new NBTTagCompound();
|
||||
@@ -207,8 +269,16 @@ public class CraftingCPUClusterFunctionalTest {
|
||||
Actionable.MODULATE);
|
||||
}
|
||||
|
||||
private void enableFakeCrafting(int crafts) {
|
||||
private void setStartItemCount(long count) {
|
||||
startItemCount = count;
|
||||
}
|
||||
|
||||
private void trackFinalOutput(int crafts) {
|
||||
finalOutput.init(AEItemStack.create(new ItemStack(Blocks.stone)).setStackSize(crafts * 8L));
|
||||
}
|
||||
|
||||
private void enableFakeCrafting(int crafts) {
|
||||
trackFinalOutput(crafts);
|
||||
finalOutput.setFakeCrafting();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package appeng.test.tile.crafting;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import appeng.tile.crafting.TileCraftingTile;
|
||||
|
||||
public class TileCraftingTileFunctionalTest {
|
||||
|
||||
@Test
|
||||
void unformedCoreKeepsPendingJobAcrossSaves() {
|
||||
final NBTTagList inventory = new NBTTagList();
|
||||
inventory.appendTag(new ItemStack(Blocks.cobblestone, 64).writeToNBT(new NBTTagCompound()));
|
||||
final NBTTagCompound saved = new NBTTagCompound();
|
||||
saved.setBoolean("core", true);
|
||||
saved.setTag("inventory", inventory);
|
||||
|
||||
final TileCraftingTile unformed = new TileCraftingTile();
|
||||
unformed.readFromNBT(saved);
|
||||
final NBTTagCompound resaved = new NBTTagCompound();
|
||||
unformed.writeToNBT(resaved);
|
||||
final TileCraftingTile reloaded = new TileCraftingTile();
|
||||
reloaded.readFromNBT(resaved);
|
||||
|
||||
assertNotNull(reloaded.getPreviousState());
|
||||
assertEquals(inventory, reloaded.getPreviousState().getTag("inventory"));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ public interface IAdvancedLevelEmitter
|
||||
|
||||
void setReportingValue(int slot, long v);
|
||||
|
||||
/** Returns the amount of the slot's configured stack currently stored in the network. */
|
||||
long getStoredAmount(int slot);
|
||||
|
||||
boolean isSlotActive(int slot);
|
||||
|
||||
void setSlotActive(int slot, boolean active);
|
||||
|
||||
@@ -21,6 +21,9 @@ public interface ILevelEmitter
|
||||
|
||||
long getReportingValue();
|
||||
|
||||
/** Returns the value the emitter currently compares against its threshold. */
|
||||
long getStoredAmount();
|
||||
|
||||
default AEStackTypeFilter getTypeFilters() {
|
||||
return new AEStackTypeFilter();
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ public class BlockCraftingUnit extends AEBaseTileBlock {
|
||||
final TileCraftingTile cp = this.getTileEntity(w, x, y, z);
|
||||
if (cp != null) {
|
||||
cp.breakCluster();
|
||||
cp.spillPreviousState();
|
||||
}
|
||||
|
||||
super.breakBlock(w, x, y, z, a, b);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package appeng.client.gui.implementations;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import appeng.api.config.RedstoneMode;
|
||||
import appeng.api.config.Settings;
|
||||
@@ -14,7 +16,9 @@ import appeng.client.gui.AEBaseGui;
|
||||
import appeng.client.gui.slots.VirtualMEPhantomSlot;
|
||||
import appeng.client.gui.widgets.GuiImgButton;
|
||||
import appeng.client.gui.widgets.MEGuiTextField;
|
||||
import appeng.client.render.StackSizeRenderer;
|
||||
import appeng.container.implementations.ContainerAdvancedLevelEmitter;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.core.localization.ColorUtils;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.util.calculators.ArithHelper;
|
||||
@@ -100,7 +104,27 @@ public class GuiAdvancedLevelEmitter extends AEBaseGui {
|
||||
SLOT_Y + y,
|
||||
this.container.configSync,
|
||||
slot,
|
||||
GuiAdvancedLevelEmitter::acceptType));
|
||||
GuiAdvancedLevelEmitter::acceptType) {
|
||||
|
||||
@Override
|
||||
public boolean drawStackAndOverlay(final Minecraft mc, final int mouseX, final int mouseY) {
|
||||
final boolean hovered = super.drawStackAndOverlay(mc, mouseX, mouseY);
|
||||
if (this.getAEStack() != null) {
|
||||
// the item icon is rendered with depth, so the text must skip the depth test
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
StackSizeRenderer.drawStackSize(
|
||||
this.getX(),
|
||||
this.getY(),
|
||||
GuiAdvancedLevelEmitter.this.container.getStoredAmount(s),
|
||||
mc.fontRenderer,
|
||||
AEConfig.instance.getTerminalFontSize());
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
return hovered;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -831,7 +831,8 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
bindTexture(BACKGROUND);
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
int relY = 0;
|
||||
// Preserve the full entry height even when rendering only visible rows.
|
||||
int relY = entry.rows * 18;
|
||||
final int slotLeftMargin = (VIEW_WIDTH - entry.rowSize * 18);
|
||||
|
||||
entry.dispY = viewY;
|
||||
@@ -841,7 +842,8 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
final int rowYTop = row * 18;
|
||||
final int rowYBot = rowYTop + 18;
|
||||
|
||||
relY += 18;
|
||||
// Scissoring hides offscreen rows but does not skip item rendering work.
|
||||
if (viewY + rowYTop >= viewHeight) break;
|
||||
/* Is the slot row in view? */
|
||||
if (viewY + rowYBot <= titleBottom) {
|
||||
continue;
|
||||
@@ -896,6 +898,7 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
for (int row = 0; row < entry.rows; ++row) {
|
||||
final int rowYTop = row * 18;
|
||||
final int rowYBot = rowYTop + 18;
|
||||
if (viewY + rowYTop >= viewHeight) break;
|
||||
/* Is the slot row in view? */
|
||||
if (viewY + rowYBot <= titleBottom) {
|
||||
continue;
|
||||
@@ -1344,6 +1347,13 @@ public class GuiInterfaceTerminal extends AEBaseGui
|
||||
if (w == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// getOutputAE() already decoded this pattern while drawing its icon.
|
||||
// Reuse that result rather than creating another PatternHelper here.
|
||||
if (itemStack.getItem() instanceof ItemEncodedPattern iep && iep.hasCachedOutputAE(itemStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(itemStack.getItem() instanceof ICraftingPatternItem icpi
|
||||
&& icpi.getPatternForItem(itemStack, w) != null);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ package appeng.client.gui.implementations;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.util.IIcon;
|
||||
@@ -20,6 +21,7 @@ import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import appeng.api.config.FuzzyMode;
|
||||
import appeng.api.config.LevelType;
|
||||
@@ -35,6 +37,7 @@ import appeng.client.gui.widgets.GuiImgButton;
|
||||
import appeng.client.gui.widgets.GuiQuantityButton;
|
||||
import appeng.client.gui.widgets.MEGuiTextField;
|
||||
import appeng.client.gui.widgets.TypeToggleButton;
|
||||
import appeng.client.render.StackSizeRenderer;
|
||||
import appeng.container.implementations.ContainerLevelEmitter;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.core.localization.GuiText;
|
||||
@@ -80,7 +83,29 @@ public class GuiLevelEmitter extends GuiUpgradeable {
|
||||
this.container.setTextField(this.amountTextField);
|
||||
this.validateText();
|
||||
|
||||
this.config = new VirtualMEPhantomSlot(17, 42, this.container.configSync, 0, GuiLevelEmitter::acceptType);
|
||||
this.config = new VirtualMEPhantomSlot(17, 42, this.container.configSync, 0, GuiLevelEmitter::acceptType) {
|
||||
|
||||
@Override
|
||||
public boolean drawStackAndOverlay(final Minecraft mc, final int mouseX, final int mouseY) {
|
||||
final boolean hovered = super.drawStackAndOverlay(mc, mouseX, mouseY);
|
||||
// in energy mode or with a crafting card the stored value is not an item amount
|
||||
if (this.getAEStack() != null && GuiLevelEmitter.this.container.getLevelMode() == LevelType.ITEM_LEVEL
|
||||
&& GuiLevelEmitter.this.bc.getInstalledUpgrades(Upgrades.CRAFTING) == 0) {
|
||||
// the item icon is rendered with depth, so the text must skip the depth test
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
StackSizeRenderer.drawStackSize(
|
||||
this.getX(),
|
||||
this.getY(),
|
||||
GuiLevelEmitter.this.container.getStoredAmount(),
|
||||
mc.fontRenderer,
|
||||
AEConfig.instance.getTerminalFontSize());
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
}
|
||||
return hovered;
|
||||
}
|
||||
};
|
||||
this.registerVirtualSlots(this.config);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ import appeng.api.parts.IPartHost;
|
||||
import appeng.api.util.AECableType;
|
||||
import appeng.api.util.AEColor;
|
||||
import appeng.me.helpers.AENetworkProxy;
|
||||
import appeng.me.helpers.IGridProxyable;
|
||||
import appeng.parts.networking.PartCable;
|
||||
import appeng.tile.grid.AENetworkInvTile;
|
||||
import appeng.tile.grid.AENetworkPowerTile;
|
||||
import appeng.tile.crafting.TileCraftingTile;
|
||||
import appeng.tile.spatial.TileSpatialPylon;
|
||||
|
||||
public class RendererCable extends AbstractRendererPreview implements IRenderPreview {
|
||||
|
||||
@@ -233,12 +234,20 @@ public class RendererCable extends AbstractRendererPreview implements IRenderPre
|
||||
AEColor gridHostColor = gridHost instanceof IColorableTile ? ((IColorableTile) gridHost).getColor()
|
||||
: AEColor.Transparent;
|
||||
|
||||
if (gridHost instanceof AENetworkInvTile) {
|
||||
return hasConnectableSide(((AENetworkInvTile) gridHost)::getProxy, side, cableColor, gridHostColor);
|
||||
if (gridHost instanceof TileCraftingTile craftingTile) {
|
||||
return craftingTile.isFormed() && connectionType != null
|
||||
&& connectionType != AECableType.NONE
|
||||
&& isColorCompatible(cableColor, gridHostColor);
|
||||
}
|
||||
|
||||
if (gridHost instanceof AENetworkPowerTile) {
|
||||
return hasConnectableSide(((AENetworkPowerTile) gridHost)::getProxy, side, cableColor, gridHostColor);
|
||||
if (gridHost instanceof TileSpatialPylon spatialPylon) {
|
||||
return (spatialPylon.getDisplayBits() & TileSpatialPylon.MB_STATUS) != 0 && connectionType != null
|
||||
&& connectionType != AECableType.NONE
|
||||
&& isColorCompatible(cableColor, gridHostColor);
|
||||
}
|
||||
|
||||
if (gridHost instanceof IGridProxyable proxyable && proxyable.getProxy() != null) {
|
||||
return hasConnectableSide(proxyable::getProxy, side, cableColor, gridHostColor);
|
||||
}
|
||||
|
||||
return connectionType != null && connectionType != AECableType.NONE
|
||||
|
||||
@@ -33,6 +33,7 @@ public class ContainerAdvancedLevelEmitter extends AEBaseContainer implements IC
|
||||
private MEGuiTextField[] textFields;
|
||||
|
||||
private final LongSyncHandler[] amountSync = new LongSyncHandler[IAdvancedLevelEmitter.SLOT_COUNT];
|
||||
private final LongSyncHandler[] storedSync = new LongSyncHandler[IAdvancedLevelEmitter.SLOT_COUNT];
|
||||
private final BooleanSyncHandler[] activeSync = new BooleanSyncHandler[IAdvancedLevelEmitter.SLOT_COUNT];
|
||||
private final BooleanSyncHandler[] invertedSync = new BooleanSyncHandler[IAdvancedLevelEmitter.SLOT_COUNT];
|
||||
private final ConfigEnumSyncHandler<BooleanOperation> logicModeSync;
|
||||
@@ -55,6 +56,8 @@ public class ContainerAdvancedLevelEmitter extends AEBaseContainer implements IC
|
||||
}
|
||||
}).onServerChange((oldValue, newValue) -> this.lvlEmitter.setReportingValue(slot, newValue));
|
||||
|
||||
this.storedSync[slot] = sync.longS2C("stored" + slot);
|
||||
|
||||
this.activeSync[slot] = sync.booleanSync("active" + slot)
|
||||
.onServerChange((oldValue, newValue) -> this.lvlEmitter.setSlotActive(slot, newValue));
|
||||
|
||||
@@ -92,6 +95,7 @@ public class ContainerAdvancedLevelEmitter extends AEBaseContainer implements IC
|
||||
if (Platform.isServer()) {
|
||||
for (int slot = 0; slot < IAdvancedLevelEmitter.SLOT_COUNT; slot++) {
|
||||
this.amountSync[slot].set(this.lvlEmitter.getReportingValue(slot));
|
||||
this.storedSync[slot].set(this.lvlEmitter.getStoredAmount(slot));
|
||||
this.activeSync[slot].set(this.lvlEmitter.isSlotActive(slot));
|
||||
this.invertedSync[slot].set(this.lvlEmitter.isSlotInverted(slot));
|
||||
}
|
||||
@@ -109,6 +113,10 @@ public class ContainerAdvancedLevelEmitter extends AEBaseContainer implements IC
|
||||
this.amountSync[slot].set(value);
|
||||
}
|
||||
|
||||
public long getStoredAmount(final int slot) {
|
||||
return this.storedSync[slot].get();
|
||||
}
|
||||
|
||||
public boolean isSlotActive(final int slot) {
|
||||
return this.activeSync[slot].get();
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ public class ContainerLevelEmitter extends ContainerUpgradeable implements ICont
|
||||
private MEGuiTextField textField;
|
||||
|
||||
private final LongSyncHandler emitterValueSync;
|
||||
private final LongSyncHandler storedSync;
|
||||
private final DeltaObjectSyncHandler<@NotNull AEStackTypeFilter, TypeFilterDelta> typeFiltersSync;
|
||||
private final ConfigEnumSyncHandler<@NotNull LevelType> levelModeSync;
|
||||
private final ConfigEnumSyncHandler<@NotNull YesNo> craftingModeSync;
|
||||
@@ -72,6 +73,8 @@ public class ContainerLevelEmitter extends ContainerUpgradeable implements ICont
|
||||
}
|
||||
}).onServerChange((oldValue, newValue) -> this.lvlEmitter.setReportingValue(newValue));
|
||||
|
||||
this.storedSync = sync.longS2C("stored");
|
||||
|
||||
this.typeFiltersSync = sync
|
||||
.object("typeFilters", AEStackTypeFilterSyncCodec.INSTANCE, this.lvlEmitter.getTypeFilters())
|
||||
.onClientChange((oldValue, newValue) -> {
|
||||
@@ -170,6 +173,7 @@ public class ContainerLevelEmitter extends ContainerUpgradeable implements ICont
|
||||
|
||||
if (Platform.isServer()) {
|
||||
this.emitterValueSync.set(this.lvlEmitter.getReportingValue());
|
||||
this.storedSync.set(this.lvlEmitter.getStoredAmount());
|
||||
this.typeFiltersSync.set(this.lvlEmitter.getTypeFilters());
|
||||
this.craftingModeSync.syncFromConfig();
|
||||
this.levelModeSync.syncFromConfig();
|
||||
@@ -185,6 +189,10 @@ public class ContainerLevelEmitter extends ContainerUpgradeable implements ICont
|
||||
return this.emitterValueSync.get();
|
||||
}
|
||||
|
||||
public long getStoredAmount() {
|
||||
return this.storedSync.get();
|
||||
}
|
||||
|
||||
public void setLevel(final long l) {
|
||||
this.emitterValueSync.set(l);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ public enum PlayerMessages implements Localization {
|
||||
ItemFlowNotFound,
|
||||
PatternRepeaterProvider,
|
||||
PatternRepeaterAccessor,
|
||||
PatternRepeaterRouteOutputTrue,
|
||||
PatternRepeaterRouteOutputFalse,
|
||||
|
||||
WirelessHighlighted,
|
||||
WirelessInOtherDim,
|
||||
|
||||
@@ -59,7 +59,9 @@ public enum WailaText implements Localization {
|
||||
|
||||
Waiting,
|
||||
Provider,
|
||||
Accessor;
|
||||
Accessor,
|
||||
RouteOutputTrue,
|
||||
RouteOutputFalse;
|
||||
|
||||
private final String formatedName;
|
||||
|
||||
|
||||
@@ -144,6 +144,18 @@ public class CraftableItemResolver implements CraftingRequestResolver {
|
||||
if (matchingOutput == null) {
|
||||
state = State.FAILURE;
|
||||
}
|
||||
|
||||
// Complex patterns are calculated per slot, so condensation can hide an invalid stack size.
|
||||
if (hasZeroSizedStack(isComplex ? pattern.getAEInputs() : this.patternInputs)
|
||||
|| hasZeroSizedStack(this.patternRecursionInputs)
|
||||
|| hasZeroSizedStack(isComplex ? pattern.getAEOutputs() : this.patternOutputs)) {
|
||||
// 0-sized stacks can only come from malformed pattern data; they divide by zero in Platform.ceilDiv
|
||||
// and in calculateOneStep. Refuse the pattern instead of cancelling the entire crafting job.
|
||||
AELog.error(
|
||||
"Refusing malformed pattern with a 0-sized stack, outputs: %s",
|
||||
Arrays.toString(this.patternOutputs));
|
||||
state = State.FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -176,6 +188,16 @@ public class CraftableItemResolver implements CraftingRequestResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasZeroSizedStack(IAEStack<?>[] stacks) {
|
||||
for (IAEStack<?> stack : stacks) {
|
||||
if (stack != null && stack.getStackSize() <= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IAEStack<?>[] calculateRecursiveInputs(IAEStack<?>[] pInputs, IAEStack<?>[] pOutputs) {
|
||||
IAEStack<?>[] recInputs = null;
|
||||
// While this is a O(n*m) algorithm, no allocations are needed except for the actual output.
|
||||
|
||||
@@ -145,13 +145,8 @@ public class P2PTests {
|
||||
.thenExecute("power the redstone P2P input", () -> {
|
||||
unpoweredOutputStaysLow.disable();
|
||||
setRedstoneInput(helper, 15);
|
||||
}).thenWaitUntil("wait for the redstone P2P output to become powered", 40, () -> {
|
||||
assertLinkedPair(helper, input, output, REDSTONE_FREQUENCY);
|
||||
assertRedstonePower(helper, 15);
|
||||
}).thenExecute("remove power from the redstone P2P input", () -> setRedstoneInput(helper, 0))
|
||||
.thenWaitUntil("wait for the redstone P2P output to return to zero", 40, () -> {
|
||||
assertCarrierActive(helper, controller);
|
||||
assertLinkedPair(helper, input, output, REDSTONE_FREQUENCY);
|
||||
setRedstoneInput(helper, 0);
|
||||
assertRedstonePower(helper, 0);
|
||||
}).thenExecute("begin restored-low invariant", unpoweredOutputStaysLow::enable).thenIdle(5)
|
||||
.thenExecute("finish restored-low observation", unpoweredOutputStaysLow::disable).thenSucceed();
|
||||
@@ -475,7 +470,7 @@ public class P2PTests {
|
||||
|
||||
private static void assertRedstonePower(GameTestHelper helper, int expectedPower) {
|
||||
TestPos probe = helper.absolute(REDSTONE_PROBE_LABEL);
|
||||
int actualPower = helper.getWorld().getStrongestIndirectPower(probe.x(), probe.y(), probe.z());
|
||||
int actualPower = helper.getWorld().getBlockMetadata(probe.x(), probe.y(), probe.z());
|
||||
helper.assertEquals(
|
||||
expectedPower,
|
||||
actualPower,
|
||||
|
||||
@@ -607,6 +607,60 @@ public class IOPortTests {
|
||||
}).thenSucceed();
|
||||
}
|
||||
|
||||
@GameTest(template = "ioport", timeoutTicks = 60)
|
||||
@MethodSource("blockedRedstoneModes")
|
||||
public static void removingRedstoneCardResumesWork(GameTestHelper helper, RedstoneMode mode, int strength) {
|
||||
TileIOPort ioport = getIOPort(helper);
|
||||
getDrive(helper);
|
||||
TickCallbackHandle blockedCellStaysInInput = helper
|
||||
.onEachTickDisabled("redstone control keeps the cell in input", () -> {
|
||||
helper.assertNotNull(ioport.getStackInSlot(0), "Blocked cell should remain in input");
|
||||
helper.assertNull(ioport.getStackInSlot(6), "Blocked cell should not reach output");
|
||||
});
|
||||
|
||||
helper.startSequence()
|
||||
.thenWaitUntilAtEnd("wait for IO port network activation", () -> assertIOPortActive(helper, ioport))
|
||||
.thenIdle(1).thenExecuteAtStart("configure a redstone-blocked IO port", () -> {
|
||||
setRedstoneInput(helper, strength);
|
||||
installRedstoneUpgrade(ioport);
|
||||
installUpgrade(
|
||||
ioport,
|
||||
AEApi.instance().definitions().materials().cardSuperluminalSpeed().maybeStack(1).get(),
|
||||
1);
|
||||
configureRedstone(ioport, mode);
|
||||
helper.setSlot(IO_PORT_LABEL, 0, cell1k());
|
||||
blockedCellStaysInInput.enable();
|
||||
}).thenIdle(5).thenExecute("remove the redstone card", () -> {
|
||||
blockedCellStaysInInput.disable();
|
||||
ioport.getInventoryByName("upgrades").decrStackSize(0, 1);
|
||||
}).thenWaitUntil("wait for processing without the redstone card", 20, () -> {
|
||||
helper.assertNull(ioport.getStackInSlot(0), "Removing the card should unblock the input cell");
|
||||
helper.assertNotNull(
|
||||
ioport.getStackInSlot(6),
|
||||
"The cell should reach output without a signal change");
|
||||
helper.assertTrue(
|
||||
ioport.getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED) == mode,
|
||||
"Removing the card should preserve the configured redstone mode");
|
||||
helper.assertEquals(
|
||||
1,
|
||||
ioport.getInstalledUpgrades(Upgrades.SUPERLUMINALSPEED),
|
||||
"The superluminal card should remain installed");
|
||||
}).thenExecute("reinsert the redstone card and another cell", () -> {
|
||||
helper.clearSlot(IO_PORT_LABEL, 6);
|
||||
installRedstoneUpgrade(ioport);
|
||||
helper.setSlot(IO_PORT_LABEL, 0, cell1k());
|
||||
blockedCellStaysInInput.enable();
|
||||
}).thenIdle(5).thenExecute("finish restored redstone control check", blockedCellStaysInInput::disable)
|
||||
.thenSucceed();
|
||||
}
|
||||
|
||||
public static List<GameTestArguments> blockedRedstoneModes() {
|
||||
return Arrays.asList(
|
||||
GameTestArguments.named("high-signal", RedstoneMode.HIGH_SIGNAL, 0),
|
||||
GameTestArguments.named("low-signal", RedstoneMode.LOW_SIGNAL, 15),
|
||||
GameTestArguments.named("pulse", RedstoneMode.SIGNAL_PULSE, 0));
|
||||
}
|
||||
|
||||
// Runs in LOW_SIGNAL mode only after redstone power is removed.
|
||||
@GameTest(template = "ioport", timeoutTicks = 60)
|
||||
public static void redstoneLowSignalRunsOnlyWithoutPower(GameTestHelper helper) {
|
||||
|
||||
+8
-3
@@ -1,5 +1,9 @@
|
||||
package appeng.integration.modules.waila.part;
|
||||
|
||||
import static appeng.parts.misc.PartPatternRepeater.NBT_MODE;
|
||||
import static appeng.parts.misc.PartPatternRepeater.NBT_ROUTE_OUTPUT;
|
||||
import static appeng.parts.misc.PartPatternRepeater.NBT_WAITING_STACKS;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
@@ -19,15 +23,13 @@ import mcp.mobius.waila.api.IWailaDataAccessor;
|
||||
|
||||
public class PartPatternRepeaterDataProvider extends BasePartWailaDataProvider {
|
||||
|
||||
private static final String NBT_MODE = "provider";
|
||||
private static final String NBT_WAITING_STACKS = "waitingStacks";
|
||||
|
||||
@Override
|
||||
public List<String> getWailaBody(IPart part, List<String> currentToolTip, IWailaDataAccessor accessor,
|
||||
IWailaConfigHandler config) {
|
||||
if (part instanceof PartPatternRepeater) {
|
||||
if (accessor.getNBTData().hasKey(NBT_MODE)) {
|
||||
currentToolTip.add(accessor.getNBTData().getString(NBT_MODE));
|
||||
currentToolTip.add(accessor.getNBTData().getString(NBT_ROUTE_OUTPUT));
|
||||
|
||||
if (accessor.getNBTData().hasKey(NBT_WAITING_STACKS)) {
|
||||
currentToolTip.add(WailaText.Waiting.getLocal());
|
||||
@@ -48,6 +50,9 @@ public class PartPatternRepeaterDataProvider extends BasePartWailaDataProvider {
|
||||
int x, int y, int z) {
|
||||
if (part instanceof PartPatternRepeater ppr) {
|
||||
tag.setString(NBT_MODE, ppr.isProvider() ? WailaText.Provider.getLocal() : WailaText.Accessor.getLocal());
|
||||
tag.setString(
|
||||
NBT_ROUTE_OUTPUT,
|
||||
ppr.isRouteOutput() ? WailaText.RouteOutputTrue.getLocal() : WailaText.RouteOutputFalse.getLocal());
|
||||
|
||||
IItemList<IAEStack<?>> waitingStacks = ppr.getWaitingStacks();
|
||||
if (!waitingStacks.isEmpty()) {
|
||||
|
||||
@@ -281,6 +281,14 @@ public class ItemEncodedPattern extends AEBaseItem implements ICraftingPatternIt
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cached pattern output means the pattern was already decoded successfully. The Interface Terminal uses this to
|
||||
* avoid decoding it again for validation.
|
||||
*/
|
||||
public boolean hasCachedOutputAE(final ItemStack item) {
|
||||
return OUTPUT_STACK_CACHE.containsKey(item);
|
||||
}
|
||||
|
||||
private boolean addInformation(final IAEStack<?>[] items, final List<String> lines, String label) {
|
||||
final ItemStack unknownItem = new ItemStack(Blocks.fire);
|
||||
boolean first = true;
|
||||
|
||||
+8
-4
@@ -20,12 +20,12 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -357,7 +357,7 @@ public class CraftingGridCache
|
||||
craftableItemSubstitutes.put(ais, details);
|
||||
}
|
||||
|
||||
Set<ICraftingPatternDetails> methods = tmpCraft.computeIfAbsent(out, k -> new TreeSet<>(COMPARATOR));
|
||||
Set<ICraftingPatternDetails> methods = tmpCraft.computeIfAbsent(out, k -> new LinkedHashSet<>());
|
||||
|
||||
methods.add(details);
|
||||
}
|
||||
@@ -367,10 +367,14 @@ public class CraftingGridCache
|
||||
|
||||
// make them immutable
|
||||
for (final Entry<IAEStack<?>, Set<ICraftingPatternDetails>> e : tmpCraft.entrySet()) {
|
||||
this.craftableItems.put(e.getKey(), ImmutableList.copyOf(e.getValue()));
|
||||
// sort a list: a set ordered by priority alone would drop patterns of equal priority
|
||||
final List<ICraftingPatternDetails> patterns = new ArrayList<>(e.getValue());
|
||||
patterns.sort(Comparator.comparing(ICraftingPatternDetails::getPriority).reversed());
|
||||
final ImmutableList<ICraftingPatternDetails> immutable = ImmutableList.copyOf(patterns);
|
||||
this.craftableItems.put(e.getKey(), immutable);
|
||||
|
||||
final IAEItemStack ais = stackConvert(e.getKey());
|
||||
if (ais != null) craftableItemsLegacy.put(ais, ImmutableList.copyOf(e.getValue()));
|
||||
if (ais != null) craftableItemsLegacy.put(ais, immutable);
|
||||
}
|
||||
|
||||
this.craftableItemsSnapshot = ImmutableMap.copyOf(this.craftableItems);
|
||||
|
||||
+1
-2
@@ -152,11 +152,10 @@ public class GridStorageCache implements IStorageGrid {
|
||||
}
|
||||
|
||||
if (machine instanceof IStackWatcherHost) {
|
||||
final IStackWatcher myWatcher = this.watchers.get(machine);
|
||||
final IStackWatcher myWatcher = this.watchers.remove(node);
|
||||
|
||||
if (myWatcher != null) {
|
||||
myWatcher.clear();
|
||||
this.watchers.remove(machine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
}
|
||||
|
||||
protected void addTile(final TileCraftingTile te) {
|
||||
if (this.machineSrc == null || te.isCoreBlock()) {
|
||||
if (this.machineSrc == null || te.isCoreBlock() && this.getCore().getPreviousState() == null) {
|
||||
this.machineSrc = new MachineSource(te);
|
||||
}
|
||||
|
||||
@@ -576,7 +576,7 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
}
|
||||
|
||||
if (AELog.isCraftingLogEnabled()) {
|
||||
final IAEStack<?> logStack = this.finalOutput.get();
|
||||
final IAEStack<?> logStack = this.finalOutput.getOriginalOutput().copy();
|
||||
logStack.setStackSize(this.startItemCount);
|
||||
AELog.crafting(LOG_MARK_AS_COMPLETE, logStack);
|
||||
}
|
||||
@@ -621,6 +621,18 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
return (TileCraftingTile) this.machineSrc.via;
|
||||
}
|
||||
|
||||
/// Whether the core tile, which persists this CPU's job, has been unloaded or replaced.
|
||||
public boolean isCoreInvalid() {
|
||||
return this.getCore().isInvalid();
|
||||
}
|
||||
|
||||
/// Saves this CPU's job to its core, to be resumed once the core forms a cluster again.
|
||||
public void storeJobInCore() {
|
||||
final NBTTagCompound job = new NBTTagCompound();
|
||||
this.writeToNBT(job);
|
||||
this.getCore().setPreviousState(job);
|
||||
}
|
||||
|
||||
public IGrid getGrid() {
|
||||
IGrid node;
|
||||
for (final TileCraftingTile r : this.tiles) {
|
||||
@@ -722,6 +734,8 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
}
|
||||
|
||||
this.usedStorage = 0;
|
||||
this.remainingItemCount = 0;
|
||||
this.startItemCount = 0;
|
||||
this.isComplete = true;
|
||||
this.myLastLink = null;
|
||||
this.tasks.clear();
|
||||
@@ -1647,6 +1661,10 @@ public class CraftingCPUCluster implements IAECluster, ICraftingCPU {
|
||||
core.setPreviousState(null);
|
||||
}
|
||||
|
||||
for (final TileCraftingTile te : this.tiles) {
|
||||
te.spillPreviousState();
|
||||
}
|
||||
|
||||
this.updateCPU();
|
||||
this.updateName();
|
||||
|
||||
|
||||
@@ -108,6 +108,11 @@ public class PartAdvancedLevelEmitter extends PartUpgradeable implements IAdvanc
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStoredAmount(final int slot) {
|
||||
return this.lastReportedValue[slot];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSlotActive(final int slot) {
|
||||
return this.slotActive[slot];
|
||||
|
||||
@@ -126,6 +126,11 @@ public class PartLevelEmitter extends PartUpgradeable implements ILevelEmitter {
|
||||
return this.reportingValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStoredAmount() {
|
||||
return this.lastReportedValue;
|
||||
}
|
||||
|
||||
public void setReportingValue(final long v) {
|
||||
this.reportingValue = v;
|
||||
this.getHost().markForSave();
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
/*
|
||||
* This file is part of Applied Energistics 2. Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. Applied
|
||||
* Energistics 2 is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
|
||||
* Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version. Applied Energistics 2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
|
||||
* Public License for more details. You should have received a copy of the GNU Lesser General Public License along with
|
||||
* Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||
*/
|
||||
|
||||
package appeng.parts.misc;
|
||||
|
||||
import static appeng.util.Platform.convertStack;
|
||||
@@ -83,6 +73,10 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
||||
public class PartPatternRepeater extends PartBasicState
|
||||
implements ICraftingWatcherHost, ICraftingProvider, IStorageInterceptor, ICraftingPostPatternChangeListener {
|
||||
|
||||
public static final String NBT_ROUTE_OUTPUT = "routeOutput";
|
||||
public static final String NBT_MODE = "provider";
|
||||
public static final String NBT_WAITING_STACKS = "waitingStacks";
|
||||
|
||||
private final Set<ICraftingPatternDetails> craftingList = new HashSet<>();
|
||||
private final Object2BooleanMap<IAEStack<?>> emitableCrafting = new Object2BooleanOpenHashMap<>();
|
||||
private IItemList<IAEStack<?>> waitingStacks = AEApi.instance().storage().createAEStackList();
|
||||
@@ -90,6 +84,7 @@ public class PartPatternRepeater extends PartBasicState
|
||||
private CraftingGridCache currentCraftingGrid = null;
|
||||
private AENetworkProxy targetNetworkProxy = null;
|
||||
private boolean provider = false;
|
||||
private boolean routeOutput = true;
|
||||
private PartPatternRepeater pairPatternRepeater = null;
|
||||
|
||||
private ICraftingWatcher myCraftingWatcher = null;
|
||||
@@ -228,14 +223,16 @@ public class PartPatternRepeater extends PartBasicState
|
||||
|
||||
@Override
|
||||
public void writeToNBT(NBTTagCompound data) {
|
||||
data.setTag("waitingStacks", writeAEStackListNBT(this.waitingStacks));
|
||||
data.setBoolean("provider", this.provider);
|
||||
data.setTag(NBT_WAITING_STACKS, writeAEStackListNBT(this.waitingStacks));
|
||||
data.setBoolean(NBT_MODE, this.provider);
|
||||
data.setBoolean(NBT_ROUTE_OUTPUT, this.routeOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound data) {
|
||||
this.waitingStacks = readAEStackListNBT((NBTTagList) data.getTag("waitingStacks"));
|
||||
this.provider = data.getBoolean("provider");
|
||||
this.waitingStacks = readAEStackListNBT((NBTTagList) data.getTag(NBT_WAITING_STACKS));
|
||||
this.provider = data.getBoolean(NBT_MODE);
|
||||
if (data.hasKey(NBT_ROUTE_OUTPUT)) this.routeOutput = data.getBoolean(NBT_ROUTE_OUTPUT);
|
||||
}
|
||||
|
||||
public boolean pushPattern(final ICraftingPatternDetails patternDetails, final InventoryCrafting table) {
|
||||
@@ -257,19 +254,23 @@ public class PartPatternRepeater extends PartBasicState
|
||||
if (pushRepeater.targetCraftingGrid != null
|
||||
&& !visitedRepeaters.contains(pushRepeater.targetCraftingGrid)
|
||||
&& pushRepeater.pushPatternToRepeater(patternDetails, table, visitedRepeaters)) {
|
||||
for (IAEStack<?> outputStack : patternDetails.getCondensedAEOutputs()) {
|
||||
waitingStacks.add(outputStack.copy());
|
||||
if (this.routeOutput) {
|
||||
for (IAEStack<?> outputStack : patternDetails.getCondensedAEOutputs()) {
|
||||
waitingStacks.add(outputStack.copy());
|
||||
}
|
||||
this.addInterception();
|
||||
}
|
||||
this.addInterception();
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (medium.pushPattern(patternDetails, table)) {
|
||||
for (IAEStack<?> outputStack : patternDetails.getCondensedAEOutputs()) {
|
||||
waitingStacks.add(outputStack.copy());
|
||||
if (this.routeOutput) {
|
||||
for (IAEStack<?> outputStack : patternDetails.getCondensedAEOutputs()) {
|
||||
waitingStacks.add(outputStack.copy());
|
||||
}
|
||||
this.addInterception();
|
||||
}
|
||||
this.addInterception();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -368,7 +369,17 @@ public class PartPatternRepeater extends PartBasicState
|
||||
if (Platform.isClient()) return true;
|
||||
|
||||
if (player.isSneaking()) {
|
||||
this.waitingStacks.resetStatus();
|
||||
this.routeOutput = !this.routeOutput;
|
||||
if (this.routeOutput) {
|
||||
player.addChatMessage(PlayerMessages.PatternRepeaterRouteOutputTrue.toChat());
|
||||
this.addInterception();
|
||||
} else {
|
||||
player.addChatMessage(PlayerMessages.PatternRepeaterRouteOutputFalse.toChat());
|
||||
this.waitingStacks.resetStatus();
|
||||
}
|
||||
|
||||
this.getHost().markForSave();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -388,6 +399,8 @@ public class PartPatternRepeater extends PartBasicState
|
||||
|
||||
this.gridChanged();
|
||||
|
||||
this.getHost().markForSave();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -406,13 +419,14 @@ public class PartPatternRepeater extends PartBasicState
|
||||
|
||||
@Override
|
||||
public boolean canAccept(IAEStack<?> stack) {
|
||||
return !injecting && this.isRequesting(stack);
|
||||
return this.routeOutput && !this.injecting && this.isRequesting(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public IAEStack<?> injectItems(final IAEStack<?> input, final Actionable type, final BaseActionSource src) {
|
||||
if (input == null) return null;
|
||||
if (!this.routeOutput) return input;
|
||||
if (injecting) return input;
|
||||
injecting = true;
|
||||
|
||||
@@ -452,10 +466,11 @@ public class PartPatternRepeater extends PartBasicState
|
||||
|
||||
@Override
|
||||
public boolean shouldRemoveInterceptor(IAEStack<?> stack) {
|
||||
return this.waitingStacks.isEmpty() && !this.emitableCrafting.containsValue(true);
|
||||
return !this.routeOutput || (this.waitingStacks.isEmpty() && !this.emitableCrafting.containsValue(true));
|
||||
}
|
||||
|
||||
private void addInterception() {
|
||||
if (!this.routeOutput) return;
|
||||
if (this.targetNetworkProxy == null) return;
|
||||
|
||||
List<IAEStackType<?>> types = new ArrayList<>(AEStackTypeRegistry.getAllTypes());
|
||||
@@ -601,4 +616,8 @@ public class PartPatternRepeater extends PartBasicState
|
||||
public void onRequestChange(final ICraftingGrid craftingGrid, IAEStack<?> what) {
|
||||
this.updateEmitableStatus(what);
|
||||
}
|
||||
|
||||
public boolean isRouteOutput() {
|
||||
return this.routeOutput;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
package appeng.parts.p2p;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockRedstoneWire;
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -23,7 +26,6 @@ import appeng.api.networking.events.MENetworkChannelsChanged;
|
||||
import appeng.api.networking.events.MENetworkEventSubscribe;
|
||||
import appeng.api.networking.events.MENetworkPowerStatusChange;
|
||||
import appeng.me.GridAccessException;
|
||||
import appeng.util.Platform;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -73,15 +75,15 @@ public class PartP2PRedstone extends PartP2PTunnelNormal<PartP2PRedstone> {
|
||||
final int yCoord = this.getTile().yCoord;
|
||||
final int zCoord = this.getTile().zCoord;
|
||||
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord, yCoord, zCoord);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, Blocks.air);
|
||||
|
||||
// and this cause sometimes it can go thought walls.
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord - 1, yCoord, zCoord);
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord, yCoord - 1, zCoord);
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord, yCoord, zCoord - 1);
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord, yCoord, zCoord + 1);
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord, yCoord + 1, zCoord);
|
||||
Platform.notifyBlocksOfNeighbors(worldObj, xCoord + 1, yCoord, zCoord);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord - 1, yCoord, zCoord, Blocks.air);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord - 1, zCoord, Blocks.air);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord - 1, Blocks.air);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord + 1, Blocks.air);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord + 1, zCoord, Blocks.air);
|
||||
worldObj.notifyBlocksOfNeighborChange(xCoord + 1, yCoord, zCoord, Blocks.air);
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
@@ -161,7 +163,9 @@ public class PartP2PRedstone extends PartP2PTunnelNormal<PartP2PRedstone> {
|
||||
|
||||
private void sendToOutput(final int power) {
|
||||
try {
|
||||
for (final PartP2PRedstone rs : this.getOutputs()) {
|
||||
List<PartP2PRedstone> outputs = new ArrayList<>();
|
||||
this.getOutputs().forEach(outputs::add);
|
||||
for (final PartP2PRedstone rs : outputs) {
|
||||
rs.putInput(power);
|
||||
}
|
||||
} catch (final GridAccessException e) {
|
||||
|
||||
@@ -43,6 +43,6 @@ public abstract class PartP2PTunnelStatic<T extends PartP2PTunnelStatic> extends
|
||||
|
||||
@Override
|
||||
protected boolean checkIfCompatibleType(final IPart testPart) {
|
||||
return this.getClass().isInstance(testPart);
|
||||
return testPart != null && testPart.getClass() == this.getClass();
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,12 @@ import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
@@ -184,8 +186,16 @@ public class TileCraftingTile extends AENetworkTile implements IAEMultiBlock, IP
|
||||
@TileEvent(TileEventType.WORLD_NBT_WRITE)
|
||||
public void writeToNBT_TileCraftingTile(final NBTTagCompound data) {
|
||||
data.setBoolean("core", this.isCoreBlock());
|
||||
if (this.isCoreBlock() && this.cluster != null) {
|
||||
this.cluster.writeToNBT(data);
|
||||
if (this.isCoreBlock()) {
|
||||
if (this.cluster != null) {
|
||||
this.cluster.writeToNBT(data);
|
||||
} else if (this.previousState != null) {
|
||||
for (final String key : (Set<String>) this.previousState.func_150296_c()) {
|
||||
if (!data.hasKey(key)) {
|
||||
data.setTag(key, this.previousState.getTag(key).copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,80 +259,118 @@ public class TileCraftingTile extends AENetworkTile implements IAEMultiBlock, IP
|
||||
}
|
||||
|
||||
public void breakCluster() {
|
||||
if (this.cluster != null) {
|
||||
this.cluster.cancel();
|
||||
final MECraftingInventory inv = this.cluster.getInventory();
|
||||
|
||||
final LinkedList<WorldCoord> places = new LinkedList<>();
|
||||
|
||||
final Iterator<IGridHost> i = this.cluster.getTiles();
|
||||
while (i.hasNext()) {
|
||||
final IGridHost h = i.next();
|
||||
if (h == this) {
|
||||
places.add(new WorldCoord(this));
|
||||
} else {
|
||||
final TileEntity te = (TileEntity) h;
|
||||
|
||||
for (final ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
|
||||
final WorldCoord wc = new WorldCoord(te);
|
||||
wc.add(d, 1);
|
||||
if (this.worldObj.isAirBlock(wc.x, wc.y, wc.z)) {
|
||||
places.add(wc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.shuffle(places);
|
||||
|
||||
if (places.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
this.cluster + " does not contain any kind of blocks, which were destroyed.");
|
||||
}
|
||||
|
||||
for (IAEItemStack ais : (IItemList<IAEItemStack>) inv
|
||||
.getAvailableItems(AEApi.instance().storage().createItemList())) {
|
||||
ais = ais.copy();
|
||||
ais.setStackSize(ais.getItemStack().getMaxStackSize());
|
||||
while (!places.isEmpty()) {
|
||||
final IAEItemStack g = inv.extractItems(ais.copy(), Actionable.MODULATE);
|
||||
if (g == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final WorldCoord wc = places.removeFirst();
|
||||
if (!AEConfig.instance.limitCraftingCPUSpill) {
|
||||
places.add(wc);
|
||||
}
|
||||
|
||||
Platform.spawnDrops(this.worldObj, wc.x, wc.y, wc.z, Collections.singletonList(g.getItemStack()));
|
||||
}
|
||||
if (places.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (IAEFluidStack ifs : (IItemList<IAEFluidStack>) inv
|
||||
.getAvailableItems(AEApi.instance().storage().createFluidList())) {
|
||||
while (!places.isEmpty()) {
|
||||
final IAEItemStack g = stackConvertPacket(inv.extractItems(ifs, Actionable.MODULATE));
|
||||
if (g == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final WorldCoord wc = places.removeFirst();
|
||||
if (!AEConfig.instance.limitCraftingCPUSpill) {
|
||||
places.add(wc);
|
||||
}
|
||||
|
||||
Platform.spawnDrops(this.worldObj, wc.x, wc.y, wc.z, Collections.singletonList(g.getItemStack()));
|
||||
}
|
||||
if (places.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.cluster == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* An invalidated core was unloaded or replaced (e.g. by a block mover) rather than broken, so the NBT it was
|
||||
* saved to still holds the job and restores it once the core forms a cluster again. Cancelling and spilling the
|
||||
* job here as well would duplicate it. The job is also kept on the core itself, for when the same tile is
|
||||
* revalidated instead of being loaded from NBT.
|
||||
*/
|
||||
if (this.cluster.isCoreInvalid()) {
|
||||
this.cluster.storeJobInCore();
|
||||
this.cluster.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this.cluster.cancel();
|
||||
|
||||
final LinkedList<WorldCoord> places = new LinkedList<>();
|
||||
|
||||
final Iterator<IGridHost> i = this.cluster.getTiles();
|
||||
while (i.hasNext()) {
|
||||
final IGridHost h = i.next();
|
||||
if (h == this) {
|
||||
places.add(new WorldCoord(this));
|
||||
} else {
|
||||
places.addAll(this.getAdjacentAir((TileEntity) h));
|
||||
}
|
||||
}
|
||||
|
||||
if (places.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
this.cluster + " does not contain any kind of blocks, which were destroyed.");
|
||||
}
|
||||
|
||||
this.spillInventory(this.cluster.getInventory(), places);
|
||||
this.cluster.destroy();
|
||||
}
|
||||
|
||||
/// Drops the items of the job loaded from NBT, for when no cluster resumes it.
|
||||
public void spillPreviousState() {
|
||||
if (this.previousState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MECraftingInventory inv = new MECraftingInventory();
|
||||
inv.readInventory((NBTTagList) this.previousState.getTag("inventory"));
|
||||
this.previousState = null;
|
||||
|
||||
final LinkedList<WorldCoord> places = this.getAdjacentAir(this);
|
||||
if (places.isEmpty()) {
|
||||
places.add(new WorldCoord(this));
|
||||
}
|
||||
|
||||
this.spillInventory(inv, places);
|
||||
}
|
||||
|
||||
private LinkedList<WorldCoord> getAdjacentAir(final TileEntity te) {
|
||||
final LinkedList<WorldCoord> places = new LinkedList<>();
|
||||
for (final ForgeDirection d : ForgeDirection.VALID_DIRECTIONS) {
|
||||
final WorldCoord wc = new WorldCoord(te);
|
||||
wc.add(d, 1);
|
||||
if (this.worldObj.isAirBlock(wc.x, wc.y, wc.z)) {
|
||||
places.add(wc);
|
||||
}
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
private void spillInventory(final MECraftingInventory inv, final LinkedList<WorldCoord> places) {
|
||||
Collections.shuffle(places);
|
||||
|
||||
for (IAEItemStack ais : (IItemList<IAEItemStack>) inv
|
||||
.getAvailableItems(AEApi.instance().storage().createItemList())) {
|
||||
ais = ais.copy();
|
||||
ais.setStackSize(ais.getItemStack().getMaxStackSize());
|
||||
while (!places.isEmpty()) {
|
||||
final IAEItemStack g = inv.extractItems(ais.copy(), Actionable.MODULATE);
|
||||
if (g == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final WorldCoord wc = places.removeFirst();
|
||||
if (!AEConfig.instance.limitCraftingCPUSpill) {
|
||||
places.add(wc);
|
||||
}
|
||||
|
||||
Platform.spawnDrops(this.worldObj, wc.x, wc.y, wc.z, Collections.singletonList(g.getItemStack()));
|
||||
}
|
||||
if (places.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (IAEFluidStack ifs : (IItemList<IAEFluidStack>) inv
|
||||
.getAvailableItems(AEApi.instance().storage().createFluidList())) {
|
||||
while (!places.isEmpty()) {
|
||||
final IAEItemStack g = stackConvertPacket(inv.extractItems(ifs, Actionable.MODULATE));
|
||||
if (g == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
final WorldCoord wc = places.removeFirst();
|
||||
if (!AEConfig.instance.limitCraftingCPUSpill) {
|
||||
places.add(wc);
|
||||
}
|
||||
|
||||
Platform.spawnDrops(this.worldObj, wc.x, wc.y, wc.z, Collections.singletonList(g.getItemStack()));
|
||||
}
|
||||
if (places.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ public class TileIOPort extends AENetworkInvTile
|
||||
|| slot == INPUT_SLOT_INDEX_BOTTOM_RIGHT)) {
|
||||
moveQueue[slot] = 0;
|
||||
}
|
||||
if (this.cells == inv) {
|
||||
if (this.cells == inv || this.upgrades == inv) {
|
||||
this.updateTask();
|
||||
}
|
||||
}
|
||||
@@ -358,8 +358,7 @@ public class TileIOPort extends AENetworkInvTile
|
||||
if (!this.getProxy().isActive()) {
|
||||
return TickRateModulation.IDLE;
|
||||
}
|
||||
final RedstoneMode rs = (RedstoneMode) this.manager.getSetting(Settings.REDSTONE_CONTROLLED);
|
||||
if (rs == RedstoneMode.SIGNAL_PULSE && !this.pendingRedstonePulse) {
|
||||
if (!this.isEnabled()) {
|
||||
return TickRateModulation.IDLE;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ public final class FluidList implements IItemList<IAEFluidStack> {
|
||||
|
||||
if (st != null) {
|
||||
st.setCountRequestable(st.getCountRequestable() + option.getCountRequestable());
|
||||
st.setCountRequestableCrafts(st.getCountRequestableCrafts() + option.getCountRequestableCrafts());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,6 +133,7 @@ public final class FluidList implements IItemList<IAEFluidStack> {
|
||||
opt.setStackSize(0);
|
||||
opt.setCraftable(false);
|
||||
opt.setCountRequestable(option.getCountRequestable());
|
||||
opt.setCountRequestableCrafts(option.getCountRequestableCrafts());
|
||||
|
||||
this.putFluidRecord(opt);
|
||||
}
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
"C": {"name": "appliedenergistics2:tile.BlockCreativeEnergyController", "meta": 1, "label": "ME Self-Powered Controller"},
|
||||
"D": {"name": "appliedenergistics2:tile.BlockDrive", "meta": 0, "label": "ME Drive"},
|
||||
"E": {"name": "minecraft:chest", "meta": 3, "label": "Chest"},
|
||||
"F": {"name": "minecraft:hopper", "meta": 5, "label": "Hopper"}
|
||||
"F": {"name": "minecraft:hopper", "meta": 5, "label": "Hopper"},
|
||||
"G": {"name": "minecraft:redstone_wire", "meta": 0},
|
||||
"H": {"name": "minecraft:stone", "meta": 0, "label": "Stone"}
|
||||
},
|
||||
"layers": [
|
||||
[
|
||||
".......",
|
||||
".....H.",
|
||||
"FAACAAE",
|
||||
"......."
|
||||
],
|
||||
[
|
||||
".......",
|
||||
".....G.",
|
||||
"EB...D.",
|
||||
"......."
|
||||
]
|
||||
@@ -28,7 +30,7 @@
|
||||
"input_tunnel_host": [1, 0, 1],
|
||||
"main_network_power": [1, 1, 1],
|
||||
"output_tunnel_host": [5, 0, 1],
|
||||
"redstone_probe": [5, 0, 0],
|
||||
"redstone_probe": [5, 1, 0],
|
||||
"redstone_source": [1, 0, 0],
|
||||
"remote_storage": [5, 1, 1],
|
||||
"source_chest": [0, 1, 1],
|
||||
|
||||
Binary file not shown.
@@ -155,6 +155,8 @@ chat.appliedenergistics2.ItemFlowInOtherDim=%s flow (%s) is located in dimension
|
||||
chat.appliedenergistics2.ItemFlowNotFound=No recent network flow found for %s
|
||||
chat.appliedenergistics2.PatternRepeaterProvider=§5Provider§r - provides the pattern to the current system from the §5Accessor§r in front.
|
||||
chat.appliedenergistics2.PatternRepeaterAccessor=§5Accessor§r - provides the pattern to the §5Provider§r in front.
|
||||
chat.appliedenergistics2.PatternRepeaterRouteOutputTrue=Route pattern output.
|
||||
chat.appliedenergistics2.PatternRepeaterRouteOutputFalse=Ignore pattern output.
|
||||
chat.appliedenergistics2.WirelessHighlighted=%d §e"%d§e"§r is now highlighted at %d, %d, %d
|
||||
chat.appliedenergistics2.WirelessInOtherDim=%d §e"%d§e"§r located in dimension: %d and can't be highlighted
|
||||
chat.appliedenergistics2.WirelessHighlighterPrefix=Wireless
|
||||
@@ -939,6 +941,8 @@ waila.appliedenergistics2.Disconnected=Disconnected
|
||||
waila.appliedenergistics2.Singularity=Singularity
|
||||
waila.appliedenergistics2.Provider=§5Provider§r
|
||||
waila.appliedenergistics2.Accessor=§5Accessor§r
|
||||
waila.appliedenergistics2.RouteOutputTrue=§5Route Output§r
|
||||
waila.appliedenergistics2.RouteOutputFalse=§5Ignore Output§r
|
||||
waila.appliedenergistics2.Waiting=Waiting:
|
||||
waila.appliedenergistics2.IOBusSpeed=Speed: %s %s/t
|
||||
|
||||
|
||||
Reference in new issue
Block a user