diff --git a/src/api/java/appeng/api/networking/crafting/ICraftingCPU.java b/src/api/java/appeng/api/networking/crafting/ICraftingCPU.java index 5888bf7aa..5db875c49 100644 --- a/src/api/java/appeng/api/networking/crafting/ICraftingCPU.java +++ b/src/api/java/appeng/api/networking/crafting/ICraftingCPU.java @@ -28,6 +28,8 @@ import appeng.api.networking.security.IActionSource; import appeng.api.networking.storage.IBaseMonitor; import appeng.api.storage.data.IAEItemStack; +import javax.annotation.Nullable; + public interface ICraftingCPU extends IBaseMonitor { @@ -56,4 +58,30 @@ public interface ICraftingCPU extends IBaseMonitor * @return an empty string or the name of the cpu. */ String getName(); + + + /** + * @return final output of the current crafting operation, or null if not crafting + */ + @Nullable + default IAEItemStack getFinalOutput() + { + return null; + } + + /** + * @return remaining count of items (or other units of processing) for the current crafting job + */ + default long getRemainingItemCount() + { + return 0; + } + + /** + * @return total count of items (or other units of processing) for the current crafting job + */ + default long getStartItemCount() + { + return 0; + } } diff --git a/src/main/java/appeng/client/gui/AEBaseGui.java b/src/main/java/appeng/client/gui/AEBaseGui.java index 5d0fc7141..7ae8df1bf 100644 --- a/src/main/java/appeng/client/gui/AEBaseGui.java +++ b/src/main/java/appeng/client/gui/AEBaseGui.java @@ -23,13 +23,8 @@ import java.awt.*; import java.io.IOException; import java.text.DecimalFormat; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; +import java.util.*; import java.util.List; -import java.util.Set; import java.util.concurrent.TimeUnit; import appeng.container.slot.*; @@ -217,6 +212,10 @@ public abstract class AEBaseGui extends GuiContainer implements IMTModGuiContain GlStateManager.disableDepth(); } + public List getJEIExclusionArea() { + return Collections.emptyList(); + } + @Optional.Method( modid = "jei" ) void bookmarkedJEIghostItem( final int mouseX, final int mouseY ) { diff --git a/src/main/java/appeng/client/gui/AEGuiHandler.java b/src/main/java/appeng/client/gui/AEGuiHandler.java index bfc78b519..4fbb0e4c8 100644 --- a/src/main/java/appeng/client/gui/AEGuiHandler.java +++ b/src/main/java/appeng/client/gui/AEGuiHandler.java @@ -1,5 +1,6 @@ package appeng.client.gui; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import appeng.api.storage.data.IAEItemStack; import appeng.client.gui.implementations.*; @@ -20,6 +21,7 @@ import java.util.List; public class AEGuiHandler implements IAdvancedGuiHandler, IGhostIngredientHandler { @Override + @Nonnull public Class getGuiContainerClass() { return AEBaseGui.class; @@ -27,14 +29,14 @@ public class AEGuiHandler implements IAdvancedGuiHandler, IGhostIngre @Nullable @Override - public List getGuiExtraAreas( AEBaseGui guiContainer ) + public List getGuiExtraAreas( @Nonnull AEBaseGui guiContainer ) { - return Collections.emptyList(); + return guiContainer.getJEIExclusionArea(); } @Nullable @Override - public Object getIngredientUnderMouse( AEBaseGui guiContainer, int mouseX, int mouseY ) + public Object getIngredientUnderMouse( @Nonnull AEBaseGui guiContainer, int mouseX, int mouseY ) { List visual; int guiSlotIdx; @@ -93,7 +95,8 @@ public class AEGuiHandler implements IAdvancedGuiHandler, IGhostIngre } @Override - public List> getTargets( AEBaseGui gui, I ingredient, boolean doStart ) + @Nonnull + public List> getTargets( @Nonnull AEBaseGui gui, @Nonnull I ingredient, boolean doStart ) { ArrayList> targets = new ArrayList<>(); if( gui instanceof IJEIGhostIngredients ) diff --git a/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java b/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java index 2fc045110..743e4b751 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java +++ b/src/main/java/appeng/client/gui/implementations/GuiCraftingStatus.java @@ -23,9 +23,17 @@ package appeng.client.gui.implementations; +import java.awt.*; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import appeng.api.storage.data.IAEItemStack; +import appeng.client.gui.widgets.GuiScrollbar; +import appeng.container.implementations.CraftingCPUStatus; import appeng.parts.reporting.PartExpandedProcessingPatternTerminal; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import org.lwjgl.input.Mouse; import net.minecraft.client.gui.GuiButton; @@ -48,17 +56,27 @@ import appeng.helpers.WirelessTerminalGuiObject; import appeng.parts.reporting.PartCraftingTerminal; import appeng.parts.reporting.PartPatternTerminal; import appeng.parts.reporting.PartTerminal; +import org.lwjgl.opengl.GL11; public class GuiCraftingStatus extends GuiCraftingCPU { + 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 GuiTabButton originalGuiBtn; private GuiBridge originalGui; private ItemStack myIcon = ItemStack.EMPTY; + private String selectedCPUName = ""; public GuiCraftingStatus( final InventoryPlayer inventoryPlayer, final ITerminalHost te ) { @@ -111,18 +129,6 @@ public class GuiCraftingStatus extends GuiCraftingCPU final boolean backwards = Mouse.isButtonDown( 1 ); - if( btn == this.selectCPU ) - { - try - { - NetworkHandler.instance().sendToServer( new PacketValueConfig( "Terminal.Cpu", backwards ? "Prev" : "Next" ) ); - } - catch( final IOException e ) - { - AELog.debug( e ); - } - } - if( btn == this.originalGuiBtn ) { NetworkHandler.instance().sendToServer( new PacketSwitchGuis( this.originalGui ) ); @@ -136,9 +142,15 @@ public class GuiCraftingStatus extends GuiCraftingCPU this.selectCPU = new GuiButton( 0, this.guiLeft + 8, this.guiTop + this.ySize - 25, 150, 20, GuiText.CraftingCPU .getLocal() + ": " + GuiText.NoCraftingCPUs ); - // selectCPU.enabled = false; + 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.isEmpty() ) { this.buttonList.add( @@ -150,28 +162,271 @@ public class GuiCraftingStatus extends GuiCraftingCPU @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.updateCPUButtonText(); super.drawScreen( mouseX, mouseY, btn ); } + @Override + 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; + final int TEXT_COLOR = 0x202020; + 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, TEXT_COLOR ); + 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, 0x009000 ); + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glTranslatef( x + CPU_TABLE_SLOT_WIDTH - 19, y + 3, 0 ); + this.drawItem( 0, 0, craftingStack.createItemStack() ); + } + 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, TEXT_COLOR ); + } + 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.createItemStack().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, tooltip.toString() ); + } + } + } + + @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 ); + } + + @Override + public List getJEIExclusionArea() { + Rectangle craftingCPUArea = new Rectangle(this.guiLeft - CPU_TABLE_WIDTH, this.guiTop, CPU_TABLE_WIDTH, CPU_TABLE_HEIGHT); + List area = new ArrayList(); + area.add(craftingCPUArea); + return area; + } + + @Override + protected void mouseClicked( int xCoord, int yCoord, int btn ) throws IOException { + 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 ); + } + } + } + + @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 ); + } + } + + @Override + public void handleMouseInput() throws IOException { + 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; + } + } + super.handleMouseInput(); + } + + 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; + } + private void updateCPUButtonText() { String btnTextText = GuiText.NoCraftingJobs.getLocal(); - if( this.status.selectedCpu >= 0 )// && status.selectedCpu < status.cpus.size() ) + if( this.status.selectedCpuSerial >= 0 )// && status.selectedCpu < status.cpus.size() ) { - if( this.status.myName.length() > 0 ) + if( this.selectedCPUName != null && this.selectedCPUName.length() > 0 ) { - final String name = this.status.myName.substring( 0, Math.min( 20, this.status.myName.length() ) ); + final String name = this.selectedCPUName.substring( 0, Math.min( 20, this.selectedCPUName.length() ) ); btnTextText = GuiText.CPUs.getLocal() + ": " + name; } else { - btnTextText = GuiText.CPUs.getLocal() + ": #" + this.status.selectedCpu; + btnTextText = GuiText.CPUs.getLocal() + ": #" + this.status.selectedCpuSerial; } } - if( this.status.noCPU ) + if( this.status.getCPUs().isEmpty() ) { btnTextText = GuiText.NoCraftingJobs.getLocal(); } @@ -184,4 +439,9 @@ public class GuiCraftingStatus extends GuiCraftingCPU { 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/implementations/GuiMEMonitorable.java b/src/main/java/appeng/client/gui/implementations/GuiMEMonitorable.java index c95d7e186..79ba8401d 100644 --- a/src/main/java/appeng/client/gui/implementations/GuiMEMonitorable.java +++ b/src/main/java/appeng/client/gui/implementations/GuiMEMonitorable.java @@ -19,6 +19,7 @@ package appeng.client.gui.implementations; +import java.awt.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -26,6 +27,10 @@ import java.util.List; import appeng.client.me.SlotME; import appeng.container.slot.SlotRestrictedInput; import appeng.core.features.ColoredItemDefinition; +import mezz.jei.gui.GuiHelper; +import mezz.jei.gui.elements.GuiIconButton; +import mezz.jei.gui.overlay.bookmarks.BookmarkButton; +import net.minecraftforge.fml.common.Loader; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; @@ -301,6 +306,11 @@ public class GuiMEMonitorable extends AEBaseMEGui implements ISortSource, IConfi int offset = this.guiTop + 8; + if(Loader.isModLoaded("jei")) { + // JEI Bookmark Next page button + offset += 16; + } + if( this.customSortOrder ) { this.buttonList @@ -399,6 +409,44 @@ public class GuiMEMonitorable extends AEBaseMEGui implements ISortSource, IConfi } + @Override + public List getJEIExclusionArea() { + List exclusionArea = new ArrayList<>(); + + int yOffset = guiTop + 8; + + if(Loader.isModLoaded("jei")) { + yOffset += 16; + } + + if(this.customSortOrder) { + Rectangle sortOrder = new Rectangle(guiLeft - 18, yOffset, 18, 18); + exclusionArea.add(sortOrder); + yOffset += 20; + } + + if(this.viewCell || this instanceof GuiWirelessTerm) { + Rectangle viewMode = new Rectangle(guiLeft - 18, yOffset, 18, 18); + exclusionArea.add(viewMode); + yOffset += 20; + } + + Rectangle sortDir = new Rectangle(guiLeft - 18, yOffset, 18, 18); + exclusionArea.add(sortDir); + yOffset += 20; + + Rectangle searchSettings = new Rectangle(guiLeft - 18, yOffset, 18, 18); + exclusionArea.add(searchSettings); + yOffset += 20; + + if( !( this instanceof GuiMEPortableCell ) || this instanceof GuiWirelessTerm ) { + Rectangle terminalStyle = new Rectangle(guiLeft - 18, yOffset, 18, 18); + exclusionArea.add(terminalStyle); + } + + return exclusionArea; + } + @Override public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) { diff --git a/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java b/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java index c534a16d5..2e778fe64 100644 --- a/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java +++ b/src/main/java/appeng/container/implementations/ContainerCraftingStatus.java @@ -19,12 +19,16 @@ package appeng.container.implementations; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.io.IOException; +import java.util.*; +import appeng.api.networking.IGrid; +import appeng.core.AELog; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketCraftingCPUsUpdate; import com.google.common.collect.ImmutableSet; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.InventoryPlayer; import appeng.api.networking.crafting.ICraftingCPU; @@ -37,13 +41,13 @@ import appeng.util.Platform; public class ContainerCraftingStatus extends ContainerCraftingCPU { - private final List cpus = new ArrayList<>(); - @GuiSync( 5 ) - public int selectedCpu = -1; - @GuiSync( 6 ) - public boolean noCPU = true; - @GuiSync( 7 ) - public String myName = ""; + 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; public ContainerCraftingStatus( final InventoryPlayer ip, final ITerminalHost te ) { @@ -53,12 +57,13 @@ public class ContainerCraftingStatus extends ContainerCraftingCPU @Override public void detectAndSendChanges() { - if( Platform.isServer() && this.getNetwork() != null ) + IGrid network = this.getNetwork(); + if( Platform.isServer() && network != null ) { - final ICraftingGrid cc = this.getNetwork().getCache( ICraftingGrid.class ); + final ICraftingGrid cc = network.getCache( ICraftingGrid.class ); final ImmutableSet cpuSet = cc.getCpus(); - int matches = 0; + /*int matches = 0; boolean changed = false; for( final ICraftingCPU c : cpuSet ) { @@ -98,12 +103,65 @@ public class ContainerCraftingStatus extends ContainerCraftingCPU this.sendCPUs(); } - this.noCPU = this.cpus.isEmpty(); + this.noCPU = this.cpus.isEmpty(); */ + + // 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 ) + { + return cpuSerialMap.computeIfAbsent( cpu, unused -> nextCpuSerial++ ); + } + private boolean cpuMatches( final ICraftingCPU c ) { return c.isBusy(); @@ -111,7 +169,22 @@ public class ContainerCraftingStatus extends ContainerCraftingCPU private void sendCPUs() { - Collections.sort( this.cpus ); + final PacketCraftingCPUsUpdate update; + for( final Object player : this.listeners ) + { + if( player instanceof EntityPlayerMP) + { + try + { + NetworkHandler.instance.sendTo( new PacketCraftingCPUsUpdate( this.cpus ), (EntityPlayerMP) player ); + } + catch( IOException e ) + { + AELog.debug( e ); + } + } + } + /*Collections.sort( this.cpus ); if( this.selectedCpu >= this.cpus.size() ) { @@ -138,10 +211,47 @@ public class ContainerCraftingStatus extends ContainerCraftingCPU else { this.setCPU( null ); + }*/ + } + + + 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 ); + } } } - public void cycleCpu( final boolean next ) + /*public void cycleCpu( final boolean next ) { if( next ) { @@ -176,5 +286,15 @@ public class ContainerCraftingStatus extends ContainerCraftingCPU this.myName = this.cpus.get( this.selectedCpu ).getName(); this.setCPU( this.cpus.get( this.selectedCpu ).getCpu() ); } + } */ + + public List getCPUs() + { + return Collections.unmodifiableList( cpus ); + } + + public void postCPUUpdate( CraftingCPUStatus[] cpus ) + { + this.cpus = Arrays.asList( cpus ); } } diff --git a/src/main/java/appeng/container/implementations/CraftingCPUStatus.java b/src/main/java/appeng/container/implementations/CraftingCPUStatus.java new file mode 100644 index 000000000..def36e6b3 --- /dev/null +++ b/src/main/java/appeng/container/implementations/CraftingCPUStatus.java @@ -0,0 +1,196 @@ +package appeng.container.implementations; + +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.storage.data.IAEItemStack; +import appeng.util.ItemSorters; +import appeng.util.item.AEItemStack; +import io.netty.buffer.ByteBuf; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; + +import javax.annotation.Nullable; +import java.io.*; + +public class CraftingCPUStatus implements Comparable { + + @Nullable + private final ICraftingCPU serverCluster; + private final String name; + private final int serial; + private final long storage; + private final long coprocessors; + private final long totalItems; + private final long remainingItems; + private final IAEItemStack crafting; + + public CraftingCPUStatus( ) + { + this.serverCluster = null; + this.name = "ERROR"; + this.serial = 0; + this.storage = 0; + this.coprocessors = 0; + this.totalItems = 0; + this.remainingItems = 0; + this.crafting = null; + } + + public CraftingCPUStatus( ICraftingCPU cluster, int serial ) + { + this.serverCluster = cluster; + this.name = cluster.getName(); + this.serial = serial; + if (cluster.isBusy()) + { + crafting = cluster.getFinalOutput(); + totalItems = cluster.getStartItemCount(); + remainingItems = cluster.getRemainingItemCount(); + } + else + { + crafting = null; + totalItems = 0; + remainingItems = 0; + } + this.storage = cluster.getAvailableStorage(); + this.coprocessors = cluster.getCoProcessors(); + } + + public CraftingCPUStatus( NBTTagCompound i ) + { + this.serverCluster = null; + this.name = i.getString( "name" ); + this.serial = i.getInteger( "serial" ); + this.storage = i.getLong("storage"); + this.coprocessors = i.getLong("coprocessors"); + this.totalItems = i.getLong("totalItems"); + this.remainingItems = i.getLong("remainingItems"); + this.crafting = i.hasKey( "crafting" ) ? AEItemStack.fromNBT( i.getCompoundTag( "crafting" ) ) : null; + } + + public CraftingCPUStatus(ByteBuf packet) throws IOException + { + this(readNBTFromPacket( packet )); + } + + private static NBTTagCompound readNBTFromPacket(ByteBuf packet) throws IOException + { + final int size = packet.readInt(); + final byte[] tagBytes = new byte[size]; + packet.readBytes( tagBytes ); + final ByteArrayInputStream di = new ByteArrayInputStream( tagBytes ); + return CompressedStreamTools.read( new DataInputStream( di )); + } + + public void writeToNBT( NBTTagCompound i ) + { + if (name != null && !name.isEmpty()) + { + i.setString( "name", name ); + } + i.setInteger( "serial", serial ); + i.setLong( "storage", storage ); + i.setLong( "coprocessors", coprocessors ); + i.setLong( "totalItems", totalItems ); + i.setLong( "remainingItems", remainingItems ); + if (crafting != null) + { + NBTTagCompound stack = new NBTTagCompound(); + crafting.writeToNBT( stack ); + i.setTag( "crafting", stack ); + } + } + + public void writeToPacket( ByteBuf i ) throws IOException + { + final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final DataOutputStream data = new DataOutputStream( bytes ); + + NBTTagCompound tag = new NBTTagCompound(); + this.writeToNBT( tag ); + CompressedStreamTools.write( tag, data ); + + final byte[] tagBytes = bytes.toByteArray(); + final int size = tagBytes.length; + + i.writeInt( size ); + i.writeBytes( tagBytes ); + } + + @Nullable + public ICraftingCPU getServerCluster() + { + return serverCluster; + } + + public String getName() + { + return name; + } + + public int getSerial() + { + return serial; + } + + public long getStorage() + { + return storage; + } + + public long getCoprocessors() + { + return coprocessors; + } + + public long getTotalItems() + { + return totalItems; + } + + public long getRemainingItems() + { + return remainingItems; + } + + public IAEItemStack getCrafting() + { + return crafting; + } + + @Override + public int compareTo( CraftingCPUStatus o ) + { + final int a = ItemSorters.compareLong( o.getCoprocessors(), this.getCoprocessors() ); + if( a != 0 ) + { + return a; + } + return ItemSorters.compareLong( o.getStorage(), this.getStorage() ); + } + + public String formatStorage() + { + long val = getStorage(); + if (val > 4_000_000_000_000L) + { + return String.format("%dT", val / 1024 / 1024 / 1024 / 1024); + } + else if (val > 4_000_000_000L) + { + return String.format("%dG", val / 1024 / 1024 / 1024); + } + else if (val > 4_000_000L) + { + return String.format("%dM", val / 1024 / 1024); + } + else if (val > 4_000L) + { + return String.format("%dk", val / 1024); + } + else + { + return Long.toString( val ); + } + } +} diff --git a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java index 8d32bc228..1aefa289f 100644 --- a/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java +++ b/src/main/java/appeng/core/sync/AppEngPacketHandlerBase.java @@ -84,7 +84,9 @@ public class AppEngPacketHandlerBase PACKET_FLUID_TANK( PacketFluidSlot.class ), - PACKET_INFORM_PLAYER( PacketInformPlayer.class ); + PACKET_INFORM_PLAYER( PacketInformPlayer.class ), + + PACKET_CRAFTING_CPUS_UPDATE( PacketCraftingCPUsUpdate.class); private final Class packetClass; diff --git a/src/main/java/appeng/core/sync/network/NetworkHandler.java b/src/main/java/appeng/core/sync/network/NetworkHandler.java index d714fbe03..384329a15 100644 --- a/src/main/java/appeng/core/sync/network/NetworkHandler.java +++ b/src/main/java/appeng/core/sync/network/NetworkHandler.java @@ -34,7 +34,7 @@ import appeng.core.sync.AppEngPacket; public class NetworkHandler { - private static NetworkHandler instance; + public static NetworkHandler instance; private final FMLEventChannel ec; private final String myChannelName; diff --git a/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java b/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java new file mode 100644 index 000000000..0a795d771 --- /dev/null +++ b/src/main/java/appeng/core/sync/packets/PacketCraftingCPUsUpdate.java @@ -0,0 +1,63 @@ +package appeng.core.sync.packets; + +import appeng.client.gui.implementations.GuiCraftingStatus; +import appeng.container.implementations.CraftingCPUStatus; +import appeng.core.sync.AppEngPacket; +import appeng.core.sync.network.INetworkInfo; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.player.EntityPlayer; + +import java.io.IOException; +import java.util.Collection; + +public class PacketCraftingCPUsUpdate extends AppEngPacket { + + private final CraftingCPUStatus[] cpus; + + public PacketCraftingCPUsUpdate( final ByteBuf stream ) + { + int count = stream.readInt(); + cpus = new CraftingCPUStatus[count]; + for( int i = 0; i < count; i++ ) + { + try + { + cpus[i] = new CraftingCPUStatus( stream ); + } + catch( IOException e ) + { + cpus[i] = new CraftingCPUStatus( ); + } + } + } + + public PacketCraftingCPUsUpdate( final Collection cpus ) throws IOException + { + this.cpus = cpus.toArray( new CraftingCPUStatus[0] ); + + final ByteBuf data = Unpooled.buffer(); + data.writeInt( this.getPacketID() ); + data.writeInt( this.cpus.length ); + for( CraftingCPUStatus cpu : this.cpus ) + { + cpu.writeToPacket( data ); + } + this.configureWrite( data ); + } + + @Override + public void clientPacketData(final INetworkInfo network, final AppEngPacket packet, final EntityPlayer player ) + { + final GuiScreen gs = Minecraft.getMinecraft().currentScreen; + + if( gs instanceof GuiCraftingStatus) + { + GuiCraftingStatus gui = (GuiCraftingStatus) gs; + gui.postCPUUpdate( 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 52fb51d76..cbf473f1b 100644 --- a/src/main/java/appeng/core/sync/packets/PacketValueConfig.java +++ b/src/main/java/appeng/core/sync/packets/PacketValueConfig.java @@ -112,10 +112,10 @@ public class PacketValueConfig extends AppEngPacket final IMouseWheelItem si = (IMouseWheelItem) is.getItem(); si.onWheel( is, this.Value.equals( "WheelUp" ) ); } - else if( this.Name.equals( "Terminal.Cpu" ) && c instanceof ContainerCraftingStatus ) + else if( this.Name.equals( "Terminal.Cpu.Set" ) && c instanceof ContainerCraftingStatus ) { final ContainerCraftingStatus qk = (ContainerCraftingStatus) c; - qk.cycleCpu( this.Value.equals( "Next" ) ); + qk.selectCPU( Integer.parseInt( this.Value ) ); } else if( this.Name.equals( "Terminal.Cpu" ) && c instanceof ContainerCraftConfirm ) { diff --git a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java index fa7ccb23b..2bbf85fa1 100644 --- a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java +++ b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java @@ -120,6 +120,12 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU this.max = max; } + @Override + public IAEItemStack getFinalOutput() + { + return finalOutput; + } + public boolean isDestroyed() { return this.isDestroyed; @@ -1432,11 +1438,13 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU return this.elapsedTime; } + @Override public long getRemainingItemCount() { return this.remainingItemCount; } + @Override public long getStartItemCount() { return this.startItemCount; diff --git a/src/main/java/appeng/util/ItemSorters.java b/src/main/java/appeng/util/ItemSorters.java index 0893eeee7..3ff5349d3 100644 --- a/src/main/java/appeng/util/ItemSorters.java +++ b/src/main/java/appeng/util/ItemSorters.java @@ -89,6 +89,19 @@ public class ItemSorters } } + public static int compareLong( final long a, final long b ) + { + if( a == b ) + { + return 0; + } + if( a < b ) + { + return -1; + } + return 1; + } + private static SortDir getDirection() { return Direction; diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/cpu_selector.png b/src/main/resources/assets/appliedenergistics2/textures/guis/cpu_selector.png new file mode 100644 index 000000000..3c898c437 Binary files /dev/null and b/src/main/resources/assets/appliedenergistics2/textures/guis/cpu_selector.png differ