diff --git a/src/main/java/appeng/client/gui/AEBaseGui.java b/src/main/java/appeng/client/gui/AEBaseGui.java index 1b21d82fed5..f85e65ca389 100644 --- a/src/main/java/appeng/client/gui/AEBaseGui.java +++ b/src/main/java/appeng/client/gui/AEBaseGui.java @@ -796,7 +796,7 @@ public void bindTexture( final String base, final String file ) this.mc.getTextureManager().bindTexture( loc ); } - protected void drawItem( final int x, final int y, final ItemStack is ) + public void drawItem( final int x, final int y, final ItemStack is ) { this.zLevel = 100.0F; itemRender.zLevel = 100.0F; diff --git a/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java b/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java index 3d6f2b08331..cfa3fa08b84 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java +++ b/src/main/java/appeng/client/gui/implementations/GuiCraftConfirm.java @@ -24,8 +24,11 @@ import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.client.gui.AEBaseGui; +import appeng.client.gui.widgets.GuiCraftingCPUTable; import appeng.client.gui.widgets.GuiScrollbar; +import appeng.client.gui.widgets.ICraftingCPUTableHolder; import appeng.container.implementations.ContainerCraftConfirm; +import appeng.container.implementations.CraftingCPUStatus; import appeng.core.AELog; import appeng.core.localization.GuiText; import appeng.core.localization.GuiColors; @@ -55,10 +58,11 @@ import java.util.List; -public class GuiCraftConfirm extends AEBaseGui +public class GuiCraftConfirm extends AEBaseGui implements ICraftingCPUTableHolder { private final ContainerCraftConfirm ccc; + private final GuiCraftingCPUTable cpuTable; private final int rows = 5; @@ -84,6 +88,8 @@ public GuiCraftConfirm( final InventoryPlayer inventoryPlayer, final ITerminalHo final GuiScrollbar scrollbar = new GuiScrollbar(); this.setScrollBar( scrollbar ); + this.cpuTable = new GuiCraftingCPUTable( this, ((ContainerCraftConfirm) inventorySlots).cpuTable ); + this.ccc = (ContainerCraftConfirm) this.inventorySlots; if( te instanceof WirelessTerminalGuiObject ) @@ -112,7 +118,13 @@ public GuiCraftConfirm( final InventoryPlayer inventoryPlayer, final ITerminalHo } } - boolean isAutoStart() + @Override + public GuiCraftingCPUTable getCPUTable() + { + return cpuTable; + } + + boolean isAutoStart() { return ( (ContainerCraftConfirm) this.inventorySlots ).isAutoStart(); } @@ -143,7 +155,15 @@ public void drawScreen( final int mouseX, final int mouseY, final float btn ) { this.updateCPUButtonText(); + cpuTable.drawScreen( ); + this.start.enabled = !( this.ccc.hasNoCPU() || this.isSimulation() ); + if (this.start.enabled) { + CraftingCPUStatus selected = this.cpuTable.getContainer().getSelectedCPU(); + if (selected == null || selected.getStorage() < this.ccc.getUsedBytes() || selected.isBusy()) { + this.start.enabled = false; + } + } this.selectCPU.enabled = !this.isSimulation(); final int gx = ( this.width - this.xSize ) / 2; @@ -212,6 +232,8 @@ private boolean isSimulation() @Override public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) { + cpuTable.drawFG( offsetX, offsetY, mouseX, mouseY, guiLeft, guiTop ); + final long BytesUsed = this.ccc.getUsedBytes(); final String byteUsed = NumberFormat.getInstance().format( BytesUsed ); final String Add = BytesUsed > 0 ? ( byteUsed + ' ' + GuiText.BytesUsed.getLocal() ) : GuiText.CalculatingWait.getLocal(); @@ -369,6 +391,7 @@ public void drawFG( final int offsetX, final int offsetY, final int mouseX, fina @Override public void drawBG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) { + cpuTable.drawBG( offsetX, offsetY ); this.setScrollBar(); this.bindTexture( "guis/craftingreport.png" ); this.drawTexturedModalRect( offsetX, offsetY, 0, 0, this.xSize, this.ySize ); @@ -529,14 +552,7 @@ protected void actionPerformed( final GuiButton btn ) if( btn == this.selectCPU ) { - try - { - NetworkHandler.instance.sendToServer( new PacketValueConfig( "Terminal.Cpu", backwards ? "Prev" : "Next" ) ); - } - catch( final IOException e ) - { - AELog.debug( e ); - } + cpuTable.cycleCPU(backwards); } if( btn == this.cancel ) @@ -559,4 +575,33 @@ protected void actionPerformed( final GuiButton btn ) public ItemStack getHoveredStack() { return hoveredStack; } + + @Override + protected void mouseClicked( int xCoord, int yCoord, int btn ) + { + super.mouseClicked( xCoord, yCoord, btn ); + cpuTable.mouseClicked( xCoord - guiLeft, yCoord - guiTop, btn ); + } + + @Override + protected void mouseClickMove( int x, int y, int c, long d ) + { + super.mouseClickMove( x, y, c, d ); + cpuTable.mouseClickMove( x - guiLeft, y - guiTop ); + } + + @Override + public void handleMouseInput() + { + if ( cpuTable.handleMouseInput(guiLeft, guiTop) ) + { + return; + } + super.handleMouseInput(); + } + + public boolean hideItemPanelSlot( int x, int y, int w, int h ) + { + return cpuTable.hideItemPanelSlot(x - guiLeft, y - guiTop, w, h ); + } } diff --git a/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java b/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java index 3c3166a3685..e1c21b2d233 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java +++ b/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java @@ -27,52 +27,37 @@ import appeng.api.definitions.IDefinitions; import appeng.api.definitions.IParts; import appeng.api.storage.ITerminalHost; -import appeng.api.storage.data.IAEItemStack; -import appeng.client.gui.widgets.GuiScrollbar; +import appeng.client.gui.widgets.GuiCraftingCPUTable; import appeng.client.gui.widgets.GuiTabButton; +import appeng.client.gui.widgets.ICraftingCPUTableHolder; import appeng.container.implementations.ContainerCraftingStatus; import appeng.container.implementations.CraftingCPUStatus; -import appeng.core.AELog; import appeng.core.localization.GuiText; -import appeng.core.localization.GuiColors; import appeng.core.sync.GuiBridge; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketSwitchGuis; -import appeng.core.sync.packets.PacketValueConfig; import appeng.helpers.WirelessTerminalGuiObject; import appeng.parts.reporting.PartCraftingTerminal; import appeng.parts.reporting.PartPatternTerminal; import appeng.parts.reporting.PartPatternTerminalEx; import appeng.parts.reporting.PartTerminal; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.GL11; -import java.io.IOException; import java.util.List; -public class GuiCraftingStatus extends GuiCraftingCPU +public class GuiCraftingStatus extends GuiCraftingCPU implements ICraftingCPUTableHolder { - private static final int CPU_TABLE_WIDTH = 94; - private static final int CPU_TABLE_HEIGHT = 164; - private static final int CPU_TABLE_SLOT_XOFF = 100; - private static final int CPU_TABLE_SLOT_YOFF = 0; - private static final int CPU_TABLE_SLOT_WIDTH = 67; - private static final int CPU_TABLE_SLOT_HEIGHT = 23; - private final ContainerCraftingStatus status; private GuiButton selectCPU; - private GuiScrollbar cpuScrollbar; + private final GuiCraftingCPUTable cpuTable; private GuiTabButton originalGuiBtn; private GuiBridge originalGui; private ItemStack myIcon = null; - private String selectedCPUName = ""; public GuiCraftingStatus( final InventoryPlayer inventoryPlayer, final ITerminalHost te ) { @@ -83,6 +68,8 @@ public GuiCraftingStatus( final InventoryPlayer inventoryPlayer, final ITerminal final IDefinitions definitions = AEApi.instance().definitions(); final IParts parts = definitions.parts(); + cpuTable = new GuiCraftingCPUTable(this, this.status.getCPUTable() ); + if( target instanceof WirelessTerminalGuiObject ) { for( final ItemStack wirelessTerminalStack : definitions.items().wirelessTerminal().maybeStack( 1 ).asSet() ) @@ -129,13 +116,24 @@ public GuiCraftingStatus( final InventoryPlayer inventoryPlayer, final ITerminal } } - @Override + @Override + public GuiCraftingCPUTable getCPUTable() + { + return cpuTable; + } + + @Override protected void actionPerformed( final GuiButton btn ) { super.actionPerformed( btn ); final boolean backwards = Mouse.isButtonDown( 1 ); + if( btn == this.selectCPU ) + { + cpuTable.cycleCPU(backwards); + } + if( btn == this.originalGuiBtn ) { NetworkHandler.instance.sendToServer( new PacketSwitchGuis( this.originalGui ) ); @@ -148,15 +146,8 @@ public void initGui() super.initGui(); this.selectCPU = new GuiButton( 0, this.guiLeft + 8, this.guiTop + this.ySize - 25, 150, 20, GuiText.CraftingCPU.getLocal() + ": " + GuiText.NoCraftingCPUs ); - this.selectCPU.enabled = false; this.buttonList.add( this.selectCPU ); - this.cpuScrollbar = new GuiScrollbar(); - this.cpuScrollbar.setLeft( -16 ); - this.cpuScrollbar.setTop( 19 ); - this.cpuScrollbar.setWidth( 12 ); - this.cpuScrollbar.setHeight( 137 ); - if( this.myIcon != null ) { this.buttonList.add( this.originalGuiBtn = new GuiTabButton( this.guiLeft + 213, this.guiTop - 4, this.myIcon, this.myIcon.getDisplayName(), itemRender ) ); @@ -167,16 +158,7 @@ public void initGui() @Override public void drawScreen( final int mouseX, final int mouseY, final float btn ) { - List cpus = this.status.getCPUs(); - this.selectedCPUName = null; - this.cpuScrollbar.setRange( 0, Integer.max(0, cpus.size() - 6), 1 ); - for (CraftingCPUStatus cpu : cpus) - { - if (cpu.getSerial() == this.status.selectedCpuSerial) - { - this.selectedCPUName = cpu.getName(); - } - } + this.cpuTable.drawScreen( ); this.updateCPUButtonText(); super.drawScreen( mouseX, mouseY, btn ); } @@ -185,260 +167,60 @@ public void drawScreen( final int mouseX, final int mouseY, final float btn ) public void drawFG( int offsetX, int offsetY, int mouseX, int mouseY ) { super.drawFG( offsetX, offsetY, mouseX, mouseY ); - if (this.cpuScrollbar != null) - { - this.cpuScrollbar.draw( this ); - } - - List cpus = this.status.getCPUs(); - final int firstCpu = this.cpuScrollbar.getCurrentScroll(); - CraftingCPUStatus hoveredCpu = hitCpu( mouseX, mouseY ); - { - FontRenderer font = Minecraft.getMinecraft().fontRenderer; - for( int i = firstCpu; i < firstCpu + 6; i++ ) - { - if( i < 0 || i >= cpus.size() ) - { - continue; - } - CraftingCPUStatus cpu = cpus.get( i ); - if( cpu == null ) - { - continue; - } - int x = -CPU_TABLE_WIDTH + 9; - int y = 19 + ( i - firstCpu ) * CPU_TABLE_SLOT_HEIGHT; - if( cpu.getSerial() == this.status.selectedCpuSerial ) - { - GL11.glColor4f( 0.0F, 0.8352F, 1.0F, 1.0F ); - } - else if( hoveredCpu != null && hoveredCpu.getSerial() == cpu.getSerial() ) - { - GL11.glColor4f( 0.65F, 0.9F, 1.0F, 1.0F ); - } else - { - GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); - } - this.bindTexture( "guis/cpu_selector.png" ); - this.drawTexturedModalRect( x, y, CPU_TABLE_SLOT_XOFF, CPU_TABLE_SLOT_YOFF, CPU_TABLE_SLOT_WIDTH, CPU_TABLE_SLOT_HEIGHT ); - GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); - - String name = cpu.getName(); - if( name == null || name.isEmpty() ) - { - name = GuiText.CPUs.getLocal() + " #" + cpu.getSerial(); - } - if( name.length() > 12 ) - { - name = name.substring( 0, 11 ) + ".."; - } - GL11.glPushMatrix(); - GL11.glTranslatef( x + 3, y + 3, 0 ); - GL11.glScalef( 0.8f, 0.8f, 1.0f ); - font.drawString( name, 0, 0, GuiColors.CraftingStatusCPUName.getColor() ); - GL11.glPopMatrix(); - - GL11.glPushMatrix(); - GL11.glTranslatef( x + 3, y + 11, 0 ); - final IAEItemStack craftingStack = cpu.getCrafting(); - if( craftingStack != null ) - { - final int iconIndex = 16 * 11 + 2; - this.bindTexture( "guis/states.png" ); - final int uv_y = iconIndex / 16; - final int uv_x = iconIndex - uv_y * 16; - - GL11.glScalef( 0.5f, 0.5f, 1.0f ); - GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); - this.drawTexturedModalRect( 0, 0, uv_x * 16, uv_y * 16, 16, 16 ); - GL11.glTranslatef( 18.0f, 2.0f, 0.0f ); - String amount = Long.toString( craftingStack.getStackSize() ); - if( amount.length() > 5 ) - { - amount = amount.substring( 0, 5 ) + ".."; - } - GL11.glScalef( 1.5f, 1.5f, 1.0f ); - font.drawString( amount, 0, 0, GuiColors.CraftingStatusCPUAmount.getColor() ); - GL11.glPopMatrix(); - GL11.glPushMatrix(); - GL11.glTranslatef( x + CPU_TABLE_SLOT_WIDTH - 19, y + 3, 0 ); - this.drawItem( 0, 0, craftingStack.getItemStack() ); - } - else - { - final int iconIndex = 16 * 4 + 3; - this.bindTexture( "guis/states.png" ); - final int uv_y = iconIndex / 16; - final int uv_x = iconIndex - uv_y * 16; - - GL11.glScalef( 0.5f, 0.5f, 1.0f ); - GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); - this.drawTexturedModalRect( 0, 0, uv_x * 16, uv_y * 16, 16, 16 ); - GL11.glTranslatef( 18.0f, 2.0f, 0.0f ); - GL11.glScalef( 1.5f, 1.5f, 1.0f ); - font.drawString( cpu.formatStorage(), 0, 0, GuiColors.CraftingStatusCPUStorage.getColor() ); - } - GL11.glPopMatrix(); - } - GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); - } - if( hoveredCpu != null ) - { - StringBuilder tooltip = new StringBuilder(); - String name = hoveredCpu.getName(); - if( name != null && !name.isEmpty() ) - { - tooltip.append( name ); - tooltip.append( '\n' ); - } - else - { - tooltip.append ( GuiText.CPUs.getLocal() ); - tooltip.append ( " #" ); - tooltip.append ( hoveredCpu.getSerial() ); - tooltip.append ( '\n' ); - } - IAEItemStack crafting = hoveredCpu.getCrafting(); - if( crafting != null && crafting.getStackSize() > 0 ) - { - tooltip.append( GuiText.Crafting.getLocal() ); - tooltip.append( ": " ); - tooltip.append( crafting.getStackSize() ); - tooltip.append( ' ' ); - tooltip.append( crafting.getItemStack().getDisplayName() ); - tooltip.append( '\n' ); - tooltip.append( hoveredCpu.getRemainingItems() ); - tooltip.append( " / " ); - tooltip.append( hoveredCpu.getTotalItems() ); - tooltip.append( '\n' ); - } - if ( hoveredCpu.getStorage() > 0 ) - { - tooltip.append( GuiText.Bytes.getLocal() ); - tooltip.append( ": " ); - tooltip.append( hoveredCpu.formatStorage() ); - tooltip.append( '\n' ); - } - if ( hoveredCpu.getCoprocessors() > 0 ) - { - tooltip.append( GuiText.CoProcessors.getLocal() ); - tooltip.append( ": " ); - tooltip.append( hoveredCpu.getCoprocessors() ); - tooltip.append( '\n' ); - } - if (tooltip.length() > 0) - { - this.drawTooltip( mouseX - offsetX, mouseY - offsetY, 0, tooltip.toString() ); - } - } + this.cpuTable.drawFG( offsetX, offsetY, mouseX, mouseY, guiLeft, guiTop ); } @Override public void drawBG( int offsetX, int offsetY, int mouseX, int mouseY ) { super.drawBG( offsetX, offsetY, mouseX, mouseY ); - this.bindTexture( "guis/cpu_selector.png" ); - this.drawTexturedModalRect( offsetX - CPU_TABLE_WIDTH, offsetY, 0, 0, CPU_TABLE_WIDTH, CPU_TABLE_HEIGHT ); + this.cpuTable.drawBG( offsetX, offsetY ); } @Override protected void mouseClicked( int xCoord, int yCoord, int btn ) { super.mouseClicked( xCoord, yCoord, btn ); - if( cpuScrollbar != null ) - { - cpuScrollbar.click( this, xCoord - this.guiLeft, yCoord - this.guiTop ); - } - CraftingCPUStatus hit = hitCpu( xCoord, yCoord ); - if (hit != null) - { - try - { - NetworkHandler.instance.sendToServer( new PacketValueConfig( "Terminal.Cpu.Set", Integer.toString( hit.getSerial() ) ) ); - } - catch( final IOException e ) - { - AELog.debug( e ); - } - } + cpuTable.mouseClicked( xCoord - guiLeft, yCoord - guiTop, btn ); } @Override protected void mouseClickMove( int x, int y, int c, long d ) { super.mouseClickMove( x, y, c, d ); - if( cpuScrollbar != null ) - { - cpuScrollbar.click( this, x - this.guiLeft, y - this.guiTop ); - } + cpuTable.mouseClickMove( x - guiLeft, y - guiTop ); } @Override public void handleMouseInput() { - int x = Mouse.getEventX() * this.width / this.mc.displayWidth; - int y = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; - x -= guiLeft - CPU_TABLE_WIDTH; - y -= guiTop; - int dwheel = Mouse.getEventDWheel(); - if (x >= 9 && x < CPU_TABLE_SLOT_WIDTH + 9 && y >= 19 && y < 19 + 6 * CPU_TABLE_SLOT_HEIGHT) - { - if (this.cpuScrollbar != null && dwheel != 0) - { - this.cpuScrollbar.wheel( dwheel ); - return; - } + if ( cpuTable.handleMouseInput(guiLeft, guiTop) ) { + return; } super.handleMouseInput(); } public boolean hideItemPanelSlot( int x, int y, int w, int h ) { - x -= guiLeft - CPU_TABLE_WIDTH; - y -= guiTop; - boolean xInside = - ( x >= 0 && x < CPU_TABLE_SLOT_WIDTH + 9 ) - || ( x + w >= 0 && x + w < CPU_TABLE_SLOT_WIDTH + 9 ) - || ( x <= 0 && x + w >= CPU_TABLE_SLOT_WIDTH + 9 ); - boolean yInside = - ( y >= 0 && y < 19 + 6 * CPU_TABLE_SLOT_HEIGHT ) - || ( y + h >= 0 && y + h < 19 + 6 * CPU_TABLE_SLOT_HEIGHT ) - || ( y < 0 && y + h >= 19 + 6 * CPU_TABLE_SLOT_HEIGHT ); - if( xInside && yInside ) - { - return true; - } - return false; - } - - private CraftingCPUStatus hitCpu( int x, int y ) - { - x -= guiLeft - CPU_TABLE_WIDTH; - y -= guiTop; - if (!(x >= 9 && x < CPU_TABLE_SLOT_WIDTH + 9 && y >= 19 && y < 19 + 6 * CPU_TABLE_SLOT_HEIGHT)) - { - return null; - } - int scrollOffset = this.cpuScrollbar != null ? this.cpuScrollbar.getCurrentScroll() : 0; - int cpuId = scrollOffset + (y - 19) / CPU_TABLE_SLOT_HEIGHT; - List cpus = this.status.getCPUs(); - return (cpuId >= 0 && cpuId < cpus.size()) ? cpus.get(cpuId) : null; + return cpuTable.hideItemPanelSlot(x - guiLeft, y - guiTop, w, h ); } private void updateCPUButtonText() { String btnTextText = GuiText.NoCraftingJobs.getLocal(); - if( this.status.selectedCpuSerial >= 0 ) + final int selectedSerial = this.cpuTable.getContainer().selectedCpuSerial; + if( selectedSerial >= 0 ) { - if( this.selectedCPUName != null && this.selectedCPUName.length() > 0 ) + String selectedCPUName = cpuTable.getSelectedCPUName(); + if( selectedCPUName != null && selectedCPUName.length() > 0 ) { - final String name = this.selectedCPUName.substring( 0, Math.min( 20, this.selectedCPUName.length() ) ); + final String name = selectedCPUName.substring( 0, Math.min( 20, selectedCPUName.length() ) ); btnTextText = GuiText.CPUs.getLocal() + ": " + name; } else { - btnTextText = GuiText.CPUs.getLocal() + ": #" + this.status.selectedCpuSerial; + btnTextText = GuiText.CPUs.getLocal() + ": #" + selectedSerial; } } @@ -455,9 +237,4 @@ protected String getGuiDisplayName( final String in ) { return in; // the cup name is on the button } - - public void postCPUUpdate( CraftingCPUStatus[] cpus ) - { - this.status.postCPUUpdate(cpus); - } } diff --git a/src/main/java/appeng/client/gui/widgets/GuiCraftingCPUTable.java b/src/main/java/appeng/client/gui/widgets/GuiCraftingCPUTable.java new file mode 100644 index 00000000000..62640cbac37 --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/GuiCraftingCPUTable.java @@ -0,0 +1,364 @@ +package appeng.client.gui.widgets; + +import appeng.api.storage.data.IAEItemStack; +import appeng.client.gui.AEBaseGui; +import appeng.container.implementations.ContainerCPUTable; +import appeng.container.implementations.CraftingCPUStatus; +import appeng.core.AELog; +import appeng.core.localization.GuiColors; +import appeng.core.localization.GuiText; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketValueConfig; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.io.IOException; +import java.util.List; + +public class GuiCraftingCPUTable +{ + private final AEBaseGui parent; + private final ContainerCPUTable container; + + public static final int CPU_TABLE_WIDTH = 94; + public static final int CPU_TABLE_HEIGHT = 164; + public static final int CPU_TABLE_SLOTS = 6; + public static final int CPU_TABLE_SLOT_XOFF = 100; + public static final int CPU_TABLE_SLOT_YOFF = 0; + public static final int CPU_TABLE_SLOT_WIDTH = 67; + public static final int CPU_TABLE_SLOT_HEIGHT = 23; + + private final GuiScrollbar cpuScrollbar; + + private String selectedCPUName = ""; + + public GuiCraftingCPUTable( AEBaseGui parent, ContainerCPUTable container ) + { + this.parent = parent; + this.container = container; + this.cpuScrollbar = new GuiScrollbar(); + this.cpuScrollbar.setLeft( -16 ); + this.cpuScrollbar.setTop( 19 ); + this.cpuScrollbar.setWidth( 12 ); + this.cpuScrollbar.setHeight( 137 ); + } + + public ContainerCPUTable getContainer() + { + return container; + } + + public String getSelectedCPUName() + { + return selectedCPUName; + } + + public void drawScreen( ) + { + final List cpus = container.getCPUs(); + final int selectedCpuSerial = container.selectedCpuSerial; + + this.selectedCPUName = null; + this.cpuScrollbar.setRange( 0, Integer.max( 0, cpus.size() - CPU_TABLE_SLOTS ), 1 ); + for( CraftingCPUStatus cpu : cpus ) + { + if( cpu.getSerial() == selectedCpuSerial ) + { + this.selectedCPUName = cpu.getName(); + } + } + } + + public void drawFG( int offsetX, int offsetY, int mouseX, int mouseY, int guiLeft, int guiTop ) + { + if( this.cpuScrollbar != null ) + { + this.cpuScrollbar.draw( parent ); + } + final List cpus = container.getCPUs(); + final int selectedCpuSerial = container.selectedCpuSerial; + final int firstCpu = this.cpuScrollbar.getCurrentScroll(); + CraftingCPUStatus hoveredCpu = hitCpu( mouseX - guiLeft, mouseY - guiTop ); + { + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + for( int i = firstCpu; i < firstCpu + CPU_TABLE_SLOTS; i++ ) + { + if( i < 0 || i >= cpus.size() ) + { + continue; + } + CraftingCPUStatus cpu = cpus.get( i ); + if( cpu == null ) + { + continue; + } + int x = -CPU_TABLE_WIDTH + 9; + int y = 19 + ( i - firstCpu ) * CPU_TABLE_SLOT_HEIGHT; + if( cpu.getSerial() == selectedCpuSerial ) + { + if( !container.getCpuFilter().test( cpu ) ) + { + GL11.glColor4f( 1.0F, 0.25F, 0.25F, 1.0F ); + } else + { + GL11.glColor4f( 0.0F, 0.8352F, 1.0F, 1.0F ); + } + } else if( hoveredCpu != null && hoveredCpu.getSerial() == cpu.getSerial() ) + { + GL11.glColor4f( 0.65F, 0.9F, 1.0F, 1.0F ); + } else if( !container.getCpuFilter().test( cpu ) ) + { + GL11.glColor4f( 0.9F, 0.65F, 0.65F, 1.0F ); + } else + { + GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); + } + parent.bindTexture( "guis/cpu_selector.png" ); + parent.drawTexturedModalRect( x, y, CPU_TABLE_SLOT_XOFF, CPU_TABLE_SLOT_YOFF, CPU_TABLE_SLOT_WIDTH, CPU_TABLE_SLOT_HEIGHT ); + GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); + + String name = cpu.getName(); + if( name == null || name.isEmpty() ) + { + name = GuiText.CPUs.getLocal() + " #" + cpu.getSerial(); + } + if( name.length() > 12 ) + { + name = name.substring( 0, 11 ) + ".."; + } + GL11.glPushMatrix(); + GL11.glTranslatef( x + 3, y + 3, 0 ); + GL11.glScalef( 0.8f, 0.8f, 1.0f ); + font.drawString( name, 0, 0, GuiColors.CraftingStatusCPUName.getColor() ); + GL11.glPopMatrix(); + + GL11.glPushMatrix(); + GL11.glTranslatef( x + 3, y + 11, 0 ); + final IAEItemStack craftingStack = cpu.getCrafting(); + if( craftingStack != null ) + { + final int iconIndex = 16 * 11 + 2; + parent.bindTexture( "guis/states.png" ); + final int uv_y = iconIndex / 16; + final int uv_x = iconIndex - uv_y * 16; + + GL11.glScalef( 0.5f, 0.5f, 1.0f ); + GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); + parent.drawTexturedModalRect( 0, 0, uv_x * 16, uv_y * 16, 16, 16 ); + GL11.glTranslatef( 18.0f, 2.0f, 0.0f ); + String amount = Long.toString( craftingStack.getStackSize() ); + if( amount.length() > 5 ) + { + amount = amount.substring( 0, 5 ) + ".."; + } + GL11.glScalef( 1.5f, 1.5f, 1.0f ); + font.drawString( amount, 0, 0, GuiColors.CraftingStatusCPUAmount.getColor() ); + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glTranslatef( x + CPU_TABLE_SLOT_WIDTH - 19, y + 3, 0 ); + parent.drawItem( 0, 0, craftingStack.getItemStack() ); + } else + { + final int iconIndex = 16 * 4 + 3; + parent.bindTexture( "guis/states.png" ); + final int uv_y = iconIndex / 16; + final int uv_x = iconIndex - uv_y * 16; + + GL11.glScalef( 0.5f, 0.5f, 1.0f ); + GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); + parent.drawTexturedModalRect( 0, 0, uv_x * 16, uv_y * 16, 16, 16 ); + GL11.glTranslatef( 18.0f, 2.0f, 0.0f ); + GL11.glScalef( 1.5f, 1.5f, 1.0f ); + font.drawString( cpu.formatStorage(), 0, 0, GuiColors.CraftingStatusCPUStorage.getColor() ); + } + GL11.glPopMatrix(); + } + GL11.glColor4f( 1.0F, 1.0F, 1.0F, 1.0F ); + } + if( hoveredCpu != null ) + { + StringBuilder tooltip = new StringBuilder(); + String name = hoveredCpu.getName(); + if( name != null && !name.isEmpty() ) + { + tooltip.append( name ); + tooltip.append( '\n' ); + } else + { + tooltip.append( GuiText.CPUs.getLocal() ); + tooltip.append( " #" ); + tooltip.append( hoveredCpu.getSerial() ); + tooltip.append( '\n' ); + } + IAEItemStack crafting = hoveredCpu.getCrafting(); + if( crafting != null && crafting.getStackSize() > 0 ) + { + tooltip.append( GuiText.Crafting.getLocal() ); + tooltip.append( ": " ); + tooltip.append( crafting.getStackSize() ); + tooltip.append( ' ' ); + tooltip.append( crafting.getItemStack().getDisplayName() ); + tooltip.append( '\n' ); + tooltip.append( hoveredCpu.getRemainingItems() ); + tooltip.append( " / " ); + tooltip.append( hoveredCpu.getTotalItems() ); + tooltip.append( '\n' ); + } + if( hoveredCpu.getStorage() > 0 ) + { + tooltip.append( GuiText.Bytes.getLocal() ); + tooltip.append( ": " ); + tooltip.append( hoveredCpu.formatStorage() ); + tooltip.append( '\n' ); + } + if( hoveredCpu.getCoprocessors() > 0 ) + { + tooltip.append( GuiText.CoProcessors.getLocal() ); + tooltip.append( ": " ); + tooltip.append( hoveredCpu.getCoprocessors() ); + tooltip.append( '\n' ); + } + if( tooltip.length() > 0 ) + { + parent.drawTooltip( mouseX - offsetX, mouseY - offsetY, 0, tooltip.toString() ); + } + } + } + + public void drawBG( int offsetX, int offsetY ) + { + parent.bindTexture( "guis/cpu_selector.png" ); + parent.drawTexturedModalRect( offsetX - CPU_TABLE_WIDTH, offsetY, 0, 0, CPU_TABLE_WIDTH, CPU_TABLE_HEIGHT ); + } + + /** + * Tests if a cpu button is under the cursor. Subtract guiLeft, guiTop from x, y before calling + */ + public CraftingCPUStatus hitCpu( int x, int y ) + { + x -= -CPU_TABLE_WIDTH; + if( !( x >= 9 && x < CPU_TABLE_SLOT_WIDTH + 9 && y >= 19 && y < 19 + CPU_TABLE_SLOTS * CPU_TABLE_SLOT_HEIGHT ) ) + { + return null; + } + int scrollOffset = this.cpuScrollbar != null ? this.cpuScrollbar.getCurrentScroll() : 0; + int cpuId = scrollOffset + ( y - 19 ) / CPU_TABLE_SLOT_HEIGHT; + List cpus = container.getCPUs(); + return ( cpuId >= 0 && cpuId < cpus.size() ) ? cpus.get( cpuId ) : null; + } + + /** + * Subtract guiLeft, guiTop from x, y before calling + */ + public void mouseClicked( int xCoord, int yCoord, int btn ) + { + if( cpuScrollbar != null ) + { + cpuScrollbar.click( parent, xCoord, yCoord ); + } + CraftingCPUStatus hit = hitCpu( xCoord, yCoord ); + if( hit != null ) + { + sendCPUSwitch( hit.getSerial() ); + } + } + + public void sendCPUSwitch( int serial ) + { + try + { + NetworkHandler.instance.sendToServer( new PacketValueConfig( "CPUTable.Cpu.Set", Integer.toString( serial ) ) ); + } catch( final IOException e ) + { + AELog.warn( e ); + } + } + + /** + * Subtract guiLeft, guiTop from x, y before calling + */ + public void mouseClickMove( int xCoord, int yCoord ) + { + if( cpuScrollbar != null ) + { + cpuScrollbar.click( parent, xCoord, yCoord ); + } + } + + /** + * @return True if event was handled + */ + public boolean handleMouseInput( int guiLeft, int guiTop ) + { + int x = Mouse.getEventX() * parent.width / parent.mc.displayWidth; + int y = parent.height - Mouse.getEventY() * parent.height / parent.mc.displayHeight - 1; + x -= guiLeft - CPU_TABLE_WIDTH; + y -= guiTop; + int dwheel = Mouse.getEventDWheel(); + if( x >= 9 && x < CPU_TABLE_SLOT_WIDTH + 9 && y >= 19 && y < 19 + CPU_TABLE_SLOTS * CPU_TABLE_SLOT_HEIGHT ) + { + if( this.cpuScrollbar != null && dwheel != 0 ) + { + this.cpuScrollbar.wheel( dwheel ); + return true; + } + } + return false; + } + + public boolean hideItemPanelSlot( int x, int y, int w, int h ) + { + x += CPU_TABLE_WIDTH; + boolean xInside = + ( x >= 0 && x < CPU_TABLE_SLOT_WIDTH + 9 ) + || ( x + w >= 0 && x + w < CPU_TABLE_SLOT_WIDTH + 9 ) + || ( x <= 0 && x + w >= CPU_TABLE_SLOT_WIDTH + 9 ); + boolean yInside = + ( y >= 0 && y < 19 + CPU_TABLE_SLOTS * CPU_TABLE_SLOT_HEIGHT ) + || ( y + h >= 0 && y + h < 19 + CPU_TABLE_SLOTS * CPU_TABLE_SLOT_HEIGHT ) + || ( y < 0 && y + h >= 19 + CPU_TABLE_SLOTS * CPU_TABLE_SLOT_HEIGHT ); + return xInside && yInside; + } + + public void cycleCPU(boolean backwards) + { + int current = container.selectedCpuSerial; + List cpus = container.getCPUs(); + final int next_increment = backwards ? (cpus.size() - 1) : 1; + if( cpus.isEmpty() ) + { + return; + } + int next = 0; + for( int i = 0; i < cpus.size(); i++ ) + { + if( cpus.get( i ).getSerial() == current ) + { + next = i + next_increment; + break; + } + } + final boolean preferBusy = container.isBusyCPUsPreferred(); + for (int i = 0; i < cpus.size(); i++) + { + next = next % cpus.size(); + CraftingCPUStatus cpu = cpus.get(next); + if (cpu.isBusy() == preferBusy && container.getCpuFilter().test(cpu)) + { + break; + } + else + { + next += next_increment; + } + } + next = next % cpus.size(); + sendCPUSwitch( cpus.get( next ).getSerial() ); + if( next < cpuScrollbar.getCurrentScroll() || next >= cpuScrollbar.getCurrentScroll() + CPU_TABLE_SLOTS ) + { + cpuScrollbar.setCurrentScroll( next ); + } + } +} diff --git a/src/main/java/appeng/client/gui/widgets/ICraftingCPUTableHolder.java b/src/main/java/appeng/client/gui/widgets/ICraftingCPUTableHolder.java new file mode 100644 index 00000000000..aaa4de377ff --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/ICraftingCPUTableHolder.java @@ -0,0 +1,6 @@ +package appeng.client.gui.widgets; + +public interface ICraftingCPUTableHolder +{ + GuiCraftingCPUTable getCPUTable(); +} diff --git a/src/main/java/appeng/container/AEBaseContainer.java b/src/main/java/appeng/container/AEBaseContainer.java index b7a39710702..c69a74564f0 100644 --- a/src/main/java/appeng/container/AEBaseContainer.java +++ b/src/main/java/appeng/container/AEBaseContainer.java @@ -62,6 +62,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraftforge.common.util.ForgeDirection; +import org.apache.commons.lang3.ArrayUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -127,23 +128,33 @@ protected IActionHost getActionHost() private void prepareSync() { - for( final Field f : this.getClass().getFields() ) - { - if( f.isAnnotationPresent( GuiSync.class ) ) - { - final GuiSync annotation = f.getAnnotation( GuiSync.class ); - if( this.syncData.containsKey( annotation.value() ) ) - { - AELog.warn( "Channel already in use: " + annotation.value() + " for " + f.getName() ); - } - else - { - this.syncData.put( annotation.value(), new SyncData( this, f, annotation ) ); - } - } - } + walkSyncFields( 0, this.getClass().getFields(), new Field[0] ); } + private void walkSyncFields(int offset, final Field[] fields, final Field[] currentIndirections) { + for( final Field f : fields ) + { + if (f.isAnnotationPresent( GuiSync.Recurse.class )) + { + final GuiSync.Recurse annotation = f.getAnnotation( GuiSync.Recurse.class ); + walkSyncFields( offset + annotation.value(), f.getType().getFields(), ArrayUtils.add( currentIndirections, f ) ); + } + if( f.isAnnotationPresent( GuiSync.class ) ) + { + final GuiSync annotation = f.getAnnotation( GuiSync.class ); + final int channel = offset + annotation.value(); + if( this.syncData.containsKey( channel ) ) + { + AELog.warn( "Channel already in use: " + channel + " for " + f.getName() ); + } + else + { + this.syncData.put( channel, new SyncData( this, currentIndirections, f, channel ) ); + } + } + } + } + public AEBaseContainer( final InventoryPlayer ip, final Object anchor ) { this.invPlayer = ip; diff --git a/src/main/java/appeng/container/guisync/GuiSync.java b/src/main/java/appeng/container/guisync/GuiSync.java index 14d6e922f90..50d70a74322 100644 --- a/src/main/java/appeng/container/guisync/GuiSync.java +++ b/src/main/java/appeng/container/guisync/GuiSync.java @@ -29,10 +29,20 @@ * Annotates that this field should be synchronized between the server and client. * Requires the field to be public. */ -@Retention( RetentionPolicy.RUNTIME ) -@Target( ElementType.FIELD ) +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) public @interface GuiSync { - int value(); + int value(); + + /** + * Recurse into the class in search of more @GuiSync-ed values. The child IDs are offset by the value. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public static @interface Recurse + { + int value(); + } } diff --git a/src/main/java/appeng/container/guisync/SyncData.java b/src/main/java/appeng/container/guisync/SyncData.java index 5928e7e24e1..9ddf6a3def4 100644 --- a/src/main/java/appeng/container/guisync/SyncData.java +++ b/src/main/java/appeng/container/guisync/SyncData.java @@ -35,175 +35,190 @@ public class SyncData { - private final AEBaseContainer source; - private final Field field; - private final int channel; - private Object clientVersion; - - public SyncData( final AEBaseContainer container, final Field field, final GuiSync annotation ) - { - this.clientVersion = null; - this.source = container; - this.field = field; - this.channel = annotation.value(); - } - - public int getChannel() - { - return this.channel; - } - - public void tick( final ICrafting c ) - { - try - { - final Object val = this.field.get( this.source ); - if( val != null && this.clientVersion == null ) - { - this.send( c, val ); - } - else if( !val.equals( this.clientVersion ) ) - { - this.send( c, val ); - } - } - catch( final IllegalArgumentException e ) - { - AELog.debug( e ); - } - catch( final IllegalAccessException e ) - { - AELog.debug( e ); - } - catch( final IOException e ) - { - AELog.debug( e ); - } - } - - private void send( final ICrafting o, final Object val ) throws IOException - { - if( val instanceof String ) - { - if( o instanceof EntityPlayerMP ) - { - NetworkHandler.instance.sendTo( new PacketValueConfig( "SyncDat." + this.channel, (String) val ), (EntityPlayerMP) o ); - } - } - else if( this.field.getType().isEnum() ) - { - o.sendProgressBarUpdate( this.source, this.channel, ( (Enum) val ).ordinal() ); - } - else if( val instanceof Long || val.getClass() == long.class ) - { - NetworkHandler.instance.sendTo( new PacketProgressBar( this.channel, (Long) val ), (EntityPlayerMP) o ); - } - else if( val instanceof Boolean || val.getClass() == boolean.class ) - { - o.sendProgressBarUpdate( this.source, this.channel, ( (Boolean) val ) ? 1 : 0 ); - } - else - { - o.sendProgressBarUpdate( this.source, this.channel, (Integer) val ); - } - - this.clientVersion = val; - } - - public void update( final Object val ) - { - try - { - final Object oldValue = this.field.get( this.source ); - if( val instanceof String ) - { - this.updateString( oldValue, (String) val ); - } - else - { - this.updateValue( oldValue, (Long) val ); - } - } - catch( final IllegalArgumentException e ) - { - AELog.debug( e ); - } - catch( final IllegalAccessException e ) - { - AELog.debug( e ); - } - } - - private void updateString( final Object oldValue, final String val ) - { - try - { - this.field.set( this.source, val ); - this.source.onUpdate( this.field.getName(), oldValue, this.field.get( this.source ) ); - } - catch( final IllegalArgumentException e ) - { - AELog.debug( e ); - } - catch( final IllegalAccessException e ) - { - AELog.debug( e ); - } - } - - private void updateValue( final Object oldValue, final long val ) - { - try - { - if( this.field.getType().isEnum() ) - { - final EnumSet valList = EnumSet.allOf( (Class) this.field.getType() ); - for( final Enum e : valList ) - { - if( e.ordinal() == val ) - { - this.field.set( this.source, e ); - break; - } - } - } - else - { - if( this.field.getType().equals( int.class ) ) - { - this.field.set( this.source, (int) val ); - } - else if( this.field.getType().equals( long.class ) ) - { - this.field.set( this.source, val ); - } - else if( this.field.getType().equals( boolean.class ) ) - { - this.field.set( this.source, val == 1 ); - } - else if( this.field.getType().equals( Integer.class ) ) - { - this.field.set( this.source, (int) val ); - } - else if( this.field.getType().equals( Long.class ) ) - { - this.field.set( this.source, val ); - } - else if( this.field.getType().equals( Boolean.class ) ) - { - this.field.set( this.source, val == 1 ); - } - } - - this.source.onUpdate( this.field.getName(), oldValue, this.field.get( this.source ) ); - } - catch( final IllegalArgumentException e ) - { - AELog.debug( e ); - } - catch( final IllegalAccessException e ) - { - AELog.debug( e ); - } - } + private final AEBaseContainer source; + private final Field[] indirections; + private final Field field; + private final String fieldName; + private final int channel; + private Object clientVersion; + + public SyncData( final AEBaseContainer container, final Field field, final GuiSync annotation ) + { + this( container, new Field[0], field, annotation.value() ); + } + + public SyncData( final AEBaseContainer container, final Field[] indirections, final Field field, final int channel ) + { + this.clientVersion = null; + this.source = container; + this.indirections = indirections; + this.field = field; + this.channel = channel; + StringBuilder nameBuilder = new StringBuilder(); + for( Field indirection : this.indirections ) + { + nameBuilder.append( indirection.getName() ); + nameBuilder.append( '.' ); + } + nameBuilder.append( this.field.getName() ); + this.fieldName = nameBuilder.toString(); + } + + public int getChannel() + { + return this.channel; + } + + private Object getValue() throws IllegalAccessException + { + Object currentObject = source; + for( Field indirection : indirections ) + { + currentObject = indirection.get( currentObject ); + } + return field.get( currentObject ); + } + + private void setValue( Object newVal ) throws IllegalAccessException + { + Object currentObject = source; + for( Field indirection : indirections ) + { + currentObject = indirection.get( currentObject ); + } + field.set( currentObject, newVal ); + } + + public void tick( final ICrafting c ) + { + try + { + final Object val = getValue(); + if( val != null && this.clientVersion == null ) + { + this.send( c, val ); + } else if( !val.equals( this.clientVersion ) ) + { + this.send( c, val ); + } + } catch( final IllegalArgumentException e ) + { + AELog.debug( e ); + } catch( final IllegalAccessException e ) + { + AELog.debug( e ); + } catch( final IOException e ) + { + AELog.debug( e ); + } + } + + private void send( final ICrafting o, final Object val ) throws IOException + { + if( val instanceof String ) + { + if( o instanceof EntityPlayerMP ) + { + NetworkHandler.instance.sendTo( new PacketValueConfig( "SyncDat." + this.channel, (String) val ), (EntityPlayerMP) o ); + } + } else if( this.field.getType().isEnum() ) + { + o.sendProgressBarUpdate( this.source, this.channel, ( (Enum) val ).ordinal() ); + } else if( val instanceof Long || val.getClass() == long.class ) + { + NetworkHandler.instance.sendTo( new PacketProgressBar( this.channel, (Long) val ), (EntityPlayerMP) o ); + } else if( val instanceof Boolean || val.getClass() == boolean.class ) + { + o.sendProgressBarUpdate( this.source, this.channel, ( (Boolean) val ) ? 1 : 0 ); + } else + { + o.sendProgressBarUpdate( this.source, this.channel, (Integer) val ); + } + + this.clientVersion = val; + } + + public void update( final Object val ) + { + try + { + final Object oldValue = this.getValue(); + if( val instanceof String ) + { + this.updateString( oldValue, (String) val ); + } else + { + this.updateValue( oldValue, (Long) val ); + } + } catch( final IllegalArgumentException e ) + { + AELog.debug( e ); + } catch( final IllegalAccessException e ) + { + AELog.debug( e ); + } + } + + private void updateString( final Object oldValue, final String val ) + { + try + { + this.setValue( val ); + this.source.onUpdate( this.fieldName, oldValue, this.getValue() ); + } catch( final IllegalArgumentException e ) + { + AELog.debug( e ); + } catch( final IllegalAccessException e ) + { + AELog.debug( e ); + } + } + + private void updateValue( final Object oldValue, final long val ) + { + try + { + if( this.field.getType().isEnum() ) + { + final EnumSet valList = EnumSet.allOf( (Class) this.field.getType() ); + for( final Enum e : valList ) + { + if( e.ordinal() == val ) + { + this.setValue( e ); + break; + } + } + } else + { + if( this.field.getType().equals( int.class ) ) + { + this.setValue( (int) val ); + } else if( this.field.getType().equals( long.class ) ) + { + this.setValue( val ); + } else if( this.field.getType().equals( boolean.class ) ) + { + this.setValue( val == 1 ); + } else if( this.field.getType().equals( Integer.class ) ) + { + this.setValue( (int) val ); + } else if( this.field.getType().equals( Long.class ) ) + { + this.setValue( val ); + } else if( this.field.getType().equals( Boolean.class ) ) + { + this.setValue( val == 1 ); + } + } + + this.source.onUpdate( this.fieldName, oldValue, this.getValue() ); + } catch( final IllegalArgumentException e ) + { + AELog.debug( e ); + } catch( final IllegalAccessException e ) + { + AELog.debug( e ); + } + } } diff --git a/src/main/java/appeng/container/implementations/ContainerCPUTable.java b/src/main/java/appeng/container/implementations/ContainerCPUTable.java new file mode 100644 index 00000000000..766408331e7 --- /dev/null +++ b/src/main/java/appeng/container/implementations/ContainerCPUTable.java @@ -0,0 +1,188 @@ +package appeng.container.implementations; + +import appeng.api.networking.IGrid; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; +import appeng.container.AEBaseContainer; +import appeng.container.guisync.GuiSync; +import appeng.container.interfaces.ICraftingCPUSelectorContainer; +import appeng.core.AELog; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketCraftingCPUsUpdate; +import appeng.util.Platform; +import com.google.common.collect.ImmutableSet; +import net.minecraft.entity.player.EntityPlayerMP; + +import java.io.IOException; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class ContainerCPUTable implements ICraftingCPUSelectorContainer +{ + private final AEBaseContainer parent; + + private ImmutableSet lastCpuSet = null; + private List cpus = new ArrayList(); + private final WeakHashMap cpuSerialMap = new WeakHashMap<>(); + private int nextCpuSerial = 1; + private int lastUpdate = 0; + @GuiSync( 0 ) + public int selectedCpuSerial = -1; + private final Consumer onCPUChange; + private final boolean preferBusyCPUs; + private final Predicate cpuFilter; + + private static final Comparator CPU_COMPARATOR = Comparator + .comparing((CraftingCPUStatus e) -> e.getName() == null || e.getName().isEmpty()) + .thenComparing(e -> e.getName() != null ? e.getName() : "") + .thenComparingInt(CraftingCPUStatus::getSerial); + + /** + * @param parent Container parent, of which this is a field + * @param onCPUChange Called whenever the current CPU is changed + * @param preferBusyCPUs Whether busy CPUs should be picked first (e.g. crafting status vs. picking a CPU for a job) + */ + public ContainerCPUTable(AEBaseContainer parent, Consumer onCPUChange, boolean preferBusyCPUs, Predicate cpuFilter) + { + this.parent = parent; + this.onCPUChange = onCPUChange; + this.preferBusyCPUs = preferBusyCPUs; + this.cpuFilter = cpuFilter; + } + + public boolean isBusyCPUsPreferred() + { + return preferBusyCPUs; + } + + public Predicate getCpuFilter() + { + return cpuFilter; + } + + public void detectAndSendChanges( IGrid network, List crafters) { + if( Platform.isServer() && network != null ) + { + final ICraftingGrid cc = network.getCache( ICraftingGrid.class ); + final ImmutableSet cpuSet = cc.getCpus(); + // Update at least once a second + ++lastUpdate; + if (!cpuSet.equals( lastCpuSet ) || lastUpdate > 20) { + lastUpdate = 0; + lastCpuSet = cpuSet; + updateCpuList(); + sendCPUs(crafters); + } + } + + // Clear selection if CPU is no longer in list + if (selectedCpuSerial != -1) { + if (cpus.stream().noneMatch(c -> c.getSerial() == selectedCpuSerial)) { + selectCPU(-1); + } + } + + // Select a suitable CPU if none is selected + if (selectedCpuSerial == -1) { + // Try preferred CPUs first + for (CraftingCPUStatus cpu : cpus) { + if ( preferBusyCPUs == cpu.isBusy() && cpuFilter.test(cpu) ) { + selectCPU(cpu.getSerial()); + break; + } + } + // If we couldn't find a preferred one, just select the first + if (selectedCpuSerial == -1 && !cpus.isEmpty()) { + selectCPU(cpus.get(0).getSerial()); + } + } + } + + private void updateCpuList() + { + this.cpus.clear(); + for (ICraftingCPU cpu : lastCpuSet) + { + int serial = getOrAssignCpuSerial(cpu); + this.cpus.add( new CraftingCPUStatus( cpu, serial ) ); + } + this.cpus.sort(CPU_COMPARATOR); + } + + private int getOrAssignCpuSerial( ICraftingCPU cpu ) + { + return cpuSerialMap.computeIfAbsent( cpu, unused -> nextCpuSerial++ ); + } + + private void sendCPUs(List crafters) + { + final PacketCraftingCPUsUpdate update; + for( final Object player : crafters ) + { + if( player instanceof EntityPlayerMP ) + { + try + { + NetworkHandler.instance.sendTo( new PacketCraftingCPUsUpdate( this.cpus ), (EntityPlayerMP) player ); + } + catch( IOException e ) + { + AELog.debug( e ); + } + } + } + } + + @Override + public void selectCPU( int serial ) + { + if (Platform.isServer()) + { + if( serial < -1 ) + { + serial = -1; + } + + final int searchedSerial = serial; + if( serial > -1 && cpus.stream().noneMatch(c -> c.getSerial() == searchedSerial) ) + { + serial = -1; + } + + ICraftingCPU newSelectedCpu = null; + if( serial != -1 ) + { + for( ICraftingCPU cpu : lastCpuSet ) + { + if( cpuSerialMap.getOrDefault( cpu, -1 ) == serial ) + { + newSelectedCpu = cpu; + break; + } + } + } + + this.selectedCpuSerial = serial; + if (onCPUChange != null) + { + onCPUChange.accept( newSelectedCpu ); + } + } + } + + public List getCPUs() + { + return Collections.unmodifiableList( cpus ); + } + + public CraftingCPUStatus getSelectedCPU() + { + return this.cpus.stream().filter(c -> c.getSerial() == selectedCpuSerial).findFirst().orElse(null); + } + + public void handleCPUUpdate( CraftingCPUStatus[] cpus ) + { + this.cpus = Arrays.asList( cpus ); + } +} diff --git a/src/main/java/appeng/container/implementations/ContainerCraftConfirm.java b/src/main/java/appeng/container/implementations/ContainerCraftConfirm.java index d30a214d091..0cf79a8a1ce 100644 --- a/src/main/java/appeng/container/implementations/ContainerCraftConfirm.java +++ b/src/main/java/appeng/container/implementations/ContainerCraftConfirm.java @@ -37,6 +37,7 @@ import appeng.api.storage.data.IItemList; import appeng.container.AEBaseContainer; import appeng.container.guisync.GuiSync; +import appeng.container.interfaces.ICraftingCPUSelectorContainer; import appeng.core.AELog; import appeng.core.sync.GuiBridge; import appeng.core.sync.network.NetworkHandler; @@ -64,10 +65,9 @@ import java.util.concurrent.Future; -public class ContainerCraftConfirm extends AEBaseContainer +public class ContainerCraftConfirm extends AEBaseContainer implements ICraftingCPUSelectorContainer { - private final ArrayList cpus = new ArrayList(); private Future job; private ICraftingJob result; @GuiSync( 0 ) @@ -80,39 +80,28 @@ public class ContainerCraftConfirm extends AEBaseContainer public boolean autoStart = false; @GuiSync( 4 ) public boolean simulation = true; - @GuiSync( 5 ) - public int selectedCpu = -1; @GuiSync( 6 ) public boolean noCPU = true; @GuiSync( 7 ) public String myName = ""; + @GuiSync.Recurse( 8 ) + public final ContainerCPUTable cpuTable; public ContainerCraftConfirm( final InventoryPlayer ip, final ITerminalHost te ) { super( ip, te ); + this.cpuTable = new ContainerCPUTable( this, this::onCPUUpdate, false, this::cpuMatches ); } - public void cycleCpu( final boolean next ) - { - if( next ) - { - this.setSelectedCpu( this.getSelectedCpu() + 1 ); - } - else - { - this.setSelectedCpu( this.getSelectedCpu() - 1 ); - } + @Override + public void selectCPU( int cpu ) + { + this.cpuTable.selectCPU( cpu ); + } - if( this.getSelectedCpu() < -1 ) - { - this.setSelectedCpu( this.cpus.size() - 1 ); - } - else if( this.getSelectedCpu() >= this.cpus.size() ) - { - this.setSelectedCpu( -1 ); - } - - if( this.getSelectedCpu() == -1 ) + public void onCPUUpdate( ICraftingCPU cpu ) + { + if( cpu == null ) { this.setCpuAvailableBytes( 0 ); this.setCpuCoProcessors( 0 ); @@ -120,66 +109,26 @@ else if( this.getSelectedCpu() >= this.cpus.size() ) } else { - this.setName( this.cpus.get( this.getSelectedCpu() ).getName() ); - this.setCpuAvailableBytes( this.cpus.get( this.getSelectedCpu() ).getSize() ); - this.setCpuCoProcessors( this.cpus.get( this.getSelectedCpu() ).getProcessors() ); + this.setName( cpu.getName() ); + this.setCpuAvailableBytes( cpu.getAvailableStorage() ); + this.setCpuCoProcessors( cpu.getCoProcessors() ); } } @Override public void detectAndSendChanges() { + // Wait with CPU selection until job bytes are retrieved + if (this.bytesUsed != 0) + { + cpuTable.detectAndSendChanges( getGrid(), crafters ); + } if( Platform.isClient() ) { return; } - if (getGrid() == null) - 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; - } - } - - 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.setNoCPU( this.cpuTable.getCPUs().isEmpty() ); super.detectAndSendChanges(); @@ -298,28 +247,9 @@ private IGrid getGrid() return h.getActionableNode().getGrid(); } - private boolean cpuMatches( final ICraftingCPU c ) + private boolean cpuMatches( final CraftingCPUStatus c ) { - 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( "" ); - } - else if( this.getSelectedCpu() != -1 ) - { - this.setName( this.cpus.get( this.getSelectedCpu() ).getName() ); - this.setCpuAvailableBytes( this.cpus.get( this.getSelectedCpu() ).getSize() ); - this.setCpuCoProcessors( this.cpus.get( this.getSelectedCpu() ).getProcessors() ); - } + return c.getStorage() >= this.getUsedBytes() && !c.isBusy(); } public void startJob() @@ -355,7 +285,8 @@ public void startJob() if( this.result != null && !this.isSimulation() && getGrid() != null) { 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() ); + CraftingCPUStatus selected = this.cpuTable.getSelectedCPU(); + final ICraftingLink g = cc.submitJob( this.result, null, (selected == null) ? null : selected.getServerCluster(), true, this.getActionSrc() ); this.setAutoStart( false ); if( g != null && originalGui != null && this.getOpenContext() != null ) { @@ -441,12 +372,7 @@ private void setCpuCoProcessors( final int cpuCoProcessors ) public int getSelectedCpu() { - return this.selectedCpu; - } - - private void setSelectedCpu( final int selectedCpu ) - { - this.selectedCpu = selectedCpu; + return this.cpuTable.selectedCpuSerial; } public String getName() diff --git a/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java b/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java index c46b42e0af7..f9976999b5b 100644 --- a/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java +++ b/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java @@ -24,14 +24,12 @@ import appeng.api.networking.crafting.ICraftingGrid; import appeng.api.storage.ITerminalHost; import appeng.container.guisync.GuiSync; +import appeng.container.interfaces.ICraftingCPUSelectorContainer; import appeng.core.AELog; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketCraftingCPUsUpdate; import appeng.util.Platform; -import com.google.common.collect.ImmutableCollection; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.InventoryPlayer; @@ -39,153 +37,37 @@ import java.util.*; -public class ContainerCraftingStatus extends ContainerCraftingCPU +public class ContainerCraftingStatus extends ContainerCraftingCPU implements ICraftingCPUSelectorContainer { - - private ImmutableSet lastCpuSet = null; - private List cpus = new ArrayList(); - private final WeakHashMap cpuSerialMap = new WeakHashMap<>(); - private int nextCpuSerial = 1; - private int lastUpdate = 0; - @GuiSync( 5 ) - public int selectedCpuSerial = -1; + @GuiSync.Recurse( 5 ) + public ContainerCPUTable cpuTable; public ContainerCraftingStatus( final InventoryPlayer ip, final ITerminalHost te ) { super( ip, te ); + cpuTable = new ContainerCPUTable( this, this::setCPU, true, c -> true ); } - @Override - public void detectAndSendChanges() - { - IGrid network = this.getNetwork(); - if( Platform.isServer() && network != null ) - { - final ICraftingGrid cc = network.getCache( ICraftingGrid.class ); - final ImmutableSet cpuSet = cc.getCpus(); - // Update at least once a second - ++lastUpdate; - if (!cpuSet.equals( lastCpuSet ) || lastUpdate > 20) { - lastUpdate = 0; - lastCpuSet = cpuSet; - updateCpuList(); - sendCPUs(); - } - } - - // Clear selection if CPU is no longer in list - if (selectedCpuSerial != -1) { - if (cpus.stream().noneMatch(c -> c.getSerial() == selectedCpuSerial)) { - selectCPU(-1); - } - } - - // Select a suitable CPU if none is selected - if (selectedCpuSerial == -1) { - // Try busy CPUs first - for (CraftingCPUStatus cpu : cpus) { - if (cpu.getRemainingItems() > 0) { - selectCPU(cpu.getSerial()); - break; - } - } - // If we couldn't find a busy one, just select the first - if (selectedCpuSerial == -1 && !cpus.isEmpty()) { - selectCPU(cpus.get(0).getSerial()); - } - } - - super.detectAndSendChanges(); - } - - private static final Comparator CPU_COMPARATOR = Comparator - .comparing((CraftingCPUStatus e) -> e.getName() == null || e.getName().isEmpty()) - .thenComparing(e -> e.getName() != null ? e.getName() : "") - .thenComparingInt(CraftingCPUStatus::getSerial); - - private void updateCpuList() - { - this.cpus.clear(); - for (ICraftingCPU cpu : lastCpuSet) - { - int serial = getOrAssignCpuSerial(cpu); - this.cpus.add( new CraftingCPUStatus( cpu, serial ) ); - } - this.cpus.sort(CPU_COMPARATOR); - } - - private int getOrAssignCpuSerial( ICraftingCPU cpu ) + public ContainerCPUTable getCPUTable() { - return cpuSerialMap.computeIfAbsent( cpu, unused -> nextCpuSerial++ ); + return cpuTable; } - private boolean cpuMatches( final ICraftingCPU c ) + @Override + public void detectAndSendChanges() { - return c.isBusy(); + cpuTable.detectAndSendChanges(getNetwork(), crafters); + super.detectAndSendChanges(); } - private void sendCPUs() - { - final PacketCraftingCPUsUpdate update; - for( final Object player : this.crafters ) - { - if( player instanceof EntityPlayerMP ) - { - try - { - NetworkHandler.instance.sendTo( new PacketCraftingCPUsUpdate( this.cpus ), (EntityPlayerMP) player ); - } - catch( IOException e ) - { - AELog.debug( e ); - } - } - } - } - + @Override public void selectCPU( int serial ) { - if (Platform.isServer()) - { - if( serial < -1 ) - { - serial = -1; - } - - final int searchedSerial = serial; - if( serial > -1 && cpus.stream().noneMatch(c -> c.getSerial() == searchedSerial) ) - { - serial = -1; - } - - ICraftingCPU newSelectedCpu = null; - if( serial != -1 ) - { - for( ICraftingCPU cpu : lastCpuSet ) - { - if( cpuSerialMap.getOrDefault( cpu, -1 ) == serial ) - { - newSelectedCpu = cpu; - break; - } - } - } - - if( newSelectedCpu != getMonitor() ) - { - this.selectedCpuSerial = serial; - setCPU( newSelectedCpu ); - } - } + cpuTable.selectCPU( serial ); } public List getCPUs() { - return Collections.unmodifiableList( cpus ); - } - - public void postCPUUpdate( CraftingCPUStatus[] cpus ) - { - this.cpus = Arrays.asList( cpus ); + return cpuTable.getCPUs(); } } diff --git a/src/main/java/appeng/container/implementations/CraftingCPUStatus.java b/src/main/java/appeng/container/implementations/CraftingCPUStatus.java index 22503ba80f0..0121db472c4 100644 --- a/src/main/java/appeng/container/implementations/CraftingCPUStatus.java +++ b/src/main/java/appeng/container/implementations/CraftingCPUStatus.java @@ -22,6 +22,7 @@ public class CraftingCPUStatus implements Comparable private final int serial; private final long storage; private final long coprocessors; + private final boolean isBusy; private final long totalItems; private final long remainingItems; private final IAEItemStack crafting; @@ -33,6 +34,7 @@ public CraftingCPUStatus( ) this.serial = 0; this.storage = 0; this.coprocessors = 0; + this.isBusy = false; this.totalItems = 0; this.remainingItems = 0; this.crafting = null; @@ -43,7 +45,8 @@ public CraftingCPUStatus( ICraftingCPU cluster, int serial ) this.serverCluster = cluster; this.name = cluster.getName(); this.serial = serial; - if (cluster.isBusy()) + this.isBusy = cluster.isBusy(); + if (isBusy) { crafting = cluster.getFinalOutput(); totalItems = cluster.getStartItemCount(); @@ -66,6 +69,7 @@ public CraftingCPUStatus( NBTTagCompound i ) this.serial = i.getInteger( "serial" ); this.storage = i.getLong("storage"); this.coprocessors = i.getLong("coprocessors"); + this.isBusy = i.getBoolean("isBusy"); this.totalItems = i.getLong("totalItems"); this.remainingItems = i.getLong("remainingItems"); this.crafting = i.hasKey( "crafting" ) ? AEItemStack.loadItemStackFromNBT( i.getCompoundTag( "crafting" ) ) : null; @@ -94,6 +98,7 @@ public void writeToNBT( NBTTagCompound i ) i.setInteger( "serial", serial ); i.setLong( "storage", storage ); i.setLong( "coprocessors", coprocessors ); + i.setBoolean( "isBusy", isBusy ); i.setLong( "totalItems", totalItems ); i.setLong( "remainingItems", remainingItems ); if (crafting != null) @@ -161,6 +166,11 @@ public IAEItemStack getCrafting() return crafting; } + public boolean isBusy() + { + return isBusy; + } + @Override public int compareTo( CraftingCPUStatus o ) { diff --git a/src/main/java/appeng/container/interfaces/ICraftingCPUSelectorContainer.java b/src/main/java/appeng/container/interfaces/ICraftingCPUSelectorContainer.java new file mode 100644 index 00000000000..95dd3f52c5d --- /dev/null +++ b/src/main/java/appeng/container/interfaces/ICraftingCPUSelectorContainer.java @@ -0,0 +1,6 @@ +package appeng.container.interfaces; + +public interface ICraftingCPUSelectorContainer +{ + void selectCPU(int cpu); +} diff --git a/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java b/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java index 7131e80b8b4..f0370834222 100644 --- a/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java +++ b/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java @@ -1,6 +1,7 @@ package appeng.core.sync.packets; import appeng.client.gui.implementations.GuiCraftingStatus; +import appeng.client.gui.widgets.ICraftingCPUTableHolder; import appeng.container.implementations.CraftingCPUStatus; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; @@ -53,10 +54,10 @@ public void clientPacketData( final INetworkInfo network, final AppEngPacket pac { final GuiScreen gs = Minecraft.getMinecraft().currentScreen; - if( gs instanceof GuiCraftingStatus ) + if( gs instanceof ICraftingCPUTableHolder ) { - GuiCraftingStatus gui = (GuiCraftingStatus) gs; - gui.postCPUUpdate( this.cpus ); + ICraftingCPUTableHolder gui = (ICraftingCPUTableHolder) gs; + gui.getCPUTable().getContainer().handleCPUUpdate( this.cpus ); } } diff --git a/src/main/java/appeng/core/sync/packets/PacketValueConfig.java b/src/main/java/appeng/core/sync/packets/PacketValueConfig.java index 92d41cad4ea..cbf410d0569 100644 --- a/src/main/java/appeng/core/sync/packets/PacketValueConfig.java +++ b/src/main/java/appeng/core/sync/packets/PacketValueConfig.java @@ -26,6 +26,7 @@ import appeng.client.gui.implementations.GuiCraftingCPU; import appeng.container.AEBaseContainer; import appeng.container.implementations.*; +import appeng.container.interfaces.ICraftingCPUSelectorContainer; import appeng.core.sync.AppEngPacket; import appeng.core.sync.network.INetworkInfo; import appeng.helpers.IMouseWheelItem; @@ -87,16 +88,11 @@ public void serverPacketData( final INetworkInfo manager, final AppEngPacket pac final IMouseWheelItem si = (IMouseWheelItem) is.getItem(); si.onWheel( is, this.Value.equals( "WheelUp" ) ); } - else if( this.Name.equals( "Terminal.Cpu.Set" ) && c instanceof ContainerCraftingStatus ) + else if( this.Name.equals( "CPUTable.Cpu.Set" ) && c instanceof ICraftingCPUSelectorContainer ) { - final ContainerCraftingStatus qk = (ContainerCraftingStatus) c; + final ICraftingCPUSelectorContainer qk = (ICraftingCPUSelectorContainer) c; qk.selectCPU( Integer.parseInt( this.Value ) ); } - else if( this.Name.equals( "Terminal.Cpu" ) && c instanceof ContainerCraftConfirm ) - { - final ContainerCraftConfirm qk = (ContainerCraftConfirm) c; - qk.cycleCpu( this.Value.equals( "Next" ) ); - } else if( this.Name.equals( "Terminal.Start" ) && c instanceof ContainerCraftConfirm ) { final ContainerCraftConfirm qk = (ContainerCraftConfirm) c; diff --git a/src/main/java/appeng/integration/modules/NEIHelpers/NEIGuiHandler.java b/src/main/java/appeng/integration/modules/NEIHelpers/NEIGuiHandler.java index e2d9c96f724..73fef68747e 100644 --- a/src/main/java/appeng/integration/modules/NEIHelpers/NEIGuiHandler.java +++ b/src/main/java/appeng/integration/modules/NEIHelpers/NEIGuiHandler.java @@ -1,5 +1,6 @@ package appeng.integration.modules.NEIHelpers; +import appeng.client.gui.implementations.GuiCraftConfirm; import appeng.client.gui.implementations.GuiCraftingStatus; import appeng.client.gui.implementations.GuiMEMonitorable; import appeng.client.gui.widgets.IDropToFillTextField; @@ -37,6 +38,8 @@ public boolean hideItemPanelSlot( GuiContainer gui, int x, int y, int w, int h ) { if (gui instanceof GuiCraftingStatus) { return ((GuiCraftingStatus) gui).hideItemPanelSlot(x, y, w, h); + } else if (gui instanceof GuiCraftConfirm ) { + return ((GuiCraftConfirm) gui).hideItemPanelSlot(x, y, w, h); } else if (gui instanceof GuiMEMonitorable) { return ((GuiMEMonitorable) gui).hideItemPanelSlot(x, y, w, h); }