Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 130 additions & 8 deletions src/main/java/com/glodblock/github/client/gui/GuiLevelMaintainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.jetbrains.annotations.Nullable;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;

import com.glodblock.github.FluidCraft;
import com.glodblock.github.client.gui.container.ContainerLevelMaintainer;
Expand All @@ -36,13 +37,20 @@

public class GuiLevelMaintainer extends GuiSub {

private static final float REFRESH_LABEL_SCALE = 0.8F;
private static final int REFRESH_FIELD_X = 90;
private static final int REFRESH_FIELD_WIDTH = 44;
private static final int REFRESH_UNITS_X = REFRESH_FIELD_X + REFRESH_FIELD_WIDTH + 4;
private static final ResourceLocation TEX_BG = FluidCraft.resource("textures/gui/level_maintainer.png");
private final ContainerLevelMaintainer cont;
private final Component[] component = new Component[TileLevelMaintainer.REQ_COUNT];
private final MouseRegionManager mouseRegions = new MouseRegionManager(this);
private Widget focusedWidget;
private final FontRenderer render;
private GuiToggleButton liteMode;
private Widget refreshRate;
/** Last refresh interval the server reported, in ticks; shown unless the player is editing the field. */
private int refreshRateTicks;

public GuiLevelMaintainer(InventoryPlayer ipl, TileLevelMaintainer tile) {
super(new ContainerLevelMaintainer(ipl, tile));
Expand All @@ -59,6 +67,9 @@ public GuiLevelMaintainer(InventoryPlayer ipl, TileLevelMaintainer tile) {
@Override
public void initGui() {
super.initGui();
// All widgets below are rebuilt here, so drop the reference to the discarded ones; a resize re-runs this and a
// stale widget would make the index based checks below read the wrong one.
this.focusedWidget = null;

for (int i = 0; i < TileLevelMaintainer.REQ_COUNT; i++) {
VirtualMEPhantomSlot slot = new VirtualMEPhantomSlot(
Expand Down Expand Up @@ -91,6 +102,23 @@ public void initGui() {
this.buttonList,
this.cont);
}
// Refresh rate. The player types seconds, the tile stores ticks; it sits in the free strip between the request
// rows and the inventory. Unlike the rows it draws its background, so its white value stands out from them.
// Keep separate areas for the label, input and units. Font metrics during initGui can differ from rendering.
this.refreshRate = new Widget(
new FCGuiTextField(
this.fontRendererObj,
guiLeft + REFRESH_FIELD_X,
guiTop + 115,
REFRESH_FIELD_WIDTH,
14),
NameConst.TT_LEVEL_MAINTAINER_REFRESH_RATE,
-1,
Action.SetRefreshRate);
this.refreshRate.textField.setEnableBackgroundDrawing(true);
this.refreshRate.validTextColor = FCGuiColors.guiTextColorWhite.getColor();
// A resize re-runs this, and the widget is rebuilt as "0", so put the value the block is using back.
showRefreshRate();
this.buttonList.add(
this.liteMode = new GuiToggleButton(
guiLeft - 18,
Expand Down Expand Up @@ -122,6 +150,7 @@ public void drawScreen(final int mouseX, final int mouseY, final float btn) {
com.getBatch().textField.handleTooltip(mouseX, mouseY, this);
com.getLine().handleTooltip(mouseX, mouseY, this);
}
this.refreshRate.textField.handleTooltip(mouseX, mouseY, this);
}

@Override
Expand All @@ -132,6 +161,7 @@ public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) {
for (int i = 0; i < TileLevelMaintainer.REQ_COUNT; i++) {
this.component[i].draw();
}
this.refreshRate.draw();
}

@Override
Expand All @@ -141,12 +171,35 @@ public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) {
8,
6,
FCGuiColors.guiTextColorGray.getColor());
drawRefreshLabel(
NameConst.i18n(NameConst.GUI_LEVEL_MAINTAINER_REFRESH_RATE, "\n", false),
8,
REFRESH_FIELD_X - 8 - 6);
drawRefreshLabel(
NameConst.i18n(NameConst.GUI_LEVEL_MAINTAINER_REFRESH_RATE_SECONDS, "\n", false),
REFRESH_UNITS_X,
172 - REFRESH_UNITS_X);
mouseRegions.render(mouseX, mouseY);
}

private void drawRefreshLabel(String text, int x, int availableWidth) {
final float scale = Math
.min(REFRESH_LABEL_SCALE, availableWidth / (float) Math.max(1, fontRendererObj.getStringWidth(text)));
GL11.glPushMatrix();
GL11.glTranslatef(x, 118, 0);
GL11.glScalef(scale, scale, 1F);
fontRendererObj.drawString(text, 0, 0, FCGuiColors.guiTextColorGray.getColor());
GL11.glPopMatrix();
}

@Override
protected void mouseClicked(final int xCoord, final int yCoord, final int btn) {
if (btn == 0) {
if (this.refreshRate.textField.isMouseIn(xCoord, yCoord)) {
this.focusWidget(this.refreshRate);
super.mouseClicked(xCoord, yCoord, btn);
return;
}
for (Component com : this.component) {
Widget textField = com.isMouseIn(xCoord, yCoord);
if (textField != null) {
Expand All @@ -168,14 +221,20 @@ protected void keyTyped(final char character, final int key) {
}
if (!this.checkHotbarKeys(key)) {
if (!((character == ' ') && this.focusedWidget.textField.getText().isEmpty())) {
final String before = this.focusedWidget.textField.getText();
this.focusedWidget.textField.textboxKeyTyped(character, key);
// Control keys (Tab, arrows) must not count as an edit, or Enter would commit the displayed default
// as an override.
if (!before.equals(this.focusedWidget.textField.getText())) {
this.focusedWidget.dirty = true;
}
}
super.keyTyped(character, key);

this.focusedWidget.validate();

if (key == Keyboard.KEY_RETURN || key == Keyboard.KEY_NUMPADENTER) {
this.component[this.focusedWidget.componentIndex].submit();
this.submitFocused();
this.focusWidget(null);
}

Expand All @@ -188,6 +247,11 @@ protected void keyTyped(final char character, final int key) {
private void focusWidget(@Nullable Widget widget) {
if (this.focusedWidget != null) {
this.focusedWidget.textField.setFocused(false);
if (this.focusedWidget != widget && this.focusedWidget == this.refreshRate && this.refreshRate.dirty) {
// Focus moved away, which discards the edit exactly like the request rows, so the field never keeps
// showing a value the block is not using. Clicking back into the box being typed in must not reset it.
showRefreshRate();
}
}
this.focusedWidget = widget;
if (this.focusedWidget != null) {
Expand All @@ -201,23 +265,69 @@ private void focusAdjacentWidget(boolean backwards) {
}

private Widget getAdjacentWidget(boolean backwards) {
int index = this.focusedWidget.componentIndex;
boolean isBatch = this.focusedWidget.action == Action.Batch;
final Widget current = this.focusedWidget;
if (current.componentIndex < 0) {
// The refresh rate sits after the last request row, so Tab walks out through it and wraps around.
return backwards ? this.component[TileLevelMaintainer.REQ_COUNT - 1].getBatch()
: this.component[0].getQty();
}
final int index = current.componentIndex;
final boolean isBatch = current.action == Action.Batch;

if (backwards) {
if (isBatch) {
return this.component[index].getQty();
}
return this.component[(index + TileLevelMaintainer.REQ_COUNT - 1) % TileLevelMaintainer.REQ_COUNT]
.getBatch();
return index == 0 ? this.refreshRate : this.component[index - 1].getBatch();
}

if (isBatch) {
return this.component[(index + 1) % TileLevelMaintainer.REQ_COUNT].getQty();
return index == TileLevelMaintainer.REQ_COUNT - 1 ? this.refreshRate : this.component[index + 1].getQty();
}
return this.component[index].getBatch();
}

/** Enter either applies one request row or the block's refresh rate. */
private void submitFocused() {
final Widget widget = this.focusedWidget;
if (widget.componentIndex < 0) {
// Submitting an untouched field would pin the effective rate in as an override and stop the block from
// following the server config, so only an actual edit is sent.
if (!widget.dirty) return;
widget.validate();
final Long typed = widget.getAmount();
if (typed != null) {
// The field takes seconds; keep the sent value in range so the packet cannot overflow, and echo the
// clamped number back straight away.
final long seconds = Math.max(
0L,
Math.min(TileLevelMaintainer.MAX_REFRESH_TICKS / TileLevelMaintainer.TICKS_PER_SECOND, typed));
FluidCraft.proxy.netHandler.sendToServer(
new CPacketLevelMaintainer(widget.action, -1, seconds * TileLevelMaintainer.TICKS_PER_SECOND));
widget.textField.setText(String.valueOf(seconds));
}
widget.dirty = false;
return;
}
this.component[widget.componentIndex].submit();
}

/** Shows the maximum idle interval, unless the player is editing it right now. */
public void updateRefreshRate(int ticks) {
this.refreshRateTicks = ticks;
if (this.focusedWidget == this.refreshRate || this.refreshRate.dirty) return;
showRefreshRate();
}

private void showRefreshRate() {
this.refreshRate.textField.setText(
this.refreshRateTicks % TileLevelMaintainer.TICKS_PER_SECOND == 0
? String.valueOf(this.refreshRateTicks / TileLevelMaintainer.TICKS_PER_SECOND)
: String.valueOf(this.refreshRateTicks / (double) TileLevelMaintainer.TICKS_PER_SECOND));
this.refreshRate.dirty = false;
this.refreshRate.validate();
}

@Override
protected void actionPerformed(final GuiButton btn) {
for (Component com : this.component) {
Expand Down Expand Up @@ -448,6 +558,12 @@ private class Widget {
public final int componentIndex;
public final Action action;
public final FCGuiTextField textField;
/**
* Set while the player has typed into this field without submitting, so the server cannot overwrite the edit.
*/
public boolean dirty;
/** Colour of a valid value; invalid input is always drawn in the error colour. */
public int validTextColor = FCGuiColors.guiTextColorGray.getColor();
private final String tooltip;
private Long amount;

Expand All @@ -464,7 +580,10 @@ public Widget(FCGuiTextField textField, String tooltip, int componentIndex, Acti
public void draw() {
String current = amount != null
? StatCollector.translateToLocal(NameConst.TT_LEVEL_MAINTAINER_CURRENT) + " "
+ NumberFormat.getNumberInstance().format(amount)
+ (this.componentIndex < 0
? NumberFormat.getNumberInstance()
.format(Calculator.conversion(this.textField.getText()))
: NumberFormat.getNumberInstance().format(amount))
+ "\n"
: "";
if (isShiftKeyDown()) {
Expand Down Expand Up @@ -497,9 +616,12 @@ public void validate() {
this.textField.setTextColor(FCGuiColors.guiLevelMaintainerError.getColor());
} else {
this.amount = (long) ArithHelper.round(result, 0);
this.textField.setTextColor(FCGuiColors.guiTextColorGray.getColor());
this.textField.setTextColor(this.validTextColor);
}

// A negative index is the block level field: there is no request slot to write the amount back to.
if (this.componentIndex < 0) return;

IAEStack<?> stack = component[this.componentIndex].getStack();
if (stack != null) {
Long amount = component[this.componentIndex].getQty().getAmount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,21 @@ public void updateGui() {
new PacketVirtualSlot(StorageName.NONE, list),
(EntityPlayerMP) this.getInventoryPlayer().player);
}
FluidCraft.proxy.netHandler.sendTo(
new SPacketLevelMaintainerGuiUpdate(this.tile.requests, !this.isFirstUpdate, this.tile.isLiteMode()),
(EntityPlayerMP) this.getInventoryPlayer().player);
this.sendGuiUpdate((EntityPlayerMP) this.getInventoryPlayer().player, !this.isFirstUpdate);
this.isFirstUpdate = false;
this.updateCount = 0;
}

private void sendGuiUpdate(EntityPlayerMP player, boolean onlyState) {
FluidCraft.proxy.netHandler.sendTo(
new SPacketLevelMaintainerGuiUpdate(
this.tile.requests,
onlyState,
this.tile.isLiteMode(),
this.tile.getRefreshTicks()),
player);
}

@Override
public void receiveSlotStacks(StorageName invName, Int2ObjectMap<IAEStack<?>> slotStacks) {
for (var entry : slotStacks.int2ObjectEntrySet()) {
Expand All @@ -125,9 +133,7 @@ public void receiveSlotStacks(StorageName invName, Int2ObjectMap<IAEStack<?>> sl
for (var player : this.crafters) {
NetworkHandler.instance
.sendTo(new PacketVirtualSlot(StorageName.NONE, slotStacks), (EntityPlayerMP) player);
FluidCraft.proxy.netHandler.sendTo(
new SPacketLevelMaintainerGuiUpdate(this.tile.requests, false, this.tile.isLiteMode()),
(EntityPlayerMP) player);
this.sendGuiUpdate((EntityPlayerMP) player, false);
}
}
}
Expand Down
23 changes: 21 additions & 2 deletions src/main/java/com/glodblock/github/common/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class Config {
public static boolean replaceEC2;
public static int levelMaintainerMinTicks;
public static int levelMaintainerMaxTicks;
public static int levelMaintainerMinIdleTicks;
public static int levelMaintainerMaxRefreshTicks;
public static int reStockTime;
public static int magnetRange;

Expand Down Expand Up @@ -71,9 +73,26 @@ private static void loadProperty() {
true,
"Set true to handle missing item mappings from EC2. Note to work properly, you must have all relevant parts.");

levelMaintainerMinTicks = Config.get("LevelMaintainer", "minTick", 5, "Number on ticks for minimal request")
levelMaintainerMinTicks = Config
.get("LevelMaintainer", "minTick", 5, "Minimum interval between active checks, in ticks").getInt();
levelMaintainerMaxTicks = Config.get(
"LevelMaintainer",
"maxTick",
120,
"Default maximum idle interval, in ticks (120 = 6 seconds), used when a requester has no override. This is not an upper limit on player overrides.")
.getInt();
levelMaintainerMaxTicks = Config.get("LevelMaintainer", "maxTick", 120, "Number on ticks for maximal request")
levelMaintainerMinIdleTicks = Config.get(
"LevelMaintainer",
"minIdleTicks",
120,
"Smallest idle interval a player may set, in ticks (120 = 6 seconds). Overrides are rounded up to whole seconds and cannot be below minTick. Does not change the active minimum or the maxTick default.")
.getInt();
levelMaintainerMaxRefreshTicks = Config
.get(
"LevelMaintainer",
"maxRefreshTicks",
1728000,
"Largest idle interval a player may set on a requester, in ticks (1728000 = 24 hours)")
.getInt();

reStockTime = Config.get("UltraWireless", "reStockTime", 1000, "Time between restocks").getInt();
Expand Down
Loading