diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml index 2cee10876..ba01625ab 100644 --- a/.github/workflows/pull_requests.yml +++ b/.github/workflows/pull_requests.yml @@ -2,7 +2,8 @@ name: 'Build PRs' on: pull_request: - branches: [ master ] + branches: + - '*' jobs: build: diff --git a/src/main/java/appeng/client/gui/AEBaseScreen.java b/src/main/java/appeng/client/gui/AEBaseScreen.java index 69da8b292..5d6630516 100644 --- a/src/main/java/appeng/client/gui/AEBaseScreen.java +++ b/src/main/java/appeng/client/gui/AEBaseScreen.java @@ -106,6 +106,7 @@ public abstract class AEBaseScreen extends HandledScr private Stopwatch dbl_clickTimer = Stopwatch.createStarted(); private ItemStack dbl_whichItem = ItemStack.EMPTY; private Slot bl_clicked; + private boolean handlingRightClick; protected final List guiSlots = new ArrayList<>(); public AEBaseScreen(T container, PlayerInventory playerInventory, Text title) { @@ -284,12 +285,18 @@ public abstract class AEBaseScreen extends HandledScr public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) { this.drag_click.clear(); + // Forward right-clicks as-if they were left-clicks if (btn == 1) { - for (final Object o : this.buttons) { - final AbstractButtonWidget widget = (AbstractButtonWidget) o; - if (widget.isMouseOver(xCoord, yCoord)) { - return super.mouseClicked(xCoord, yCoord, 0); + handlingRightClick = true; + try { + for (final Object o : this.buttons) { + final AbstractButtonWidget widget = (AbstractButtonWidget) o; + if (widget.isMouseOver(xCoord, yCoord)) { + return super.mouseClicked(xCoord, yCoord, 0); + } } + } finally { + handlingRightClick = false; } } @@ -635,16 +642,8 @@ public abstract class AEBaseScreen extends HandledScr this.itemRenderer.zOffset = 0.0F; } - protected Text getGuiDisplayName(final Text in) { - return this.hasCustomInventoryName() ? new LiteralText(this.getInventoryName()) : in; - } - - private boolean hasCustomInventoryName() { - return this.handler.getCustomName() != null; - } - - private String getInventoryName() { - return this.handler.getCustomName(); + protected ITextComponent getGuiDisplayName(final Text in) { + return title.getString().isEmpty() ? in : title; } /** @@ -824,6 +823,14 @@ public abstract class AEBaseScreen extends HandledScr } } + /** + * Returns true while the current event being handled is a click of the right + * mouse button. + */ + public boolean isHandlingRightClick() { + return handlingRightClick; + } + public List getExclusionZones() { return Lists.newArrayList(new Rectangle(x, y, backgroundWidth, backgroundHeight)); } diff --git a/src/main/java/appeng/client/gui/implementations/CraftAmountScreen.java b/src/main/java/appeng/client/gui/implementations/CraftAmountScreen.java index e0e6abaa1..d53746444 100644 --- a/src/main/java/appeng/client/gui/implementations/CraftAmountScreen.java +++ b/src/main/java/appeng/client/gui/implementations/CraftAmountScreen.java @@ -46,22 +46,28 @@ public class CraftAmountScreen extends AEBaseScreen { public void init() { super.init(); - this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT, value -> { - }); + this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT); this.amountToCraft.setValue(1); this.amountToCraft.setTextFieldBounds(62, 57, 50); this.amountToCraft.setMinValue(1); + this.amountToCraft.setHideValidationIcon(true); this.amountToCraft.addButtons(children::add, this::addButton); this.next = this .addButton(new ButtonWidget(this.x + 128, this.y + 51, 38, 20, GuiText.Next.text(), this::confirm)); + this.amountToCraft.setOnConfirm(() -> this.confirm(this.next)); subGui.addBackButton(this::addButton, 154, 0); + + changeFocus(true); } private void confirm(ButtonWidget button) { - NetworkHandler.instance() - .sendToServer(new CraftRequestPacket((int) this.amountToCraft.getValue(), hasShiftDown())); + int amount = this.amountToCraft.getIntValue().orElse(0); + if (amount <= 0) { + return; + } + NetworkHandler.instance().sendToServer(new CraftRequestPacket(amount, hasShiftDown())); } @Override @@ -77,23 +83,9 @@ public class CraftAmountScreen extends AEBaseScreen { this.bindTexture("guis/craft_amt.png"); drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight); - this.next.active = this.amountToCraft.getValue() > 0; + this.next.active = this.amountToCraft.getIntValue().orElse(0) > 0; this.amountToCraft.render(matrices, offsetX, offsetY, partialTicks); } - @Override - public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) { - if (keyCode == 28) { - this.next.onPress(); - return true; - } else { - return super.keyPressed(keyCode, scanCode, p_keyPressed_3_); - } - } - - protected String getBackground() { - return "guis/craftAmt.png"; - } - } diff --git a/src/main/java/appeng/client/gui/implementations/CraftConfirmScreen.java b/src/main/java/appeng/client/gui/implementations/CraftConfirmScreen.java index 62086e91e..b965c91bc 100644 --- a/src/main/java/appeng/client/gui/implementations/CraftConfirmScreen.java +++ b/src/main/java/appeng/client/gui/implementations/CraftConfirmScreen.java @@ -25,6 +25,8 @@ import java.util.List; import com.mojang.blaze3d.systems.RenderSystem; +import org.lwjgl.glfw.GLFW; + import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerInventory; @@ -73,10 +75,6 @@ public class CraftConfirmScreen extends AEBaseScreen { this.setScrollBar(scrollbar); } - boolean isAutoStart() { - return this.handler.isAutoStart(); - } - @Override public void init() { super.init(); @@ -87,7 +85,7 @@ public class CraftConfirmScreen extends AEBaseScreen { this.addButton(this.start); this.selectCPU = new ButtonWidget(this.x + (219 - 180) / 2, this.y + this.backgroundHeight - 68, 180, 20, - GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text()), btn -> selectNextCpu()); + getNextCpuButtonLabel(), btn -> selectNextCpu()); this.selectCPU.active = false; this.addButton(this.selectCPU); @@ -135,22 +133,22 @@ public class CraftConfirmScreen extends AEBaseScreen { } private void updateCPUButtonText() { - Text btnTextText = GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text()); - if (this.handler.getSelectedCpu() >= 0)// && status.selectedCpu < status.cpus.size() ) - { - if (this.handler.getName() != null) { - final String name = this.handler.getName().asTruncatedString(20); - btnTextText = GuiText.CraftingCPU.withSuffix(": " + name); - } else { - btnTextText = GuiText.CraftingCPU.withSuffix(": #" + this.handler.getSelectedCpu()); - } + this.selectCPU.setMessage(getNextCpuButtonLabel()); + } + + private ITextComponent getNextCpuButtonLabel() { + if (this.container.hasNoCPU()) { + return GuiText.NoCraftingCPUs.text(); } - if (this.handler.hasNoCPU()) { - btnTextText = GuiText.NoCraftingCPUs.text(); + ITextComponent cpuName; + if (this.container.cpuName == null) { + cpuName = GuiText.Automatic.text(); + } else { + cpuName = this.container.cpuName; } - this.selectCPU.setMessage(btnTextText); + return GuiText.CraftingCPU.withSuffix(": ").append(cpuName); } private boolean isSimulation() { @@ -442,7 +440,7 @@ public class CraftConfirmScreen extends AEBaseScreen { @Override public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) { if (!this.checkHotbarKeys(keyCode, scanCode)) { - if (keyCode == 28) { + if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) { this.start(); return true; } @@ -451,7 +449,7 @@ public class CraftConfirmScreen extends AEBaseScreen { } private void selectNextCpu() { - final boolean backwards = getClient().mouse.wasRightButtonClicked(); + final boolean backwards = isHandlingRightClick(); NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next")); } diff --git a/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java b/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java index 042980ba0..028b2e113 100644 --- a/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java +++ b/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java @@ -44,12 +44,12 @@ public class CraftingStatusScreen extends CraftingCPUScreen selectNextCpu()); + getNextCpuButtonLabel(), btn -> selectNextCpu()); this.addButton(this.selectCPU); subGui.addBackButton(btn -> { addButton(btn); - btn.setHideEdge(13); + btn.setHideEdge(true); }, 213, -4); } @@ -60,23 +60,14 @@ public class CraftingStatusScreen extends CraftingCPUScreen= 0)// && status.selectedCpu < status.cpus.size() ) - { - if (this.handler.myName != null) { - final String name = this.handler.myName.asTruncatedString(20); - btnTextText = GuiText.CPUs.withSuffix(": " + name); - } else { - btnTextText = GuiText.CPUs.withSuffix(": #" + this.handler.selectedCpu); - } - } + this.selectCPU.setMessage(getNextCpuButtonLabel()); + } + private ITextComponent getNextCpuButtonLabel() { if (this.handler.noCPU) { - btnTextText = GuiText.NoCraftingJobs.text(); + return GuiText.NoCraftingJobs.text(); } - - this.selectCPU.setMessage(btnTextText); + return GuiText.CraftingCPU.withSuffix(": ").append(container.cpuName); } @Override @@ -84,9 +75,8 @@ public class CraftingStatusScreen extends CraftingCPUScreen { } private void selectNextInterfaceMode() { - final boolean backwards = getClient().mouse.wasRightButtonClicked(); + final boolean backwards = isHandlingRightClick(); NetworkHandler.instance().sendToServer(new ConfigButtonPacket(Settings.INTERFACE_TERMINAL, backwards)); } diff --git a/src/main/java/appeng/client/gui/implementations/LevelEmitterScreen.java b/src/main/java/appeng/client/gui/implementations/LevelEmitterScreen.java index f9cb89255..b066e67c2 100644 --- a/src/main/java/appeng/client/gui/implementations/LevelEmitterScreen.java +++ b/src/main/java/appeng/client/gui/implementations/LevelEmitterScreen.java @@ -34,8 +34,6 @@ import appeng.client.gui.widgets.ServerSettingToggleButton; import appeng.client.gui.widgets.SettingToggleButton; import appeng.container.implementations.LevelEmitterContainer; import appeng.core.localization.GuiText; -import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.ConfigValuePacket; public class LevelEmitterScreen extends UpgradeableScreen { @@ -51,15 +49,18 @@ public class LevelEmitterScreen extends UpgradeableScreen public void init() { super.init(); - this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT, - this::onLevelChange); + this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT); this.level.setTextFieldBounds(25, 44, 75); this.level.addButtons(children::add, this::addButton); - handler.setTextField(this.level); + this.level.setValue(container.getReportingValue()); + this.level.setOnChange(this::saveReportingValue); + this.level.setOnConfirm(this::closeScreen); + + this.changeFocus(true); } - private void onLevelChange(long level) { - NetworkHandler.instance().sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(level))); + private void saveReportingValue() { + this.level.getLongValue().ifPresent(container::setReportingValue); } @Override diff --git a/src/main/java/appeng/client/gui/implementations/MEMonitorableScreen.java b/src/main/java/appeng/client/gui/implementations/MEMonitorableScreen.java index e19615d42..60b8e0ff7 100644 --- a/src/main/java/appeng/client/gui/implementations/MEMonitorableScreen.java +++ b/src/main/java/appeng/client/gui/implementations/MEMonitorableScreen.java @@ -51,6 +51,7 @@ import appeng.client.gui.widgets.SettingToggleButton; import appeng.client.gui.widgets.TabButton; import appeng.client.me.InternalSlotME; import appeng.client.me.ItemRepo; +import appeng.client.render.StackSizeRenderer; import appeng.container.implementations.CraftingStatusContainer; import appeng.container.implementations.MEMonitorableContainer; import appeng.container.slot.AppEngSlot; @@ -237,7 +238,7 @@ public class MEMonitorableScreen extends AEBas if (this.viewCell || this instanceof WirelessTermScreen) { this.craftingStatusBtn = this.addButton(new TabButton(this.x + 170, this.y - 4, 2 + 11 * 16, GuiText.CraftingStatus.text(), this.itemRenderer, btn -> showCraftingStatus())); - this.craftingStatusBtn.setHideEdge(13); + this.craftingStatusBtn.setHideEdge(true); } this.isAutoFocus = SearchBoxMode.AUTOSEARCH == searchMode || SearchBoxMode.JEI_AUTOSEARCH == searchMode @@ -294,6 +295,16 @@ public class MEMonitorableScreen extends AEBas this.currentMouseX = mouseX; this.currentMouseY = mouseY; + + // Show the number of active crafting jobs + if (this.craftingStatusBtn != null && container.activeCraftingJobs != -1) { + // The stack size renderer expects a 16x16 slot, while the button is normally + // bigger + int x = this.craftingStatusBtn.x + (this.craftingStatusBtn.getWidth() - 16) / 2; + int y = this.craftingStatusBtn.y + (this.craftingStatusBtn.getHeightRealms() - 16) / 2; + StackSizeRenderer.renderSizeLabel(font, x - this.guiLeft, y - this.guiTop, + String.valueOf(container.activeCraftingJobs)); + } } @Override @@ -358,6 +369,7 @@ public class MEMonitorableScreen extends AEBas if (this.searchField != null) { this.searchField.render(matrices, mouseX, mouseY, partialTicks); } + } protected String getBackground() { diff --git a/src/main/java/appeng/client/gui/implementations/NumberEntryWidget.java b/src/main/java/appeng/client/gui/implementations/NumberEntryWidget.java index 70806142b..53fa3e271 100644 --- a/src/main/java/appeng/client/gui/implementations/NumberEntryWidget.java +++ b/src/main/java/appeng/client/gui/implementations/NumberEntryWidget.java @@ -2,70 +2,112 @@ package appeng.client.gui.implementations; import java.util.ArrayList; import java.util.List; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.function.Consumer; -import java.util.function.LongConsumer; +import net.minecraft.client.gui.AbstractGui; import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.Element; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.Text; +import net.minecraft.util.text.TranslationTextComponent; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.NumberEntryType; -import appeng.client.gui.widgets.ITickingWidget; -import appeng.client.gui.widgets.NumberBox; +import appeng.client.gui.widgets.ConfirmableTextField; +import appeng.client.gui.widgets.ValidationIcon; import appeng.core.AEConfig; /** * A utility widget that consists of a text-field to enter a number with * attached buttons to increment/decrement the number in fixed intervals. */ -public class NumberEntryWidget implements ITickingWidget { +public class NumberEntryWidget extends DrawableHelper { + private static final ITextComponent INVALID_NUMBER = new TranslationTextComponent( + "gui.appliedenergistics2.validation.InvalidNumber"); + private static final String NUMBER_LESS_THAN_MIN_VALUE = "gui.appliedenergistics2.validation.NumberLessThanMinValue"; private static final Text PLUS = Text.of("+"); private static final Text MINUS = Text.of("-"); + private static final int TEXT_COLOR_ERROR = 0xFF1900; + private static final int TEXT_COLOR_NORMAL = 0xFFFFFF; private final AEBaseScreen parent; private final int x; private final int y; - private final NumberBox level; + private final ConfirmableTextField textField; private final NumberEntryType type; private List buttons; + private long minValue; + private ValidationIcon validationIcon; - public NumberEntryWidget(AEBaseScreen parent, int x, int y, int width, int height, NumberEntryType type, - LongConsumer changeListener) { + // Called when the value changes + private Runnable onChange; + + // Called when the user presses enter while there's a valid number in the field + private Runnable onConfirm; + + private boolean hideValidationIcon; + + public NumberEntryWidget(AEBaseScreen parent, int x, int y, int width, int height, NumberEntryType type) { this.parent = parent; this.x = x; this.y = y; this.type = type; - TextRenderer font = parent.getClient().textRenderer; - int inputX = parent.getX() + x; - int inputY = parent.getY() + y; - this.level = new NumberBox(font, inputX, inputY, width, font.fontHeight, type.getInputType(), changeListener); - this.level.setHasBorder(false); - this.level.setMaxLength(16); - this.level.setEditableColor(0xFFFFFF); - this.level.setVisible(true); - parent.setInitialFocus(this.level); + FontRenderer font = parent.getMinecraft().fontRenderer; + int inputX = parent.getGuiLeft() + x; + int inputY = parent.getGuiTop() + y; + this.textField = new ConfirmableTextField(font, inputX, inputY, width, font.FONT_HEIGHT, + StringTextComponent.EMPTY); + this.textField.setEnableBackgroundDrawing(false); + this.textField.setMaxStringLength(16); + this.textField.setTextColor(TEXT_COLOR_NORMAL); + this.textField.setVisible(true); + this.textField.setFocused2(true); + parent.setFocusedDefault(this.textField); + this.textField.setResponder(text -> { + validate(); + if (onChange != null) { + this.onChange.run(); + } + }); + this.textField.setOnConfirm(() -> { + // Only confirm if it's actually valid + if (this.onConfirm != null && getLongValue().isPresent()) { + this.onConfirm.run(); + } + }); + validate(); + } + + public void setOnConfirm(Runnable callback) { + this.onConfirm = callback; + } + + public void setOnChange(Runnable callback) { + this.onChange = callback; } public void setActive(boolean active) { - this.level.active = active; + this.textField.active = active; this.buttons.forEach(b -> b.active = active); } public void setTextFieldBounds(int x, int y, int width) { - this.level.x = parent.getX() + x; - this.level.y = parent.getY() + y; - this.level.setWidth(width); + this.textField.x = parent.getX() + x; + this.textField.y = parent.getY() + y; + this.textField.setWidth(width); } public void setMinValue(long minValue) { - this.level.setMinValue(minValue); + this.minValue = minValue; + validate(); } public void addButtons(Consumer addChildren, Consumer addButton) { @@ -78,54 +120,116 @@ public class NumberEntryWidget implements ITickingWidget { int left = parent.getX() + x; int top = parent.getY() + y; - List buttons = new ArrayList<>(8); + List buttons = new ArrayList<>(9); buttons.add(new ButtonWidget(left, top, 22, 20, makeLabel(PLUS, a), btn -> addQty(a))); buttons.add(new ButtonWidget(left + 28, top, 28, 20, makeLabel(PLUS, b), btn -> addQty(b))); buttons.add(new ButtonWidget(left + 62, top, 32, 20, makeLabel(PLUS, c), btn -> addQty(c))); buttons.add(new ButtonWidget(left + 100, top, 38, 20, makeLabel(PLUS, d), btn -> addQty(d))); + // Need to add these now for sensible tab-order + buttons.forEach(addButton); + // Placing this here will give a sensible tab order - addChildren.accept(this.level); + addChildren.accept(this.textField); buttons.add(new ButtonWidget(left, top + 42, 22, 20, makeLabel(MINUS, a), btn -> addQty(-a))); buttons.add(new ButtonWidget(left + 28, top + 42, 28, 20, makeLabel(MINUS, b), btn -> addQty(-b))); buttons.add(new ButtonWidget(left + 62, top + 42, 32, 20, makeLabel(MINUS, c), btn -> addQty(-c))); buttons.add(new ButtonWidget(left + 100, top + 42, 38, 20, makeLabel(MINUS, d), btn -> addQty(-d))); + // This element is not focusable + if (!hideValidationIcon) { + this.validationIcon = new ValidationIcon(left + 104, top + 27); + buttons.add(this.validationIcon); + } + + // Add the rest to the tab order + buttons.subList(4, buttons.size()).forEach(addButton); + this.buttons = buttons; - this.buttons.forEach(addButton); + + // we need to re-validate because the icon may now be present and needs it's + // initial state + this.validate(); } - private void addQty(final long i) { - long currentValue = this.level.getValue(); - long minValue = this.level.getMinValue(); - this.level.setText(String.valueOf(Math.max(minValue, currentValue + i))); + /** + * Returns the integer value currently in the text-field, if it is a valid + * number and is within the allowed min/max value. + */ + public OptionalInt getIntValue() { + String text = textField.getText().trim(); + try { + int value = Integer.parseInt(text, 10); + if (value < minValue) { + return OptionalInt.empty(); + } + return OptionalInt.of(value); + } catch (NumberFormatException ignored) { + return OptionalInt.empty(); + } } - public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) { - this.level.render(matrices, mouseX, mouseY, partialTicks); - } - - public void setValue(long value, boolean skipNotify) { - this.level.setValue(value, skipNotify); + /** + * Returns the long value currently in the text-field, if it is a valid number + * and is within the allowed min/max value. + */ + public OptionalLong getLongValue() { + String text = textField.getText().trim(); + try { + long value = Long.parseLong(text, 10); + if (value < minValue) { + return OptionalLong.empty(); + } + return OptionalLong.of(value); + } catch (NumberFormatException ignored) { + return OptionalLong.empty(); + } } public void setValue(long value) { - setValue(value, false); + this.textField.setText(String.valueOf(Math.max(minValue, value))); + this.textField.setCursorPositionEnd(); + this.textField.setSelectionPos(0); + validate(); } - public long getValue() { - return level.getValue(); + private void addQty(final long i) { + getLongValue().ifPresent(currentValue -> setValue(currentValue + i)); } - @Override - public void tick() { - this.level.tick(); + public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) { + this.textField.render(matrices, mouseX, mouseY, partialTicks); + } + + private void validate() { + List validationErrors = new ArrayList<>(); + + String text = textField.getText().trim(); + try { + long value = Long.parseLong(text, 10); + if (value < minValue) { + validationErrors.add(new TranslationTextComponent(NUMBER_LESS_THAN_MIN_VALUE, minValue)); + } + } catch (NumberFormatException ignored) { + validationErrors.add(INVALID_NUMBER); + } + + boolean valid = validationErrors.isEmpty(); + this.textField.setTextColor(valid ? TEXT_COLOR_NORMAL : TEXT_COLOR_ERROR); + if (this.validationIcon != null) { + this.validationIcon.setValid(valid); + this.validationIcon.setTooltip(validationErrors); + } } private Text makeLabel(Text prefix, int amount) { return prefix.copy().append(String.valueOf(amount)); } + public void setHideValidationIcon(boolean hideValidationIcon) { + this.hideValidationIcon = hideValidationIcon; + } + } diff --git a/src/main/java/appeng/client/gui/implementations/PriorityScreen.java b/src/main/java/appeng/client/gui/implementations/PriorityScreen.java index a28220cc4..40d20530d 100644 --- a/src/main/java/appeng/client/gui/implementations/PriorityScreen.java +++ b/src/main/java/appeng/client/gui/implementations/PriorityScreen.java @@ -18,6 +18,8 @@ package appeng.client.gui.implementations; +import java.util.OptionalInt; + import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.text.Text; @@ -26,8 +28,6 @@ import appeng.client.gui.AEBaseScreen; import appeng.client.gui.NumberEntryType; import appeng.container.implementations.PriorityContainer; import appeng.core.localization.GuiText; -import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.ConfigValuePacket; public class PriorityScreen extends AEBaseScreen { @@ -38,24 +38,38 @@ public class PriorityScreen extends AEBaseScreen { public PriorityScreen(PriorityContainer container, PlayerInventory playerInventory, Text title) { super(container, playerInventory, title); this.subGui = new AESubScreen(this, container.getPriorityHost()); + + // This is the effective size of the background image + xSize = 175; + ySize = 128; } @Override public void init() { super.init(); - this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY, this::onPriorityChange); + this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY); this.priority.setTextFieldBounds(62, 57, 50); this.priority.setMinValue(Integer.MIN_VALUE); - handler.setTextField(this.priority); + this.priority.setValue(this.container.getPriorityValue()); this.priority.addButtons(children::add, this::addButton); this.subGui.addBackButton(this::addButton, 154, 0); + + this.priority.setOnChange(this::savePriority); + this.priority.setOnConfirm(() -> { + savePriority(); + this.subGui.goBack(); + }); + + changeFocus(true); } - private void onPriorityChange(long priority) { - NetworkHandler.instance() - .sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(priority))); + private void savePriority() { + OptionalInt priority = this.priority.getIntValue(); + if (priority.isPresent()) { + container.setPriority(priority.getAsInt()); + } } @Override @@ -66,13 +80,10 @@ public class PriorityScreen extends AEBaseScreen { @Override public void drawBG(MatrixStack matrices, final int offsetX, final int offsetY, final int mouseX, final int mouseY, float partialTicks) { - this.bindTexture(getBackground()); + this.bindTexture("guis/priority.png"); drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight); this.priority.render(matrices, mouseX, mouseY, partialTicks); } - protected String getBackground() { - return "guis/priority.png"; - } } diff --git a/src/main/java/appeng/client/gui/widgets/ConfirmableTextField.java b/src/main/java/appeng/client/gui/widgets/ConfirmableTextField.java new file mode 100644 index 000000000..d8c202bba --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/ConfirmableTextField.java @@ -0,0 +1,51 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2015, 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 . + */ + +package appeng.client.gui.widgets; + +import org.lwjgl.glfw.GLFW; + +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.util.text.ITextComponent; + +public class ConfirmableTextField extends TextFieldWidget { + + private Runnable onConfirm; + + public ConfirmableTextField(FontRenderer fontRenderer, int x, int y, int width, int height, ITextComponent text) { + super(fontRenderer, x, y, width, height, text); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (canWrite() && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER)) { + if (this.onConfirm != null) { + this.onConfirm.run(); + } + return true; + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + public void setOnConfirm(Runnable onConfirm) { + this.onConfirm = onConfirm; + } + +} diff --git a/src/main/java/appeng/client/gui/widgets/IconButton.java b/src/main/java/appeng/client/gui/widgets/IconButton.java index f3e620dd6..01fb550b0 100644 --- a/src/main/java/appeng/client/gui/widgets/IconButton.java +++ b/src/main/java/appeng/client/gui/widgets/IconButton.java @@ -21,6 +21,7 @@ package appeng.client.gui.widgets; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.audio.SoundHandler; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.texture.TextureManager; import net.minecraft.client.util.math.MatrixStack; @@ -33,6 +34,10 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { private boolean halfSize = false; + private boolean disableClickSound = false; + + private boolean disableBackground = false; + public IconButton(final int x, final int y, PressAction onPress) { super(x, y, 16, 16, LiteralText.EMPTY, onPress); } @@ -42,6 +47,13 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { this.active = vis; } + @Override + public void playDownSound(SoundHandler soundHandler) { + if (!disableClickSound) { + super.playDownSound(soundHandler); + } + } + @Override public void renderButton(MatrixStack matrices, final int mouseX, final int mouseY, float partial) { @@ -72,7 +84,9 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { final int uv_y = iconIndex / 16; final int uv_x = iconIndex - uv_y * 16; - drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16); + if (!disableBackground) { + drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16); + } drawTexture(matrices, 0, 0, uv_x * 16, uv_y * 16, 16, 16); RenderSystem.popMatrix(); } else { @@ -85,12 +99,18 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { final int uv_y = iconIndex / 16; final int uv_x = iconIndex - uv_y * 16; - drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16); + if (!disableBackground) { + drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16); + } drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 16, 16); } RenderSystem.enableDepthTest(); + RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f); + + if (isHovered()) { + renderToolTip(matrixStack, mouseX, mouseY); + } } - RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f); } protected abstract int getIconIndex(); @@ -133,4 +153,20 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { this.halfSize = halfSize; } + public boolean isDisableClickSound() { + return disableClickSound; + } + + public void setDisableClickSound(boolean disableClickSound) { + this.disableClickSound = disableClickSound; + } + + public boolean isDisableBackground() { + return disableBackground; + } + + public void setDisableBackground(boolean disableBackground) { + this.disableBackground = disableBackground; + } + } diff --git a/src/main/java/appeng/client/gui/widgets/NumberBox.java b/src/main/java/appeng/client/gui/widgets/NumberBox.java deleted file mode 100644 index 82b63565f..000000000 --- a/src/main/java/appeng/client/gui/widgets/NumberBox.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * This file is part of Applied Energistics 2. - * Copyright (c) 2013 - 2015, 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 . - */ - -package appeng.client.gui.widgets; - -import java.util.function.LongConsumer; - -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.text.LiteralText; - -public class NumberBox extends TextFieldWidget { - - private final LongConsumer changeListener; - - private long lastValue; - - private long minValue = 0; - - private long maxValue; - - public NumberBox(final TextRenderer fontRenderer, final int x, final int y, final int width, final int height, - final Class type, LongConsumer changeListener) { - super(fontRenderer, x, y, width, height, new LiteralText("0")); - this.setText("0"); - setChangedListener(this::handleTextChanged); - this.lastValue = 0; - this.changeListener = changeListener; - if (type == int.class || type == Integer.class) { - maxValue = Integer.MAX_VALUE; - } else { - maxValue = Long.MAX_VALUE; - } - } - - // NOTE: This method MUST NOT BE NAMED like a method in the superclass because - // of a ForgeGradle remapping bug - // since it'll not remap this method's name, but it will remap the name of the - // method reference above. - private void handleTextChanged(String text) { - if (text.isEmpty()) { - setText("0"); // Will call onTextChanged recursively - return; - } - - boolean canBeNegative = canBeNegative(); - if (canBeNegative && text.equals("-")) { - // Allow this as a special case to make typing in a negative number easier - return; - } - - StringBuilder sanitized = new StringBuilder(text); - boolean encounteredNonZero = false; - for (int i = 0; i < sanitized.length(); i++) { - char ch = sanitized.charAt(i); - if (canBeNegative && i == 0 && ch == '-') { - continue; // Allow leading minus sign - } - if (ch >= '1' && ch <= '9') { - encounteredNonZero = true; - continue; - } - if (ch != '0' || !encounteredNonZero) { - sanitized.deleteCharAt(i--); - } - } - if (sanitized.length() == 0) { - sanitized.append('0'); - } - String sanitizedStr = sanitized.toString(); - if (!sanitizedStr.equals(text)) { - setText(sanitizedStr); // Will call onTextChanged recursively - return; - } - if (getValue() < minValue) { - setText(String.valueOf(minValue)); // Will call onTextChanged recursively - return; - } - if (getValue() > maxValue) { - setText(String.valueOf(maxValue)); // Will call onTextChanged recursively - return; - } - - reportChange(); - } - - private void reportChange() { - long value = getValue(); - if (value != lastValue) { - lastValue = value; - changeListener.accept(value); - } - } - - public void setValue(long value, boolean skipNotify) { - // This check avoid changing the cursor position needlessly - if (value == this.getValue()) { - return; - } - - if (skipNotify) { - lastValue = value; - } - setText(String.valueOf(value)); - } - - public long getValue() { - if (getText().equals("-") && canBeNegative()) { - return lastValue; // Allow this as a special case to type in a negative number more easily - } - - return Long.parseLong(getText()); - } - - @Override - public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) { - if (super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_)) { - return true; - } - - // Swallow key presses for numbers because they would otherwise trigger the - // hotbar swapping unintentionally - return isFocused() && p_keyPressed_1_ >= '0' && p_keyPressed_1_ <= '9'; - } - - public void setMinValue(long minValue) { - this.minValue = minValue; - } - - public long getMinValue() { - return minValue; - } - - private boolean canBeNegative() { - return minValue < 0; - } - -} diff --git a/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java b/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java index c06cecda6..e9cd9ff17 100644 --- a/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java +++ b/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java @@ -25,6 +25,7 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; @@ -47,6 +48,7 @@ import appeng.api.config.StorageFilter; import appeng.api.config.TerminalStyle; import appeng.api.config.ViewItems; import appeng.api.config.YesNo; +import appeng.client.gui.AEBaseScreen; import appeng.core.localization.ButtonToolTips; import appeng.util.EnumCycler; @@ -89,9 +91,11 @@ public class SettingToggleButton> extends IconButton { registerApp(16 * 7, Settings.CONDENSER_OUTPUT, CondenserOutput.TRASH, ButtonToolTips.CondenserOutput, ButtonToolTips.Trash); registerApp(16 * 7 + 1, Settings.CONDENSER_OUTPUT, CondenserOutput.MATTER_BALLS, - ButtonToolTips.CondenserOutput, ButtonToolTips.MatterBalls); + ButtonToolTips.CondenserOutput, + ButtonToolTips.MatterBalls.text(CondenserOutput.MATTER_BALLS.requiredPower)); registerApp(16 * 7 + 2, Settings.CONDENSER_OUTPUT, CondenserOutput.SINGULARITY, - ButtonToolTips.CondenserOutput, ButtonToolTips.Singularity); + ButtonToolTips.CondenserOutput, + ButtonToolTips.Singularity.text(CondenserOutput.SINGULARITY.requiredPower)); registerApp(16 * 9 + 1, Settings.ACCESS, AccessRestriction.READ, ButtonToolTips.IOMode, ButtonToolTips.Read); @@ -230,7 +234,13 @@ public class SettingToggleButton> extends IconButton { } private void triggerPress() { - boolean backwards = MinecraftClient.getInstance().mouse.wasRightButtonClicked(); + boolean backwards = false; + // This isn't great, but we don't get any information about right-clicks + // otherwise + Screen currentScreen = MinecraftClient.getInstance().currentScreen; + if (currentScreen instanceof AEBaseScreen) { + backwards = ((AEBaseScreen) currentScreen).isHandlingRightClick(); + } onPress.handle(this, backwards); } diff --git a/src/main/java/appeng/client/gui/widgets/TabButton.java b/src/main/java/appeng/client/gui/widgets/TabButton.java index e487d799b..d1cdf51e5 100644 --- a/src/main/java/appeng/client/gui/widgets/TabButton.java +++ b/src/main/java/appeng/client/gui/widgets/TabButton.java @@ -31,7 +31,7 @@ import net.minecraft.util.Identifier; public class TabButton extends ButtonWidget implements ITooltip { public static final Identifier TEXTURE_STATES = new Identifier("appliedenergistics2", "textures/guis/states.png"); private final ItemRenderer itemRenderer; - private int hideEdge = 0; + private boolean hideEdge; private int myIcon = -1; private ItemStack myItem; @@ -69,14 +69,17 @@ public class TabButton extends ButtonWidget implements ITooltip { RenderSystem.enableAlphaTest(); - int uv_x = (this.hideEdge > 0 ? 11 : 13); + // Selects the button border from the sprite-sheet, where each type occupies a + // 2x2 slot + int uv_x = 8 + (this.hideEdge ? 0 : 2); + int uv_y = 12 + (this.isFocused() ? 2 : 0); - final int offsetX = this.hideEdge > 0 ? 1 : 0; + final int offsetX = this.hideEdge ? 1 : 0; - drawTexture(matrices, this.x, this.y, uv_x * 16, 0, 25, 22); + drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 25, 22); if (this.myIcon >= 0) { - final int uv_y = this.myIcon / 16; + uv_y = this.myIcon / 16; uv_x = this.myIcon - uv_y * 16; drawTexture(matrices, offsetX + this.x + 3, this.y + 3, uv_x * 16, uv_y * 16, 16, 16); @@ -122,11 +125,11 @@ public class TabButton extends ButtonWidget implements ITooltip { return this.visible; } - public int getHideEdge() { + public boolean getHideEdge() { return this.hideEdge; } - public void setHideEdge(final int hideEdge) { + public void setHideEdge(final boolean hideEdge) { this.hideEdge = hideEdge; } } diff --git a/src/main/java/appeng/client/gui/widgets/ValidationIcon.java b/src/main/java/appeng/client/gui/widgets/ValidationIcon.java new file mode 100644 index 000000000..4078ac964 --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/ValidationIcon.java @@ -0,0 +1,65 @@ +package appeng.client.gui.widgets; + +import java.util.ArrayList; +import java.util.List; + +import com.mojang.blaze3d.matrix.MatrixStack; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.util.text.ITextComponent; + +/** + * Displays a small icon that shows validation errors for some input control. + */ +public class ValidationIcon extends IconButton { + + private final List tooltip = new ArrayList<>(); + + private boolean valid; + + public ValidationIcon(int x, int y) { + super(x, y, btn -> { + }); + setDisableBackground(true); + setDisableClickSound(true); + setHalfSize(true); + } + + public void setValid(boolean valid) { + setVisibility(!valid); + if (valid) { + this.tooltip.clear(); + } + } + + public void setTooltip(List lines) { + this.tooltip.clear(); + this.tooltip.addAll(lines); + } + + @Override + protected int getIconIndex() { + return 16 * 8; + } + + @Override + public boolean changeFocus(boolean flag) { + return false; // Cannot focus this element + } + + @Override + public void renderToolTip(MatrixStack matrices, int mouseX, int mouseY) { + if (this.tooltip.isEmpty()) { + return; + } + + Minecraft client = Minecraft.getInstance(); + Screen screen = client.currentScreen; + if (screen == null) { + return; + } + + screen.func_243308_b(matrices, this.tooltip, x, y); + } +} diff --git a/src/main/java/appeng/container/AEBaseContainer.java b/src/main/java/appeng/container/AEBaseContainer.java index 8eff993a1..64a924e22 100644 --- a/src/main/java/appeng/container/AEBaseContainer.java +++ b/src/main/java/appeng/container/AEBaseContainer.java @@ -66,10 +66,8 @@ import appeng.container.slot.PlayerInvSlot; import appeng.core.AELog; import appeng.core.Api; import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.ConfigValuePacket; import appeng.core.sync.packets.InventoryActionPacket; import appeng.core.sync.packets.TargetItemStackPacket; -import appeng.helpers.ICustomNameObject; import appeng.helpers.InventoryAction; import appeng.me.helpers.PlayerSource; import appeng.mixins.ScreenHandlerListeners; @@ -88,11 +86,9 @@ public abstract class AEBaseContainer extends ScreenHandler { private final IGuiItemObject obj; private final HashMap syncData = new HashMap<>(); private boolean isContainerValid = true; - private String customName; private ContainerLocator locator; private IMEInventoryHandler cellInv; private IEnergySource powerSrc; - private boolean sentCustomName; private int ticksSinceCheck = 900; private IAEItemStack clientRequestedTargetItem = null; @@ -317,9 +313,7 @@ public abstract class AEBaseContainer extends ScreenHandler { @Override public void sendContentUpdates() { - this.sendCustomName(); - - if (Platform.isServer()) { + if (isServer()) { if (this.tileEntity != null && this.tileEntity.getWorld().getBlockEntity(this.tileEntity.getPos()) != this.tileEntity) { this.setValidContainer(false); @@ -886,43 +880,6 @@ public abstract class AEBaseContainer extends ScreenHandler { this.sendContentUpdates(); } - private void sendCustomName() { - // FIXME: Trash this, this is handled by NamedContainerProvider now - if (!this.sentCustomName) { - this.sentCustomName = true; - if (Platform.isServer()) { - ICustomNameObject name = null; - - if (this.part instanceof ICustomNameObject) { - name = (ICustomNameObject) this.part; - } - - if (this.tileEntity instanceof ICustomNameObject) { - name = (ICustomNameObject) this.tileEntity; - } - - if (this.obj instanceof ICustomNameObject) { - name = (ICustomNameObject) this.obj; - } - - if (this instanceof ICustomNameObject) { - name = (ICustomNameObject) this; - } - - if (name != null) { - if (name.hasCustomInventoryName()) { - this.setCustomName(name.getCustomInventoryName().getString()); - } - - if (this.getCustomName() != null) { - NetworkHandler.instance().sendTo(new ConfigValuePacket("CustomName", this.getCustomName()), - (ServerPlayerEntity) this.getPlayerInventory().player); - } - } - } - } - } - public void swapSlotContents(final int slotA, final int slotB) { final Slot a = this.getSlot(slotA); final Slot b = this.getSlot(slotB); @@ -1012,14 +969,6 @@ public abstract class AEBaseContainer extends ScreenHandler { this.cellInv = cellInv; } - public String getCustomName() { - return this.customName; - } - - public void setCustomName(final String customName) { - this.customName = customName; - } - public PlayerInventory getPlayerInventory() { return this.invPlayer; } @@ -1048,4 +997,18 @@ public abstract class AEBaseContainer extends ScreenHandler { this.powerSrc = powerSrc; } + /** + * Returns whether this container instance lives on the client. + */ + protected boolean isClient() { + return invPlayer.player.getEntityWorld().isRemote(); + } + + /** + * Returns whether this container instance lives on the server. + */ + protected boolean isServer() { + return !isClient(); + } + } diff --git a/src/main/java/appeng/container/guisync/SyncData.java b/src/main/java/appeng/container/guisync/SyncData.java index 35feba6c6..0926fbfac 100644 --- a/src/main/java/appeng/container/guisync/SyncData.java +++ b/src/main/java/appeng/container/guisync/SyncData.java @@ -89,6 +89,8 @@ public class SyncData { NetworkHandler.instance().sendTo(new ConfigValuePacket("SyncDat." + this.channel, json), (ServerPlayerEntity) o); } + this.clientVersion = val; + return; } // Types other than Text must be non-null diff --git a/src/main/java/appeng/container/implementations/CellWorkbenchContainer.java b/src/main/java/appeng/container/implementations/CellWorkbenchContainer.java index 6dd9d12e5..6bb570716 100644 --- a/src/main/java/appeng/container/implementations/CellWorkbenchContainer.java +++ b/src/main/java/appeng/container/implementations/CellWorkbenchContainer.java @@ -50,7 +50,6 @@ import appeng.container.slot.RestrictedInputSlot; import appeng.core.Api; import appeng.tile.misc.CellWorkbenchBlockEntity; import appeng.util.EnumCycler; -import appeng.util.Platform; import appeng.util.helpers.ItemHandlerUtil; import appeng.util.inv.WrapperSupplierItemHandler; import appeng.util.iterators.NullIterator; @@ -143,7 +142,7 @@ public class CellWorkbenchContainer extends UpgradeableContainer { @Override public void sendContentUpdates() { final ItemStack is = this.workBench.getInventoryByName("cell").getInvStack(0); - if (Platform.isServer()) { + if (isServer()) { for (final ScreenHandlerListener listener : this.getListeners()) { if (this.prevStack != is) { // if the bars changed an item was probably made, so just send shit! diff --git a/src/main/java/appeng/container/implementations/CondenserContainer.java b/src/main/java/appeng/container/implementations/CondenserContainer.java index 7e0e1c909..6a25066fd 100644 --- a/src/main/java/appeng/container/implementations/CondenserContainer.java +++ b/src/main/java/appeng/container/implementations/CondenserContainer.java @@ -34,7 +34,6 @@ import appeng.container.interfaces.IProgressProvider; import appeng.container.slot.OutputSlot; import appeng.container.slot.RestrictedInputSlot; import appeng.tile.misc.CondenserBlockEntity; -import appeng.util.Platform; public class CondenserContainer extends AEBaseContainer implements IProgressProvider { @@ -76,13 +75,13 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv @Override public void sendContentUpdates() { - if (Platform.isServer()) { + if (isServer()) { final double maxStorage = this.condenser.getStorage(); final double requiredEnergy = this.condenser.getRequiredPower(); this.requiredEnergy = requiredEnergy == 0 ? (int) maxStorage : (int) Math.min(requiredEnergy, maxStorage); this.storedPower = (int) this.condenser.getStoredPower(); - this.setOutput((CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT)); + this.output = (CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT); } super.sendContentUpdates(); @@ -102,7 +101,4 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv return this.output; } - private void setOutput(final CondenserOutput output) { - this.output = output; - } } diff --git a/src/main/java/appeng/container/implementations/ContainerHelper.java b/src/main/java/appeng/container/implementations/ContainerHelper.java index c75482742..adf7877be 100644 --- a/src/main/java/appeng/container/implementations/ContainerHelper.java +++ b/src/main/java/appeng/container/implementations/ContainerHelper.java @@ -4,6 +4,8 @@ import javax.annotation.Nullable; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.block.entity.BlockEntity; +import java.util.function.Function; + import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; @@ -15,6 +17,9 @@ import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraftforge.fml.network.NetworkHooks; import appeng.api.config.SecurityPermissions; import appeng.api.features.IWirelessTermHandler; @@ -44,6 +49,8 @@ public final class ContainerHelper { private final SecurityPermissions requiredPermission; + private Function containerTitleStrategy = this::getDefaultContainerTitle; + public ContainerHelper(ContainerFactory factory, Class interfaceClass) { this(factory, interfaceClass, null); } @@ -55,19 +62,47 @@ public final class ContainerHelper { this.factory = factory; } + /** + * Specifies a custom strategy for obtaining a custom container name. + * + * The stratgy should return {@link StringTextComponent#EMPTY} if there's no + * custom name. + */ + public ContainerHelper withContainerTitle(Function containerTitleStrategy) { + this.containerTitleStrategy = containerTitleStrategy; + return this; + } + /** * Opens a container that is based around a single block entity. The tile * entity's position is encoded in the packet buffer. */ public C fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf packetBuf) { + return fromNetwork(windowId, inv, packetBuf, (accessObj, container, buffer) -> { + }); + } + + /** + * Same as {@link #open}, but allows or additional data to be read from the + * packet, and passed onto the container. + */ + public C fromNetwork(int windowId, PlayerInventory inv, PacketBuffer packetBuf, + InitialDataDeserializer initialDataDeserializer) { I host = getHostFromLocator(inv.player, ContainerLocator.read(packetBuf)); if (host != null) { - return factory.create(windowId, inv, host); + C container = factory.create(windowId, inv, host); + initialDataDeserializer.deserializeInitialData(host, container, packetBuf); + return container; } return null; } public boolean open(PlayerEntity player, ContainerLocator locator) { + return open(player, locator, (accessObj, buffer) -> { + }); + } + + public boolean open(PlayerEntity player, ContainerLocator locator, InitialDataSerializer initialDataSerializer) { if (!(player instanceof ServerPlayerEntity)) { // Cannot open containers on the client or for non-players // FIXME logging? @@ -84,7 +119,7 @@ public final class ContainerHelper { return false; } - Text title = findContainerTitle(player.world, locator, accessInterface); + Text title = containerTitleStrategy.apply(accessInterface); player.openHandledScreen(new HandlerFactory(locator, title, accessInterface)); @@ -108,6 +143,7 @@ public final class ContainerHelper { @Override public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) { locator.write(buf); + initialDataSerializer.serializeInitialData(accessInterface, buf); } @Override @@ -127,28 +163,6 @@ public final class ContainerHelper { } - private Text findContainerTitle(World world, ContainerLocator locator, I accessInterface) { - - if (accessInterface instanceof ICustomNameObject) { - ICustomNameObject customNameObject = (ICustomNameObject) accessInterface; - if (customNameObject.hasCustomInventoryName()) { - return customNameObject.getCustomInventoryName(); - } - } - - // Use block name at position - // FIXME: this is not right, we'd need to check the part's item stack, or custom - // naming interface impl - // FIXME: Should move this up, because at this point, it's hard to know where - // the terminal host came from (part or tile) - if (locator.hasBlockPos()) { - return new TranslatableText(world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey()); - } - - return new LiteralText("Unknown"); - - } - private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) { if (locator.hasItemIndex()) { return getHostFromPlayerInventory(player, locator); @@ -225,6 +239,24 @@ public final class ContainerHelper { C create(int windowId, PlayerInventory playerInv, I accessObj); } + /** + * Strategy used to serialize initial data for opening the container on the + * client-side into the packet that is sent to the client. + */ + @FunctionalInterface + public interface InitialDataSerializer { + void serializeInitialData(I host, PacketBuffer buffer); + } + + /** + * Strategy used to deserialize initial data for opening the container on the + * client-side from the packet received by the server. + */ + @FunctionalInterface + public interface InitialDataDeserializer { + void deserializeInitialData(I host, C container, PacketBuffer buffer); + } + private boolean checkPermission(PlayerEntity player, Object accessInterface) { if (requiredPermission != null) { @@ -235,4 +267,15 @@ public final class ContainerHelper { } + private Text getDefaultContainerTitle(I accessInterface) { + if (accessInterface instanceof ICustomNameObject) { + ICustomNameObject customNameObject = (ICustomNameObject) accessInterface; + if (customNameObject.hasCustomInventoryName()) { + return customNameObject.getCustomInventoryName(); + } + } + + return LiteralText.EMPTY; + } + } diff --git a/src/main/java/appeng/container/implementations/CraftConfirmContainer.java b/src/main/java/appeng/container/implementations/CraftConfirmContainer.java index e66d98fed..bfaee2421 100644 --- a/src/main/java/appeng/container/implementations/CraftConfirmContainer.java +++ b/src/main/java/appeng/container/implementations/CraftConfirmContainer.java @@ -19,14 +19,8 @@ package appeng.container.implementations; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.concurrent.Future; -import javax.annotation.Nullable; - -import com.google.common.collect.ImmutableSet; - import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.network.PacketByteBuf; @@ -66,9 +60,8 @@ import appeng.me.helpers.PlayerSource; import appeng.parts.reporting.CraftingTerminalPart; import appeng.parts.reporting.PatternTerminalPart; import appeng.parts.reporting.TerminalPart; -import appeng.util.Platform; -public class CraftConfirmContainer extends AEBaseContainer { +public class CraftConfirmContainer extends AEBaseContainer implements CraftingCPUCyclingContainer { public static ScreenHandlerType TYPE; @@ -83,98 +76,51 @@ public class CraftConfirmContainer extends AEBaseContainer { return helper.open(player, locator); } - private final ArrayList cpus = new ArrayList<>(); + private final CraftingCPUCycler cpuCycler; + + private ICraftingCPU selectedCpu; + private Future job; private ICraftingJob result; @GuiSync(0) public long bytesUsed; - @GuiSync(1) - public long cpuBytesAvail; - @GuiSync(2) - public int cpuCoProcessors; @GuiSync(3) public boolean autoStart = false; @GuiSync(4) public boolean simulation = true; - @GuiSync(5) - public int selectedCpu = -1; + + // Indicates whether any CPUs are available @GuiSync(6) public boolean noCPU = true; + + // Properties of the currently selected crafting CPU, this can be null + // if no CPUs are available, or if an automatic one is selected + @GuiSync(1) + public long cpuBytesAvail; + @GuiSync(2) + public int cpuCoProcessors; @GuiSync(7) - public Text myName; + public Text cpuName; public CraftConfirmContainer(int id, PlayerInventory ip, ITerminalHost te) { super(TYPE, id, ip, te); + this.cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged); + // A player can select no crafting CPU to use a suitable one automatically + this.cpuCycler.setAllowNoSelection(true); } - public void cycleCpu(final boolean next) { - if (next) { - this.setSelectedCpu(this.getSelectedCpu() + 1); - } else { - this.setSelectedCpu(this.getSelectedCpu() - 1); - } - - if (this.getSelectedCpu() < -1) { - this.setSelectedCpu(this.cpus.size() - 1); - } else if (this.getSelectedCpu() >= this.cpus.size()) { - this.setSelectedCpu(-1); - } - - if (this.getSelectedCpu() == -1) { - this.setCpuAvailableBytes(0); - this.setCpuCoProcessors(0); - this.setName(null); - } else { - CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu()); - this.setName(cpu.getName()); - this.setCpuAvailableBytes(cpu.getSize()); - this.setCpuCoProcessors(cpu.getProcessors()); - } + @Override + public void cycleSelectedCPU(final boolean next) { + this.cpuCycler.cycleCpu(next); } @Override public void sendContentUpdates() { - if (Platform.isClient()) { + if (isClient()) { return; } - final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class); - final ImmutableSet cpuSet = cc.getCpus(); - - int matches = 0; - boolean changed = false; - for (final ICraftingCPU c : cpuSet) { - boolean found = false; - for (final CraftingCPURecord ccr : this.cpus) { - if (ccr.getCpu() == c) { - found = true; - break; - } - } - - final boolean matched = this.cpuMatches(c); - - if (matched) { - matches++; - } - - if (found == !matched) { - changed = true; - } - } - - if (changed || this.cpus.size() != matches) { - this.cpus.clear(); - for (final ICraftingCPU c : cpuSet) { - if (this.cpuMatches(c)) { - this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c)); - } - } - - this.sendCPUs(); - } - - this.setNoCPU(this.cpus.isEmpty()); + this.cpuCycler.detectAndSendChanges(this.getGrid()); super.sendContentUpdates(); @@ -277,22 +223,6 @@ public class CraftConfirmContainer extends AEBaseContainer { return c.getAvailableStorage() >= this.getUsedBytes() && !c.isBusy(); } - private void sendCPUs() { - Collections.sort(this.cpus); - - if (this.getSelectedCpu() >= this.cpus.size()) { - this.setSelectedCpu(-1); - this.setCpuAvailableBytes(0); - this.setCpuCoProcessors(0); - this.setName(null); - } else if (this.getSelectedCpu() != -1) { - CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu()); - this.setName(cpu.getName()); - this.setCpuAvailableBytes(cpu.getSize()); - this.setCpuCoProcessors(cpu.getProcessors()); - } - } - public void startJob() { ScreenHandlerType originalGui = null; @@ -315,9 +245,7 @@ public class CraftConfirmContainer extends AEBaseContainer { if (this.result != null && !this.isSimulation()) { final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class); - final ICraftingLink g = cc.submitJob(this.result, null, - this.getSelectedCpu() == -1 ? null : this.cpus.get(this.getSelectedCpu()).getCpu(), true, - this.getActionSrc()); + final ICraftingLink g = cc.submitJob(this.result, null, this.selectedCpu, true, this.getActionSrc()); this.setAutoStart(false); if (g != null && originalGui != null && this.getLocator() != null) { ContainerOpener.openContainer(originalGui, getPlayerInventory().player, getLocator()); @@ -347,6 +275,22 @@ public class CraftConfirmContainer extends AEBaseContainer { } } + private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) { + noCPU = !cpusAvailable; + + if (cpuRecord == null) { + cpuBytesAvail = 0; + cpuCoProcessors = 0; + cpuName = null; + selectedCpu = null; + } else { + cpuBytesAvail = cpuRecord.getSize(); + cpuCoProcessors = cpuRecord.getProcessors(); + cpuName = cpuRecord.getName(); + selectedCpu = cpuRecord.getCpu(); + } + } + public World getWorld() { return this.getPlayerInv().player.world; } @@ -371,42 +315,18 @@ public class CraftConfirmContainer extends AEBaseContainer { return this.cpuBytesAvail; } - private void setCpuAvailableBytes(final long cpuBytesAvail) { - this.cpuBytesAvail = cpuBytesAvail; - } - public int getCpuCoProcessors() { return this.cpuCoProcessors; } - private void setCpuCoProcessors(final int cpuCoProcessors) { - this.cpuCoProcessors = cpuCoProcessors; - } - - public int getSelectedCpu() { - return this.selectedCpu; - } - - private void setSelectedCpu(final int selectedCpu) { - this.selectedCpu = selectedCpu; - } - public Text getName() { - return this.myName; - } - - private void setName(@Nullable final Text myName) { - this.myName = myName; + return this.cpuName; } public boolean hasNoCPU() { return this.noCPU; } - private void setNoCPU(final boolean noCPU) { - this.noCPU = noCPU; - } - public boolean isSimulation() { return this.simulation; } diff --git a/src/main/java/appeng/container/implementations/CraftingCPUContainer.java b/src/main/java/appeng/container/implementations/CraftingCPUContainer.java index 481bf79a1..501fe92f3 100644 --- a/src/main/java/appeng/container/implementations/CraftingCPUContainer.java +++ b/src/main/java/appeng/container/implementations/CraftingCPUContainer.java @@ -46,24 +46,28 @@ import appeng.core.Api; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.ConfigValuePacket; import appeng.core.sync.packets.MEInventoryUpdatePacket; -import appeng.helpers.ICustomNameObject; import appeng.me.cluster.implementations.CraftingCPUCluster; import appeng.tile.crafting.CraftingBlockEntity; -import appeng.util.Platform; -public class CraftingCPUContainer extends AEBaseContainer - implements IMEMonitorHandlerReceiver, ICustomNameObject { +public class CraftingCPUContainer extends AEBaseContainer implements IMEMonitorHandlerReceiver { public static ScreenHandlerType TYPE; private static final ContainerHelper helper = new ContainerHelper<>( - CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT); + CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT) + .withContainerTitle(craftingTileEntity -> { + // Use the cluster's custom name instead of the right-clicked block entities one + CraftingCPUCluster cluster = craftingTileEntity.getCluster(); + if (cluster != null && cluster.getName() != null) { + return cluster.getName(); + } + return StringTextComponent.EMPTY; + }); private final IItemList list = Api.instance().storage().getStorageChannel(IItemStorageChannel.class) .createList(); - private IGrid network; + private final IGrid network; private CraftingCPUCluster monitor = null; - private Text cpuName = null; @GuiSync(0) public long eta = -1; @@ -77,14 +81,16 @@ public class CraftingCPUContainer extends AEBaseContainer final IActionHost host = (IActionHost) (te instanceof IActionHost ? te : null); if (host != null && host.getActionableNode() != null) { - this.setNetwork(host.getActionableNode().getGrid()); + this.network = host.getActionableNode().getGrid(); + } else { + this.network = null; } if (te instanceof CraftingBlockEntity) { this.setCPU(((CraftingBlockEntity) te).getCluster()); } - if (this.getNetwork() == null && Platform.isServer()) { + if (this.getNetwork() == null && isServer()) { this.setValidContainer(false); } } @@ -114,15 +120,13 @@ public class CraftingCPUContainer extends AEBaseContainer } if (c instanceof CraftingCPUCluster) { - this.cpuName = c.getName(); - this.setMonitor((CraftingCPUCluster) c); + this.monitor = (CraftingCPUCluster) c; this.list.resetStatus(); this.getMonitor().getListOfItem(this.list, CraftingItemList.ALL); this.getMonitor().addListener(this, null); this.setEstimatedTime(0); } else { - this.setMonitor(null); - this.cpuName = null; + this.monitor = null; this.setEstimatedTime(-1); } } @@ -153,7 +157,7 @@ public class CraftingCPUContainer extends AEBaseContainer @Override public void sendContentUpdates() { - if (Platform.isServer() && this.getMonitor() != null && !this.list.isEmpty()) { + if (isServer() && this.getMonitor() != null && !this.list.isEmpty()) { try { if (this.getEstimatedTime() >= 0) { final long elapsedTime = this.getMonitor().getElapsedTime(); @@ -218,16 +222,6 @@ public class CraftingCPUContainer extends AEBaseContainer } - @Override - public Text getCustomInventoryName() { - return this.cpuName; - } - - @Override - public boolean hasCustomInventoryName() { - return this.cpuName != null; - } - public long getEstimatedTime() { return this.eta; } @@ -240,15 +234,8 @@ public class CraftingCPUContainer extends AEBaseContainer return this.monitor; } - private void setMonitor(final CraftingCPUCluster monitor) { - this.monitor = monitor; - } - IGrid getNetwork() { return this.network; } - private void setNetwork(final IGrid network) { - this.network = network; - } } diff --git a/src/main/java/appeng/container/implementations/CraftingCPUCycler.java b/src/main/java/appeng/container/implementations/CraftingCPUCycler.java new file mode 100644 index 000000000..d0ada4826 --- /dev/null +++ b/src/main/java/appeng/container/implementations/CraftingCPUCycler.java @@ -0,0 +1,131 @@ +package appeng.container.implementations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +import com.google.common.collect.ImmutableSet; + +import net.minecraft.util.text.StringTextComponent; + +import appeng.api.networking.IGrid; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; + +/** + * Utility class for dialogs that can cycle through crafting CPUs + */ +class CraftingCPUCycler { + + @FunctionalInterface + public interface ChangeListener { + void onChange(CraftingCPURecord selectedCpu, boolean cpusAvailable); + } + + private final Predicate cpuFilter; + private final ChangeListener changeListener; + private final List cpus = new ArrayList<>(); + private int selectedCpu = -1; + private boolean initialDataSent = false; + private boolean allowNoSelection; + + public CraftingCPUCycler(Predicate cpuFilter, ChangeListener changeListener) { + this.cpuFilter = cpuFilter; + this.changeListener = changeListener; + } + + public void detectAndSendChanges(IGrid network) { + final ICraftingGrid cc = network.getCache(ICraftingGrid.class); + final ImmutableSet cpuSet = cc.getCpus(); + + int matches = 0; + boolean changed = !initialDataSent; + initialDataSent = true; + for (final ICraftingCPU c : cpuSet) { + boolean found = false; + for (final CraftingCPURecord ccr : this.cpus) { + if (ccr.getCpu() == c) { + found = true; + break; + } + } + + final boolean matched = this.cpuFilter.test(c); + + if (matched) { + matches++; + } + + if (found == !matched) { + changed = true; + } + } + + if (changed || this.cpus.size() != matches) { + this.cpus.clear(); + for (final ICraftingCPU c : cpuSet) { + if (this.cpuFilter.test(c)) { + this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c)); + } + } + + // Sort and assign numeric IDs in case they have no names + Collections.sort(this.cpus); + for (int i = 0; i < this.cpus.size(); i++) { + CraftingCPURecord cpu = cpus.get(i); + if (cpu.getName() == null) { + cpu.setName(new StringTextComponent("#" + (i + 1))); + } + } + + this.notifyListener(); + } + } + + public void cycleCpu(final boolean next) { + if (next) { + this.selectedCpu++; + } else { + this.selectedCpu--; + } + + // If "no CPU" is a valid selection, then -1 is the first potential item + int lowerLimit = this.allowNoSelection ? -1 : 0; + + if (this.selectedCpu < lowerLimit) { + this.selectedCpu = this.cpus.size() - 1; + } else if (this.selectedCpu >= this.cpus.size()) { + this.selectedCpu = lowerLimit; + } + + this.notifyListener(); + } + + public boolean isAllowNoSelection() { + return allowNoSelection; + } + + public void setAllowNoSelection(boolean allowNoSelection) { + this.allowNoSelection = allowNoSelection; + } + + private void notifyListener() { + if (this.selectedCpu >= this.cpus.size()) { + this.selectedCpu = -1; + } + + // Force the selected CPU to the first available CPU unless no-selection is + // explicitly allowed + if (!this.allowNoSelection && this.selectedCpu == -1 && !this.cpus.isEmpty()) { + this.selectedCpu = 0; + } + + if (this.selectedCpu != -1) { + this.changeListener.onChange(this.cpus.get(this.selectedCpu), true); + } else { + this.changeListener.onChange(null, !this.cpus.isEmpty()); + } + } + +} diff --git a/src/main/java/appeng/container/implementations/CraftingCPUCyclingContainer.java b/src/main/java/appeng/container/implementations/CraftingCPUCyclingContainer.java new file mode 100644 index 000000000..b2877ac9f --- /dev/null +++ b/src/main/java/appeng/container/implementations/CraftingCPUCyclingContainer.java @@ -0,0 +1,12 @@ +package appeng.container.implementations; + +/** + * Implemented on screens that show information about a crafting CPU and allow + * the CPU to be cycled. Is triggered by receiving a config value packet with + * name Terminal.Cpu. + */ +public interface CraftingCPUCyclingContainer { + + void cycleSelectedCPU(boolean forward); + +} diff --git a/src/main/java/appeng/container/implementations/CraftingCPURecord.java b/src/main/java/appeng/container/implementations/CraftingCPURecord.java index e99030186..a6049d097 100644 --- a/src/main/java/appeng/container/implementations/CraftingCPURecord.java +++ b/src/main/java/appeng/container/implementations/CraftingCPURecord.java @@ -25,16 +25,16 @@ import net.minecraft.text.Text; import appeng.api.networking.crafting.ICraftingCPU; public class CraftingCPURecord implements Comparable { - private final Text myName; private final ICraftingCPU cpu; private final long size; private final int processors; + private Text name; public CraftingCPURecord(final long size, final int coProcessors, final ICraftingCPU server) { this.size = size; this.processors = coProcessors; this.cpu = server; - this.myName = server.getName(); + this.name = server.getName(); } @Override @@ -50,10 +50,6 @@ public class CraftingCPURecord implements Comparable { return this.cpu; } - Text getName() { - return this.myName; - } - int getProcessors() { return this.processors; } @@ -61,4 +57,13 @@ public class CraftingCPURecord implements Comparable { long getSize() { return this.size; } + + public Text getName() { + return name; + } + + public void setName(Text name) { + this.name = name; + } + } diff --git a/src/main/java/appeng/container/implementations/CraftingStatusContainer.java b/src/main/java/appeng/container/implementations/CraftingStatusContainer.java index 24164b2ab..6f9a5653e 100644 --- a/src/main/java/appeng/container/implementations/CraftingStatusContainer.java +++ b/src/main/java/appeng/container/implementations/CraftingStatusContainer.java @@ -18,12 +18,6 @@ package appeng.container.implementations; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.google.common.collect.ImmutableSet; - import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.network.PacketByteBuf; @@ -31,14 +25,13 @@ import net.minecraft.screen.ScreenHandlerType; import net.minecraft.text.Text; import appeng.api.config.SecurityPermissions; +import appeng.api.networking.IGrid; import appeng.api.networking.crafting.ICraftingCPU; -import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.storage.ITerminalHost; import appeng.container.ContainerLocator; import appeng.container.guisync.GuiSync; -import appeng.util.Platform; -public class CraftingStatusContainer extends CraftingCPUContainer { +public class CraftingStatusContainer extends CraftingCPUContainer implements CraftingCPUCyclingContainer { public static ScreenHandlerType TYPE; @@ -53,13 +46,13 @@ public class CraftingStatusContainer extends CraftingCPUContainer { return helper.open(player, locator); } - private final List cpus = new ArrayList<>(); - @GuiSync(5) - public int selectedCpu = -1; + private final CraftingCPUCycler cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged); + @GuiSync(6) public boolean noCPU = true; + @GuiSync(7) - public Text myName; + public Text cpuName; public CraftingStatusContainer(int id, final PlayerInventory ip, final ITerminalHost te) { super(TYPE, id, ip, te); @@ -67,43 +60,9 @@ public class CraftingStatusContainer extends CraftingCPUContainer { @Override public void sendContentUpdates() { - if (Platform.isServer() && this.getNetwork() != null) { - final ICraftingGrid cc = this.getNetwork().getCache(ICraftingGrid.class); - final ImmutableSet cpuSet = cc.getCpus(); - - int matches = 0; - boolean changed = false; - for (final ICraftingCPU c : cpuSet) { - boolean found = false; - for (final CraftingCPURecord ccr : this.cpus) { - if (ccr.getCpu() == c) { - found = true; - } - } - - final boolean matched = this.cpuMatches(c); - - if (matched) { - matches++; - } - - if (found == !matched) { - changed = true; - } - } - - if (changed || this.cpus.size() != matches) { - this.cpus.clear(); - for (final ICraftingCPU c : cpuSet) { - if (this.cpuMatches(c)) { - this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c)); - } - } - - this.sendCPUs(); - } - - this.noCPU = this.cpus.isEmpty(); + IGrid network = this.getNetwork(); + if (isServer() && network != null) { + cpuCycler.detectAndSendChanges(network); } super.sendContentUpdates(); @@ -113,52 +72,20 @@ public class CraftingStatusContainer extends CraftingCPUContainer { return c.isBusy(); } - private void sendCPUs() { - Collections.sort(this.cpus); - - if (this.selectedCpu >= this.cpus.size()) { - this.selectedCpu = -1; - this.myName = null; - } else if (this.selectedCpu != -1) { - this.myName = this.cpus.get(this.selectedCpu).getName(); - } - - if (this.selectedCpu == -1 && this.cpus.size() > 0) { - this.selectedCpu = 0; - } - - if (this.selectedCpu != -1) { - if (this.cpus.get(this.selectedCpu).getCpu() != this.getMonitor()) { - this.setCPU(this.cpus.get(this.selectedCpu).getCpu()); - } + private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) { + noCPU = !cpusAvailable; + if (cpuRecord == null) { + cpuName = null; + setCPU(null); } else { - this.setCPU(null); + cpuName = cpuRecord.getName(); + setCPU(cpuRecord.getCpu()); } } - public void cycleCpu(final boolean next) { - if (next) { - this.selectedCpu++; - } else { - this.selectedCpu--; - } - - if (this.selectedCpu < -1) { - this.selectedCpu = this.cpus.size() - 1; - } else if (this.selectedCpu >= this.cpus.size()) { - this.selectedCpu = -1; - } - - if (this.selectedCpu == -1 && this.cpus.size() > 0) { - this.selectedCpu = 0; - } - - if (this.selectedCpu == -1) { - this.myName = null; - this.setCPU(null); - } else { - this.myName = this.cpus.get(this.selectedCpu).getName(); - this.setCPU(this.cpus.get(this.selectedCpu).getCpu()); - } + @Override + public void cycleSelectedCPU(boolean forward) { + this.cpuCycler.cycleCpu(forward); } + } diff --git a/src/main/java/appeng/container/implementations/FormationPlaneContainer.java b/src/main/java/appeng/container/implementations/FormationPlaneContainer.java index cad591712..6c06d2104 100644 --- a/src/main/java/appeng/container/implementations/FormationPlaneContainer.java +++ b/src/main/java/appeng/container/implementations/FormationPlaneContainer.java @@ -36,7 +36,6 @@ import appeng.container.slot.FakeTypeOnlySlot; import appeng.container.slot.OptionalTypeOnlyFakeSlot; import appeng.container.slot.RestrictedInputSlot; import appeng.parts.automation.FormationPlanePart; -import appeng.util.Platform; public class FormationPlaneContainer extends UpgradeableContainer { @@ -108,7 +107,7 @@ public class FormationPlaneContainer extends UpgradeableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE)); this.setPlaceMode((YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.PLACE_BLOCK)); } diff --git a/src/main/java/appeng/container/implementations/IOPortContainer.java b/src/main/java/appeng/container/implementations/IOPortContainer.java index ab1e9d185..98e4c2303 100644 --- a/src/main/java/appeng/container/implementations/IOPortContainer.java +++ b/src/main/java/appeng/container/implementations/IOPortContainer.java @@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync; import appeng.container.slot.OutputSlot; import appeng.container.slot.RestrictedInputSlot; import appeng.tile.storage.IOPortBlockEntity; -import appeng.util.Platform; public class IOPortContainer extends UpgradeableContainer { @@ -112,7 +111,7 @@ public class IOPortContainer extends UpgradeableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.setOperationMode( (OperationMode) this.getUpgradeable().getConfigManager().getSetting(Settings.OPERATION_MODE)); this.setFullMode( diff --git a/src/main/java/appeng/container/implementations/InscriberContainer.java b/src/main/java/appeng/container/implementations/InscriberContainer.java index 87712452f..fd7996b90 100644 --- a/src/main/java/appeng/container/implementations/InscriberContainer.java +++ b/src/main/java/appeng/container/implementations/InscriberContainer.java @@ -36,7 +36,7 @@ import appeng.container.slot.RestrictedInputSlot; import appeng.core.Api; import appeng.tile.misc.InscriberBlockEntity; import appeng.tile.misc.InscriberRecipes; -import appeng.util.Platform; +import appeng.tile.misc.InscriberTileEntity; /** * @author AlgorithmX2 @@ -120,7 +120,7 @@ public class InscriberContainer extends UpgradeableContainer implements IProgres public void sendContentUpdates() { this.standardDetectAndSendChanges(); - if (Platform.isServer()) { + if (isServer()) { this.maxProcessingTime = this.ti.getMaxProcessingTime(); this.processingTime = this.ti.getProcessingTime(); } diff --git a/src/main/java/appeng/container/implementations/InterfaceTerminalContainer.java b/src/main/java/appeng/container/implementations/InterfaceTerminalContainer.java index 5a768a4db..946d5c3d4 100644 --- a/src/main/java/appeng/container/implementations/InterfaceTerminalContainer.java +++ b/src/main/java/appeng/container/implementations/InterfaceTerminalContainer.java @@ -55,7 +55,6 @@ import appeng.parts.reporting.InterfaceTerminalPart; import appeng.tile.inventory.AppEngInternalInventory; import appeng.tile.misc.InterfaceBlockEntity; import appeng.util.InventoryAdaptor; -import appeng.util.Platform; import appeng.util.helpers.ItemHandlerUtil; import appeng.util.inv.AdaptorFixedInv; import appeng.util.inv.WrapperCursorItemHandler; @@ -88,7 +87,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer { public InterfaceTerminalContainer(int id, final PlayerInventory ip, final InterfaceTerminalPart anchor) { super(TYPE, id, ip, anchor); - if (Platform.isServer()) { + if (isServer()) { this.grid = anchor.getActionableNode().getGrid(); } @@ -97,7 +96,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer { @Override public void sendContentUpdates() { - if (Platform.isClient()) { + if (isClient()) { return; } diff --git a/src/main/java/appeng/container/implementations/LevelEmitterContainer.java b/src/main/java/appeng/container/implementations/LevelEmitterContainer.java index dbe123d1f..8b2222ec8 100644 --- a/src/main/java/appeng/container/implementations/LevelEmitterContainer.java +++ b/src/main/java/appeng/container/implementations/LevelEmitterContainer.java @@ -22,10 +22,9 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.screen.ScreenHandlerType; - -import alexiil.mc.lib.attributes.item.FixedItemInv; +import net.minecraft.inventory.container.ContainerType; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.items.IItemHandler; import appeng.api.config.FuzzyMode; import appeng.api.config.LevelType; @@ -33,13 +32,13 @@ import appeng.api.config.RedstoneMode; import appeng.api.config.SecurityPermissions; import appeng.api.config.Settings; import appeng.api.config.YesNo; -import appeng.client.gui.implementations.NumberEntryWidget; import appeng.container.ContainerLocator; import appeng.container.guisync.GuiSync; import appeng.container.slot.FakeTypeOnlySlot; import appeng.container.slot.RestrictedInputSlot; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.ConfigValuePacket; import appeng.parts.automation.LevelEmitterPart; -import appeng.util.Platform; public class LevelEmitterContainer extends UpgradeableContainer { @@ -49,38 +48,46 @@ public class LevelEmitterContainer extends UpgradeableContainer { LevelEmitterContainer::new, LevelEmitterPart.class, SecurityPermissions.BUILD); public static LevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) { - return helper.fromNetwork(windowId, inv, buf); + return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> { + container.reportingValue = buffer.readVarLong(); + }); } public static boolean open(PlayerEntity player, ContainerLocator locator) { - return helper.open(player, locator); + return helper.open(player, locator, (host, buffer) -> { + buffer.writeVarLong(host.getReportingValue()); + }); } private final LevelEmitterPart lvlEmitter; - @Environment(EnvType.CLIENT) - private NumberEntryWidget textField; @GuiSync(2) public LevelType lvType; @GuiSync(3) - public long EmitterValue = -1; - @GuiSync(4) public YesNo cmType; + // Only synced once on container-open, and only used on client + private long reportingValue; + public LevelEmitterContainer(int id, final PlayerInventory ip, final LevelEmitterPart te) { super(TYPE, id, ip, te); this.lvlEmitter = te; } - @Environment(EnvType.CLIENT) - public void setTextField(final NumberEntryWidget level) { - this.textField = level; - this.textField.setValue(this.EmitterValue); + public long getReportingValue() { + return reportingValue; } - public void setLevel(final long l, final PlayerEntity player) { - this.lvlEmitter.setReportingValue(l); - this.EmitterValue = l; + public void setReportingValue(long reportingValue) { + if (isClient()) { + if (reportingValue != this.reportingValue) { + this.reportingValue = reportingValue; + NetworkHandler.instance() + .sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(reportingValue))); + } + } else { + this.lvlEmitter.setReportingValue(reportingValue); + } } @Override @@ -116,7 +123,6 @@ public class LevelEmitterContainer extends UpgradeableContainer { @Override public int availableUpgrades() { - return 1; } @@ -124,8 +130,7 @@ public class LevelEmitterContainer extends UpgradeableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { - this.EmitterValue = this.lvlEmitter.getReportingValue(); + if (isServer()) { this.setCraftingMode( (YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.CRAFT_VIA_REDSTONE)); this.setLevelMode((LevelType) this.getUpgradeable().getConfigManager().getSetting(Settings.LEVEL_TYPE)); @@ -137,15 +142,6 @@ public class LevelEmitterContainer extends UpgradeableContainer { this.standardDetectAndSendChanges(); } - @Override - public void onUpdate(final String field, final Object oldValue, final Object newValue) { - if (field.equals("EmitterValue")) { - if (this.textField != null) { - this.textField.setValue(this.EmitterValue); - } - } - } - @Override public YesNo getCraftingMode() { return this.cmType; @@ -163,4 +159,5 @@ public class LevelEmitterContainer extends UpgradeableContainer { private void setLevelMode(final LevelType lvType) { this.lvType = lvType; } + } diff --git a/src/main/java/appeng/container/implementations/MEMonitorableContainer.java b/src/main/java/appeng/container/implementations/MEMonitorableContainer.java index e8d0b45df..54e678e5a 100644 --- a/src/main/java/appeng/container/implementations/MEMonitorableContainer.java +++ b/src/main/java/appeng/container/implementations/MEMonitorableContainer.java @@ -46,6 +46,8 @@ import appeng.api.implementations.tiles.IViewCellStorage; import appeng.api.networking.IGrid; import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.networking.energy.IEnergyGrid; import appeng.api.networking.energy.IEnergySource; import appeng.api.networking.security.IActionHost; @@ -73,7 +75,6 @@ import appeng.core.sync.packets.MEInventoryUpdatePacket; import appeng.me.helpers.ChannelPowerSrc; import appeng.util.ConfigManager; import appeng.util.IConfigManagerHost; -import appeng.util.Platform; public class MEMonitorableContainer extends AEBaseContainer implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver { @@ -101,6 +102,13 @@ public class MEMonitorableContainer extends AEBaseContainer public boolean canAccessViewCells = false; @GuiSync(98) public boolean hasPower = false; + /** + * The number of active crafting jobs in the network. -1 means unknown and will + * hide the label on the screen. + */ + @GuiSync(100) + public int activeCraftingJobs = -1; + private IConfigManagerHost gui; private IConfigManager serverCM; private IGridNode networkNode; @@ -122,7 +130,7 @@ public class MEMonitorableContainer extends AEBaseContainer this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL); this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING); - if (Platform.isServer()) { + if (isServer()) { this.serverCM = monitorable.getConfigManager(); this.monitor = monitorable @@ -183,12 +191,14 @@ public class MEMonitorableContainer extends AEBaseContainer @Override public void sendContentUpdates() { - if (Platform.isServer()) { + if (isServer()) { if (this.monitor != this.host .getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class))) { this.setValidContainer(false); } + this.updateActiveCraftingJobs(); + for (final Settings set : this.serverCM.getSettings()) { final Enum sideLocal = this.serverCM.getSetting(set); final Enum sideRemote = this.clientCM.getSetting(set); @@ -286,8 +296,38 @@ public class MEMonitorableContainer extends AEBaseContainer this.queueInventory(c); } + private void updateActiveCraftingJobs() { + IGridNode hostNode = networkNode; + if (hostNode == null) { + // Wireless terminals do not directly expose the target grid (even though they + // have one) + if (host instanceof IActionHost) { + hostNode = ((IActionHost) host).getActionableNode(); + } + } + IGrid grid = null; + if (hostNode != null) { + grid = hostNode.getGrid(); + } + + if (grid == null) { + // No grid to query crafting jobs from + this.activeCraftingJobs = -1; + return; + } + + int activeJobs = 0; + ICraftingGrid craftingGrid = grid.getCache(ICraftingGrid.class); + for (ICraftingCPU cpus : craftingGrid.getCpus()) { + if (cpus.isBusy()) { + activeJobs++; + } + } + this.activeCraftingJobs = activeJobs; + } + private void queueInventory(final ScreenHandlerListener c) { - if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) { + if (isServer() && c instanceof PlayerEntity && this.monitor != null) { try { MEInventoryUpdatePacket piu = new MEInventoryUpdatePacket(); final IItemList monitorCache = this.monitor.getStorageList(); @@ -356,7 +396,7 @@ public class MEMonitorableContainer extends AEBaseContainer @Override public IConfigManager getConfigManager() { - if (Platform.isServer()) { + if (isServer()) { return this.serverCM; } return this.clientCM; diff --git a/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java b/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java index b5948eac6..c8f4a4372 100644 --- a/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java +++ b/src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java @@ -42,7 +42,6 @@ import appeng.container.slot.RestrictedInputSlot; import appeng.core.Api; import appeng.items.misc.EncodedPatternItem; import appeng.tile.crafting.MolecularAssemblerBlockEntity; -import appeng.util.Platform; public class MolecularAssemblerContainer extends UpgradeableContainer implements IProgressProvider { @@ -148,7 +147,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.setRedStoneMode( (RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED)); } diff --git a/src/main/java/appeng/container/implementations/NetworkStatusContainer.java b/src/main/java/appeng/container/implementations/NetworkStatusContainer.java index 972411400..813f5632b 100644 --- a/src/main/java/appeng/container/implementations/NetworkStatusContainer.java +++ b/src/main/java/appeng/container/implementations/NetworkStatusContainer.java @@ -43,7 +43,6 @@ import appeng.container.guisync.GuiSync; import appeng.core.Api; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.MEInventoryUpdatePacket; -import appeng.util.Platform; import appeng.util.item.AEItemStack; public class NetworkStatusContainer extends AEBaseContainer { @@ -83,7 +82,7 @@ public class NetworkStatusContainer extends AEBaseContainer { } } - if (this.network == null && Platform.isServer()) { + if (this.network == null && isServer()) { this.setValidContainer(false); } } @@ -100,7 +99,7 @@ public class NetworkStatusContainer extends AEBaseContainer { @Override public void sendContentUpdates() { this.delay++; - if (Platform.isServer() && this.delay > 15 && this.network != null) { + if (isServer() && this.delay > 15 && this.network != null) { this.delay = 0; final IEnergyGrid eg = this.network.getCache(IEnergyGrid.class); diff --git a/src/main/java/appeng/container/implementations/PatternTermContainer.java b/src/main/java/appeng/container/implementations/PatternTermContainer.java index 55e2e32b6..94d491ed5 100644 --- a/src/main/java/appeng/container/implementations/PatternTermContainer.java +++ b/src/main/java/appeng/container/implementations/PatternTermContainer.java @@ -297,9 +297,9 @@ public class PatternTermContainer extends MEMonitorableContainer @Override public boolean isSlotEnabled(final int idx) { if (idx == 1) { - return Platform.isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode(); + return isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode(); } else if (idx == 2) { - return Platform.isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode(); + return isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode(); } else { return false; } @@ -401,7 +401,7 @@ public class PatternTermContainer extends MEMonitorableContainer @Override public void sendContentUpdates() { super.sendContentUpdates(); - if (Platform.isServer()) { + if (isServer()) { if (this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe()) { this.setCraftingMode(this.getPatternTerminal().isCraftingRecipe()); this.updateOrderOfOutputSlots(); @@ -423,7 +423,7 @@ public class PatternTermContainer extends MEMonitorableContainer @Override public void onSlotChange(final Slot s) { - if (s == this.patternSlotOUT && Platform.isServer()) { + if (s == this.patternSlotOUT && isServer()) { for (final ScreenHandlerListener listener : this.getListeners()) { for (int i = 0; i < this.slots.size(); i++) { Slot slot = this.slots.get(i); @@ -438,7 +438,7 @@ public class PatternTermContainer extends MEMonitorableContainer this.sendContentUpdates(); } - if (s == this.craftSlot && Platform.isClient()) { + if (s == this.craftSlot && isClient()) { this.getAndUpdateOutput(); } } diff --git a/src/main/java/appeng/container/implementations/PriorityContainer.java b/src/main/java/appeng/container/implementations/PriorityContainer.java index 4f35a2af8..7d88e7062 100644 --- a/src/main/java/appeng/container/implementations/PriorityContainer.java +++ b/src/main/java/appeng/container/implementations/PriorityContainer.java @@ -28,12 +28,11 @@ import net.minecraft.screen.ScreenHandlerType; import appeng.api.config.SecurityPermissions; import appeng.api.parts.IPart; -import appeng.client.gui.implementations.NumberEntryWidget; import appeng.container.AEBaseContainer; import appeng.container.ContainerLocator; -import appeng.container.guisync.GuiSync; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.ConfigValuePacket; import appeng.helpers.IPriorityHost; -import appeng.util.Platform; public class PriorityContainer extends AEBaseContainer { @@ -43,59 +42,53 @@ public class PriorityContainer extends AEBaseContainer { PriorityContainer::new, IPriorityHost.class, SecurityPermissions.BUILD); public static PriorityContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) { - return helper.fromNetwork(windowId, inv, buf); + return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> { + container.priorityValue = buffer.readVarInt(); + }); } public static boolean open(PlayerEntity player, ContainerLocator locator) { - return helper.open(player, locator); + return helper.open(player, locator, (host, buffer) -> buffer.writeVarInt(host.getPriority())); } private final IPriorityHost priHost; - @Environment(EnvType.CLIENT) - private NumberEntryWidget textField; - @GuiSync(2) - public long PriorityValue = -1; + private int priorityValue; public PriorityContainer(int id, final PlayerInventory ip, final IPriorityHost te) { super(TYPE, id, ip, (BlockEntity) (te instanceof BlockEntity ? te : null), (IPart) (te instanceof IPart ? te : null)); this.priHost = te; + this.priorityValue = te.getPriority(); } - @Environment(EnvType.CLIENT) - public void setTextField(final NumberEntryWidget level) { - this.textField = level; - this.textField.setValue(this.PriorityValue, true); - } - - public void setPriority(final int newValue, final PlayerEntity player) { - this.priHost.setPriority(newValue); - this.PriorityValue = newValue; + public void setPriority(final int newValue) { + if (newValue != priorityValue) { + if (isClient()) { + // If for whatever reason the client enters the value first, do not update based + // on incoming server data + this.priorityValue = newValue; + NetworkHandler.instance() + .sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(newValue))); + } else { + this.priHost.setPriority(newValue); + this.priorityValue = newValue; + } + } } @Override public void sendContentUpdates() { super.sendContentUpdates(); this.verifyPermissions(SecurityPermissions.BUILD, false); - - if (Platform.isServer()) { - this.PriorityValue = this.priHost.getPriority(); - } } - @Override - public void onUpdate(final String field, final Object oldValue, final Object newValue) { - if (field.equals("PriorityValue")) { - if (this.textField != null) { - this.textField.setValue(this.PriorityValue, true); - } - } - - super.onUpdate(field, oldValue, newValue); + public int getPriorityValue() { + return priorityValue; } public IPriorityHost getPriorityHost() { return this.priHost; } + } diff --git a/src/main/java/appeng/container/implementations/QuartzKnifeContainer.java b/src/main/java/appeng/container/implementations/QuartzKnifeContainer.java index b32507db5..625236c7d 100644 --- a/src/main/java/appeng/container/implementations/QuartzKnifeContainer.java +++ b/src/main/java/appeng/container/implementations/QuartzKnifeContainer.java @@ -37,7 +37,6 @@ import appeng.core.Api; import appeng.items.contents.QuartzKnifeObj; import appeng.items.materials.MaterialItem; import appeng.tile.inventory.AppEngInternalInventory; -import appeng.util.Platform; public class QuartzKnifeContainer extends AEBaseContainer { @@ -142,7 +141,7 @@ public class QuartzKnifeContainer extends AEBaseContainer { } private void makePlate() { - if (Platform.isServer()) { + if (isServer()) { if (!inSlot.getSlot(0).extract(1).isEmpty()) { final ItemStack item = QuartzKnifeContainer.this.toolInv.getItemStack(); final ItemStack before = item.copy(); diff --git a/src/main/java/appeng/container/implementations/SpatialIOPortContainer.java b/src/main/java/appeng/container/implementations/SpatialIOPortContainer.java index a922fad1d..0c3e3b1ce 100644 --- a/src/main/java/appeng/container/implementations/SpatialIOPortContainer.java +++ b/src/main/java/appeng/container/implementations/SpatialIOPortContainer.java @@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync; import appeng.container.slot.OutputSlot; import appeng.container.slot.RestrictedInputSlot; import appeng.tile.spatial.SpatialIOPortBlockEntity; -import appeng.util.Platform; public class SpatialIOPortContainer extends AEBaseContainer { @@ -65,7 +64,7 @@ public class SpatialIOPortContainer extends AEBaseContainer { public SpatialIOPortContainer(int id, final PlayerInventory ip, final SpatialIOPortBlockEntity spatialIOPort) { super(TYPE, id, ip, spatialIOPort, null); - if (Platform.isServer()) { + if (isServer()) { this.network = spatialIOPort.getGridNode(AEPartLocation.INTERNAL).getGrid(); } @@ -89,7 +88,7 @@ public class SpatialIOPortContainer extends AEBaseContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.delay++; if (this.delay > 15 && this.network != null) { this.delay = 0; diff --git a/src/main/java/appeng/container/implementations/StorageBusContainer.java b/src/main/java/appeng/container/implementations/StorageBusContainer.java index 194cf63fc..982af3243 100644 --- a/src/main/java/appeng/container/implementations/StorageBusContainer.java +++ b/src/main/java/appeng/container/implementations/StorageBusContainer.java @@ -45,7 +45,6 @@ import appeng.container.slot.OptionalTypeOnlyFakeSlot; import appeng.container.slot.RestrictedInputSlot; import appeng.core.Api; import appeng.parts.misc.StorageBusPart; -import appeng.util.Platform; import appeng.util.helpers.ItemHandlerUtil; import appeng.util.iterators.NullIterator; @@ -125,7 +124,7 @@ public class StorageBusContainer extends UpgradeableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE)); this.setReadWriteMode( (AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS)); diff --git a/src/main/java/appeng/container/implementations/UpgradeableContainer.java b/src/main/java/appeng/container/implementations/UpgradeableContainer.java index cd754bb91..5e9804288 100644 --- a/src/main/java/appeng/container/implementations/UpgradeableContainer.java +++ b/src/main/java/appeng/container/implementations/UpgradeableContainer.java @@ -52,7 +52,6 @@ import appeng.container.slot.RestrictedInputSlot; import appeng.items.contents.NetworkToolViewer; import appeng.items.tools.NetworkToolItem; import appeng.parts.automation.ExportBusPart; -import appeng.util.Platform; public class UpgradeableContainer extends AEBaseContainer implements IOptionalSlotHost { @@ -200,7 +199,7 @@ public class UpgradeableContainer extends AEBaseContainer implements IOptionalSl public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { final IConfigManager cm = this.getUpgradeable().getConfigManager(); this.loadSettingsFromHost(cm); } diff --git a/src/main/java/appeng/container/implementations/VibrationChamberContainer.java b/src/main/java/appeng/container/implementations/VibrationChamberContainer.java index 57c79d718..eff9f7ec1 100644 --- a/src/main/java/appeng/container/implementations/VibrationChamberContainer.java +++ b/src/main/java/appeng/container/implementations/VibrationChamberContainer.java @@ -29,7 +29,6 @@ import appeng.container.guisync.GuiSync; import appeng.container.interfaces.IProgressProvider; import appeng.container.slot.RestrictedInputSlot; import appeng.tile.misc.VibrationChamberBlockEntity; -import appeng.util.Platform; public class VibrationChamberContainer extends AEBaseContainer implements IProgressProvider { @@ -65,7 +64,7 @@ public class VibrationChamberContainer extends AEBaseContainer implements IProgr @Override public void sendContentUpdates() { - if (Platform.isServer()) { + if (isServer()) { this.remainingBurnTime = this.vibrationChamber.getMaxBurnTime() <= 0 ? 0 : (int) (100.0 * this.vibrationChamber.getBurnTime() / this.vibrationChamber.getMaxBurnTime()); this.burnSpeed = this.remainingBurnTime <= 0 ? 0 : this.vibrationChamber.getBurnSpeed(); diff --git a/src/main/java/appeng/container/implementations/WirelessTermContainer.java b/src/main/java/appeng/container/implementations/WirelessTermContainer.java index db5868106..988ca3610 100644 --- a/src/main/java/appeng/container/implementations/WirelessTermContainer.java +++ b/src/main/java/appeng/container/implementations/WirelessTermContainer.java @@ -28,7 +28,6 @@ import appeng.container.ContainerLocator; import appeng.core.AEConfig; import appeng.core.localization.PlayerMessages; import appeng.helpers.WirelessTerminalGuiObject; -import appeng.util.Platform; public class WirelessTermContainer extends MEPortableCellContainer { @@ -57,7 +56,7 @@ public class WirelessTermContainer extends MEPortableCellContainer { super.sendContentUpdates(); if (!this.wirelessTerminalGUIObject.rangeCheck()) { - if (Platform.isServer() && this.isValidContainer()) { + if (isServer() && this.isValidContainer()) { this.getPlayerInv().player.sendSystemMessage(PlayerMessages.OutOfRange.get(), Util.NIL_UUID); } diff --git a/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java b/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java index e86079935..b59c6947d 100644 --- a/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java +++ b/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java @@ -37,7 +37,7 @@ import appeng.container.AEBaseContainer; import appeng.container.implementations.CellWorkbenchContainer; import appeng.container.implementations.CraftConfirmContainer; import appeng.container.implementations.CraftingCPUContainer; -import appeng.container.implementations.CraftingStatusContainer; +import appeng.container.implementations.CraftingCPUCyclingContainer; import appeng.container.implementations.LevelEmitterContainer; import appeng.container.implementations.NetworkToolContainer; import appeng.container.implementations.PatternTermContainer; @@ -97,12 +97,9 @@ public class ConfigValuePacket extends BasePacket { final ItemStack is = player.getStackInHand(hand); final IMouseWheelItem si = (IMouseWheelItem) is.getItem(); si.onWheel(is, this.Value.equals("WheelUp")); - } else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingStatusContainer) { - final CraftingStatusContainer qk = (CraftingStatusContainer) c; - qk.cycleCpu(this.Value.equals("Next")); - } else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftConfirmContainer) { - final CraftConfirmContainer qk = (CraftConfirmContainer) c; - qk.cycleCpu(this.Value.equals("Next")); + } else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingCPUCyclingContainer) { + final CraftingCPUCyclingContainer qk = (CraftingCPUCyclingContainer) c; + qk.cycleSelectedCPU(this.Value.equals("Next")); } else if (this.Name.equals("Terminal.Start") && c instanceof CraftConfirmContainer) { final CraftConfirmContainer qk = (CraftConfirmContainer) c; qk.startJob(); @@ -117,13 +114,13 @@ public class ConfigValuePacket extends BasePacket { sc.toggleSetting(this.Value, player); } else if (this.Name.equals("PriorityHost.Priority") && c instanceof PriorityContainer) { final PriorityContainer pc = (PriorityContainer) c; - pc.setPriority(Integer.parseInt(this.Value), player); + pc.setPriority(Integer.parseInt(this.Value)); } else if (this.Name.equals("LevelEmitter.Value") && c instanceof LevelEmitterContainer) { final LevelEmitterContainer lvc = (LevelEmitterContainer) c; - lvc.setLevel(Long.parseLong(this.Value), player); + lvc.setReportingValue(Long.parseLong(this.Value)); } else if (this.Name.equals("FluidLevelEmitter.Value") && c instanceof FluidLevelEmitterContainer) { final FluidLevelEmitterContainer lvc = (FluidLevelEmitterContainer) c; - lvc.setLevel(Long.parseLong(this.Value), player); + lvc.setReportingValue(Long.parseLong(this.Value)); } else if (this.Name.startsWith("PatternTerminal.") && c instanceof PatternTermContainer) { final PatternTermContainer cpt = (PatternTermContainer) c; if (this.Name.equals("PatternTerminal.CraftMode")) { @@ -191,9 +188,7 @@ public class ConfigValuePacket extends BasePacket { public void clientPacketData(final INetworkInfo network, final PlayerEntity player) { final ScreenHandler c = player.currentScreenHandler; - if (this.Name.equals("CustomName") && c instanceof AEBaseContainer) { - ((AEBaseContainer) c).setCustomName(this.Value); - } else if (this.Name.startsWith("SyncDat.")) { + if (this.Name.startsWith("SyncDat.")) { ((AEBaseContainer) c).stringSync(Integer.parseInt(this.Name.substring(8)), this.Value); } else if (this.Name.equals("CraftingStatus") && this.Value.equals("Clear")) { final Screen gs = MinecraftClient.getInstance().currentScreen; diff --git a/src/main/java/appeng/fluids/client/gui/FluidLevelEmitterScreen.java b/src/main/java/appeng/fluids/client/gui/FluidLevelEmitterScreen.java index 64b84fa5e..aaecac4ca 100644 --- a/src/main/java/appeng/fluids/client/gui/FluidLevelEmitterScreen.java +++ b/src/main/java/appeng/fluids/client/gui/FluidLevelEmitterScreen.java @@ -11,8 +11,6 @@ import appeng.client.gui.implementations.NumberEntryWidget; import appeng.client.gui.implementations.UpgradeableScreen; import appeng.client.gui.widgets.ServerSettingToggleButton; import appeng.core.localization.GuiText; -import appeng.core.sync.network.NetworkHandler; -import appeng.core.sync.packets.ConfigValuePacket; import appeng.fluids.client.gui.widgets.FluidSlotWidget; import appeng.fluids.container.FluidLevelEmitterContainer; @@ -28,17 +26,24 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen(this.x - 18, this.y + 28, Settings.REDSTONE_EMITTER, @@ -79,8 +84,4 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen { + container.reportingValue = buffer.readVarLong(); + }); } public static boolean open(PlayerEntity player, ContainerLocator locator) { - return helper.open(player, locator); + return helper.open(player, locator, (host, buffer) -> { + buffer.writeVarLong(host.getReportingValue()); + }); } private final FluidLevelEmitterPart lvlEmitter; - @Environment(EnvType.CLIENT) - private NumberEntryWidget textField; - @GuiSync(3) - public long EmitterValue = -1; + // Only synced once on container-open, and only used on client + private long reportingValue; public FluidLevelEmitterContainer(int id, final PlayerInventory ip, final FluidLevelEmitterPart te) { super(TYPE, id, ip, te); this.lvlEmitter = te; } - @Environment(EnvType.CLIENT) - public void setTextField(final NumberEntryWidget level) { - this.textField = level; - this.textField.setValue(this.EmitterValue); + public long getReportingValue() { + return reportingValue; } - public void setLevel(final long l, final PlayerEntity player) { - this.lvlEmitter.setReportingValue(l); - this.EmitterValue = l; + public void setReportingValue(long reportingValue) { + if (isClient()) { + if (reportingValue != this.reportingValue) { + this.reportingValue = reportingValue; + NetworkHandler.instance() + .sendToServer(new ConfigValuePacket("FluidLevelEmitter.Value", String.valueOf(reportingValue))); + } + } else { + this.lvlEmitter.setReportingValue(reportingValue); + } } @Override @@ -74,8 +81,7 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { - this.EmitterValue = this.lvlEmitter.getReportingValue(); + if (isServer()) { this.setRedStoneMode( (RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_EMITTER)); } @@ -83,15 +89,6 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer { this.standardDetectAndSendChanges(); } - @Override - public void onUpdate(final String field, final Object oldValue, final Object newValue) { - if (field.equals("EmitterValue")) { - if (this.textField != null) { - this.textField.setValue(this.EmitterValue); - } - } - } - @Override public IAEFluidTank getFluidConfigInventory() { return this.lvlEmitter.getConfig(); diff --git a/src/main/java/appeng/fluids/container/FluidStorageBusContainer.java b/src/main/java/appeng/fluids/container/FluidStorageBusContainer.java index 4f16f3d69..4830e8833 100644 --- a/src/main/java/appeng/fluids/container/FluidStorageBusContainer.java +++ b/src/main/java/appeng/fluids/container/FluidStorageBusContainer.java @@ -129,7 +129,7 @@ public class FluidStorageBusContainer extends FluidConfigurableContainer { public void sendContentUpdates() { this.verifyPermissions(SecurityPermissions.BUILD, false); - if (Platform.isServer()) { + if (isServer()) { this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE)); this.setReadWriteMode( (AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS)); diff --git a/src/main/java/appeng/fluids/container/FluidTerminalContainer.java b/src/main/java/appeng/fluids/container/FluidTerminalContainer.java index 41dd925fb..1c636f1f2 100644 --- a/src/main/java/appeng/fluids/container/FluidTerminalContainer.java +++ b/src/main/java/appeng/fluids/container/FluidTerminalContainer.java @@ -124,7 +124,7 @@ public class FluidTerminalContainer extends AEBaseContainer this.clientCM.registerSetting(Settings.SORT_BY, SortOrder.NAME); this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING); this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL); - if (Platform.isServer()) { + if (isServer()) { this.serverCM = terminal.getConfigManager(); this.monitor = terminal .getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class)); @@ -196,7 +196,7 @@ public class FluidTerminalContainer extends AEBaseContainer } private void queueInventory(final ScreenHandlerListener c) { - if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) { + if (isServer() && c instanceof PlayerEntity && this.monitor != null) { try { MEFluidInventoryUpdatePacket piu = new MEFluidInventoryUpdatePacket(); final IItemList monitorCache = this.monitor.getStorageList(); @@ -221,14 +221,14 @@ public class FluidTerminalContainer extends AEBaseContainer @Override public IConfigManager getConfigManager() { - if (Platform.isServer()) { + if (isServer()) { return this.serverCM; } return this.clientCM; } public void setTargetStack(final IAEFluidStack stack) { - if (Platform.isClient()) { + if (isClient()) { if (stack == null && this.clientRequestedTargetFluid == null) { return; } @@ -251,7 +251,7 @@ public class FluidTerminalContainer extends AEBaseContainer @Override public void sendContentUpdates() { - if (Platform.isServer()) { + if (isServer()) { if (this.monitor != this.terminal .getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class))) { this.setValidContainer(false); diff --git a/src/main/java/appeng/fluids/util/AEFluidInventory.java b/src/main/java/appeng/fluids/util/AEFluidInventory.java index 9e6b15457..687ea1226 100644 --- a/src/main/java/appeng/fluids/util/AEFluidInventory.java +++ b/src/main/java/appeng/fluids/util/AEFluidInventory.java @@ -19,6 +19,14 @@ import appeng.core.AELog; import appeng.util.Platform; public class AEFluidInventory implements IAEFluidTank { + + /** + * While this may seem redundant, it helps since this class heavily mixes AE + * fluids stacks, which use null to represent "nothing", and Minecraft's + * FluidStack, which uses #isEmpty() to represent nothing. + */ + private static final IAEFluidStack EMPTY_AE_FLUIDSTACK = null; + private final IAEFluidStack[] fluids; private final IAEFluidInventory handler; private final int capacity; @@ -48,13 +56,13 @@ public class AEFluidInventory implements IAEFluidTank { public void setFluidInSlot(final int slot, final IAEFluidStack fluid) { if (slot >= 0 && slot < this.getSlots()) { if (Objects.equals(this.fluids[slot], fluid)) { - if (fluid != null && fluid.getStackSize() != this.fluids[slot].getStackSize()) { + if (fluid != EMPTY_AE_FLUIDSTACK && fluid.getStackSize() != this.fluids[slot].getStackSize()) { this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity)); this.onContentChanged(slot); } } else { - if (fluid == null) { - this.fluids[slot] = null; + if (fluid == EMPTY_AE_FLUIDSTACK) { + this.fluids[slot] = EMPTY_AE_FLUIDSTACK; } else { this.fluids[slot] = fluid.copy(); this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity)); @@ -76,7 +84,7 @@ public class AEFluidInventory implements IAEFluidTank { if (slot >= 0 && slot < this.getSlots()) { return this.fluids[slot]; } - return null; + return EMPTY_AE_FLUIDSTACK; } @Override @@ -99,7 +107,7 @@ public class AEFluidInventory implements IAEFluidTank { if (tank < 0 || tank >= fluids.length) { return FluidVolumeUtil.EMPTY; } - return fluids[tank] == null ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack(); + return fluids[tank] == EMPTY_AE_FLUIDSTACK ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack(); } @Override @@ -119,13 +127,13 @@ public class AEFluidInventory implements IAEFluidTank { final IAEFluidStack fluid = this.fluids[slot]; - if (fluid != null && !fluid.getFluidStack().equals(resource)) { + if (fluid != EMPTY_AE_FLUIDSTACK && !fluid.getFluidStack().equals(resource)) { return 0; } int amountToStore = this.capacity; - if (fluid != null) { + if (fluid != EMPTY_AE_FLUIDSTACK) { amountToStore -= fluid.getStackSize(); } @@ -145,8 +153,8 @@ public class AEFluidInventory implements IAEFluidTank { public FluidVolume drain(final int slot, final FluidVolume resource, final boolean doDrain) { final IAEFluidStack fluid = this.fluids[slot]; - if (resource.isEmpty() || fluid == null || !fluid.getFluidStack().equals(resource)) { - return null; + if (resource.isEmpty() || fluid == EMPTY_AE_FLUIDSTACK || !fluid.getFluidStack().equals(resource)) { + return FluidStack.EMPTY; } int toDrain = (int) resource.getAmount_F().asLong(1000, RoundingMode.DOWN); return this.drain(slot, toDrain, doDrain); @@ -154,8 +162,8 @@ public class AEFluidInventory implements IAEFluidTank { public FluidVolume drain(final int slot, final int maxDrain, boolean doDrain) { final IAEFluidStack fluid = this.fluids[slot]; - if (fluid == null || maxDrain <= 0) { - return null; + if (fluid == EMPTY_AE_FLUIDSTACK || maxDrain <= 0) { + return FluidStack.EMPTY; } int drained = maxDrain; @@ -167,7 +175,7 @@ public class AEFluidInventory implements IAEFluidTank { if (doDrain) { fluid.setStackSize(fluid.getStackSize() - drained); if (fluid.getStackSize() <= 0) { - this.fluids[slot] = null; + this.fluids[slot] = EMPTY_AE_FLUIDSTACK; } this.onContentChanged(slot); } @@ -185,7 +193,7 @@ public class AEFluidInventory implements IAEFluidTank { try { final CompoundTag c = new CompoundTag(); - if (this.fluids[x] != null) { + if (this.fluids[x] != EMPTY_AE_FLUIDSTACK) { this.fluids[x].writeToNBT(c); } diff --git a/src/main/java/appeng/tile/crafting/CraftingStorageBlockEntity.java b/src/main/java/appeng/tile/crafting/CraftingStorageBlockEntity.java index c12fac140..9163d67a2 100644 --- a/src/main/java/appeng/tile/crafting/CraftingStorageBlockEntity.java +++ b/src/main/java/appeng/tile/crafting/CraftingStorageBlockEntity.java @@ -78,7 +78,7 @@ public class CraftingStorageBlockEntity extends CraftingBlockEntity { return 0; } - final AbstractCraftingUnitBlock unit = (AbstractCraftingUnitBlock) this.world.getBlockState(this.pos) + final AbstractCraftingUnitBlock unit = (AbstractCraftingUnitBlock) this.world.getBlockState(this.pos) .getBlock(); switch (unit.type) { default: diff --git a/src/main/java/appeng/util/Platform.java b/src/main/java/appeng/util/Platform.java index 31e9df6b9..2de131513 100644 --- a/src/main/java/appeng/util/Platform.java +++ b/src/main/java/appeng/util/Platform.java @@ -219,13 +219,6 @@ public class Platform { return !AppEng.instance().isOnServerThread(); } - /* - * returns true if client classes are available. - */ - public static boolean isClientInstall() { - return CLIENT_INSTALL; - } - public static boolean hasPermissions(final DimensionalCoord dc, final PlayerEntity player) { if (!dc.isInWorld(player.world)) { return false; diff --git a/src/main/resources/assets/appliedenergistics2/lang/en_us.json b/src/main/resources/assets/appliedenergistics2/lang/en_us.json index 9ecfa4a7a..ebe5e7526 100644 --- a/src/main/resources/assets/appliedenergistics2/lang/en_us.json +++ b/src/main/resources/assets/appliedenergistics2/lang/en_us.json @@ -289,6 +289,8 @@ "gui.appliedenergistics2.With": "with", "gui.appliedenergistics2.Yellow": "Yellow", "gui.appliedenergistics2.Yes": "Yes", + "gui.appliedenergistics2.validation.InvalidNumber": "Please enter a number", + "gui.appliedenergistics2.validation.NumberLessThanMinValue": "Please enter a number greater than or equal to %d", "gui.appliedenergistics2.inWorldCraftingPresses": "Crafting Presses are found in the center of meteorites which can be found in around the world, they can be located by using a meteorite compass.", "gui.appliedenergistics2.inWorldFluix": "Drop 1 Charged Certus Quartz + 1 Nether Quartz + Redstone Dust into a puddle next to one another and wait a moment to receive 2 Fluix Crystals.", "gui.appliedenergistics2.inWorldPurificationCertus": "Drop a Certus Quartz Seed made from Certus Quartz Dust and Sand into a puddle of water; to make the process faster add Crystal Growth Accelerators.", diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png index d401c375c..7d2f809a9 100644 Binary files a/src/main/resources/assets/appliedenergistics2/textures/guis/states.png and b/src/main/resources/assets/appliedenergistics2/textures/guis/states.png differ