diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java index 99132df53dd..90c43f781a0 100644 --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -187,6 +187,12 @@ protected IMultipleTankHandler getOutputTank() { return metaTileEntity.getExportFluids(); } + public Recipe getParallelRecipe() { + if (previousRecipe == null) return null; + return findParallelRecipe(previousRecipe, getInputInventory(), getInputTank(), + getOutputInventory(), getOutputTank(), getMaxParallelVoltage(), getParallelLimit()); + } + /** * @return true if energy is consumed by this Recipe Logic, otherwise false */ diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java index 1fccce2d897..1e581113c0d 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java @@ -166,7 +166,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java index 9ce6514702b..7d241836797 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java @@ -151,7 +151,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { .addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage())) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } protected void configureWarningText(MultiblockUIBuilder builder) { diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java index 0ccda35523e..53cf2b3f0ac 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java @@ -118,7 +118,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { }) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java index 08304c4db60..73903a160f8 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java @@ -2,23 +2,38 @@ import gregtech.api.GTValues; import gregtech.api.capability.IEnergyContainer; +import gregtech.api.capability.impl.AbstractRecipeLogic; +import gregtech.api.mui.GTByteBufAdapters; +import gregtech.api.mui.drawable.GTObjectDrawable; +import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.chance.boost.ChanceBoostFunction; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.util.FluidStackHashStrategy; +import gregtech.api.util.GTUtility; +import gregtech.api.util.ItemStackHashStrategy; import gregtech.api.util.KeyUtil; import gregtech.api.util.TextFormattingUtil; import gregtech.common.ConfigHolder; +import net.minecraft.item.ItemStack; import net.minecraft.network.PacketBuffer; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.FluidStack; import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.api.drawable.IRichTextBuilder; +import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters; import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer; import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer; import com.cleanroommc.modularui.value.sync.PanelSyncManager; import com.cleanroommc.modularui.value.sync.SyncHandler; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.objects.Object2LongLinkedOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,6 +61,8 @@ public class MultiblockUIBuilder { private final InternalSyncHandler syncHandler = new InternalSyncHandler(); private final KeyManager manager = new InternalKeyManager(); + private static final int DEFAULT_MAX_RECIPE_LINES = 25; + @Nullable private InternalSyncer syncer; @@ -313,17 +330,23 @@ public MultiblockUIBuilder addIdlingLine(boolean checkState) { } /** - * Adds a simple progress line that displays progress as a percentage. + * Adds a progress line that displays recipe progress as "time / total time (percentage)". *
* Added if structure is formed and the machine is active. * - * @param progressPercent Progress formatted as a range of [0,1] representing the progress of the recipe. + * @param progress current progress. + * @param maxProgress total progress to be made. */ - public MultiblockUIBuilder addProgressLine(double progressPercent) { + public MultiblockUIBuilder addProgressLine(int progress, int maxProgress) { if (!isStructureFormed || !isActive) return this; - addKey(KeyUtil.lang(TextFormatting.GRAY, - "gregtech.multiblock.progress", - (int) (getSyncer().syncDouble(progressPercent) * 100))); + + progress = getSyncer().syncInt(progress); + maxProgress = getSyncer().syncInt(maxProgress); + + addKey(KeyUtil.lang(TextFormatting.WHITE, "gregtech.multiblock.recipe_progress", + String.format("%,3.2f", (float) progress / 20), + String.format("%,3.2f", (float) maxProgress / 20), + String.format("%,3.1f", (float) progress / maxProgress * 100f))); return this; } @@ -484,6 +507,201 @@ public MultiblockUIBuilder addRecipeMapLine(RecipeMap map) { return this; } + /** + * Adds the current outputs of a recipe from recipe logic. Items then fluids. + * + * @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from. + */ + public MultiblockUIBuilder addRecipeOutputLine(@NotNull AbstractRecipeLogic arl) { + return addRecipeOutputLine(arl, DEFAULT_MAX_RECIPE_LINES); + } + + /** + * Adds the current outputs of a recipe from recipe logic. Items then fluids. + * + * @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from. + * @param maxLines the maximum number of lines to print until truncating with {@code ...} + */ + public MultiblockUIBuilder addRecipeOutputLine(AbstractRecipeLogic arl, int maxLines) { + // todo doing this every tick on the server is probably not good + Recipe recipe = arl.getParallelRecipe(); + + if (getSyncer().syncBoolean(recipe == null)) return this; + if (getSyncer().syncBoolean(arl.getRecipeMap() == null)) return this; + + // noinspection DataFlowIssue + long eut = getSyncer().syncLong(() -> recipe.getEUt()); + long maxVoltage = getSyncer().syncLong(arl.getMaximumOverclockVoltage()); + + List itemOutputs = new ArrayList<>(); + List chancedItemOutputs = new ArrayList<>(); + List fluidOutputs = new ArrayList<>(); + List chancedFluidOutputs = new ArrayList<>(); + + if (isServer()) { + // recipe is checked indirectly for null + // noinspection DataFlowIssue + itemOutputs.addAll(recipe.getOutputs()); + chancedItemOutputs.addAll(recipe.getChancedOutputs().getChancedEntries()); + fluidOutputs.addAll(recipe.getFluidOutputs()); + chancedFluidOutputs.addAll(recipe.getChancedFluidOutputs().getChancedEntries()); + } + + itemOutputs = getSyncer().syncCollection(itemOutputs, ByteBufAdapters.ITEM_STACK); + fluidOutputs = getSyncer().syncCollection(fluidOutputs, ByteBufAdapters.FLUID_STACK); + chancedItemOutputs = getSyncer().syncCollection(chancedItemOutputs, GTByteBufAdapters.CHANCED_ITEM_OUTPUT); + chancedFluidOutputs = getSyncer().syncCollection(chancedFluidOutputs, GTByteBufAdapters.CHANCED_FLUID_OUTPUT); + + addKey(KeyUtil.string(TextFormatting.GRAY, "Producing: "), Operation.NEW_LINE); + + var chanceFunction = arl.getRecipeMap().getChanceFunction(); + int recipeTier = GTUtility.getTierByVoltage(eut); + int machineTier = GTUtility.getOCTierByVoltage(maxVoltage); + + // items + + Object2LongMap itemMap = new Object2LongLinkedOpenCustomHashMap<>( + ItemStackHashStrategy.comparingAllButCount()); + + for (ItemStack stack : itemOutputs) { + itemMap.merge(stack, (long) stack.getCount(), Long::sum); + } + + for (var stack : itemMap.keySet()) { + addItemOutputLine(stack, itemMap.getLong(stack), arl.getMaxProgress()); + } + + for (var chancedItemOutput : chancedItemOutputs) { + addChancedItemOutputLine(chancedItemOutput, chanceFunction, recipeTier, machineTier, arl.getMaxProgress()); + } + + // fluids + + Object2LongMap fluidMap = new Object2LongLinkedOpenCustomHashMap<>( + FluidStackHashStrategy.comparingAllButAmount); + + for (FluidStack stack : fluidOutputs) { + fluidMap.merge(stack, (long) stack.amount, Long::sum); + } + + for (var stack : fluidMap.keySet()) { + addFluidOutputLine(stack, fluidMap.getLong(stack), arl.getMaxProgress()); + } + + for (var chancedFluidOutput : chancedFluidOutputs) { + addChancedFluidOutputLine(chancedFluidOutput, chanceFunction, recipeTier, machineTier, + arl.getMaxProgress()); + } + return this; + } + + /** + * Add an item output of a recipe to the display. + * + * @param stack the {@link ItemStack} to display. + * @param recipeLength the recipe length, in ticks. + */ + private void addItemOutputLine(@NotNull ItemStack stack, long count, int recipeLength) { + IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName()); + IKey amount = KeyUtil.number(TextFormatting.GOLD, count); + IKey rate = KeyUtil.string(TextFormatting.WHITE, + formatRecipeRate(getSyncer().syncInt(recipeLength), count)); + + addKey(new GTObjectDrawable(stack, count) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD); + // addKey(IKey.SPACE, Operation.ADD); + } + + /** + * Add the fluid outputs of a recipe to the display. + * + * @param stack a {@link FluidStack}s to display. + * @param recipeLength the recipe length, in ticks. + */ + private void addFluidOutputLine(@NotNull FluidStack stack, long count, int recipeLength) { + IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack); + IKey amount = KeyUtil.number(TextFormatting.GOLD, count); + IKey rate = KeyUtil.string(TextFormatting.WHITE, + formatRecipeRate(getSyncer().syncInt(recipeLength), count)); + + addKey(new GTObjectDrawable(stack, count) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD); + // addKey(IKey.SPACE, Operation.ADD); + } + + /** + * Add a chanced item output of a recipe to the display. + * + * @param chancedItemOutput chanced item output + * @param boostFunction function to calculate boosted chance + * @param recipeTier voltage tier of the recipe + * @param machineTier machine tier + * @param recipeLength max duration of the recipe + */ + private void addChancedItemOutputLine(@NotNull ChancedItemOutput chancedItemOutput, + ChanceBoostFunction boostFunction, + int recipeTier, int machineTier, int recipeLength) { + var stack = chancedItemOutput.getIngredient(); + IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName()); + IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.getCount()); + IKey rate = KeyUtil.string(TextFormatting.WHITE, + formatRecipeRate(getSyncer().syncInt(recipeLength), stack.getCount())); + + addKey(new GTObjectDrawable(chancedItemOutput, stack.getCount()) + .setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier)) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD); + // addKey(IKey.SPACE, Operation.ADD); + } + + /** + * Add a chanced fluid output of a recipe to the display. + * + * @param chancedFluidOutput chanced fluid output + * @param boostFunction function to calculate boosted chance + * @param recipeTier voltage tier of the recipe + * @param machineTier machine tier + * @param recipeLength max duration of the recipe + */ + private void addChancedFluidOutputLine(@NotNull ChancedFluidOutput chancedFluidOutput, + ChanceBoostFunction boostFunction, + int recipeTier, int machineTier, int recipeLength) { + var stack = chancedFluidOutput.getIngredient(); + IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack); + IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.amount); + IKey rate = KeyUtil.string(TextFormatting.WHITE, + formatRecipeRate(getSyncer().syncInt(recipeLength), stack.amount)); + + addKey(new GTObjectDrawable(chancedFluidOutput, stack.amount) + .setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier)) + .asIcon() + .asHoverable() + .addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD); + // addKey(IKey.SPACE, Operation.ADD); + } + + private static String formatRecipeRate(int recipeLength, long amount) { + float perSecond = ((float) amount / recipeLength) * 20f; + + String rate; + if (perSecond > 1) { + rate = "(" + String.format("%,.2f", perSecond).replaceAll("\\.?0+$", "") + "/s)"; + } else { + rate = "(" + String.format("%,.2f", 1 / (perSecond)).replaceAll("\\.?0+$", "") + "s/ea)"; + } + + return rate; + } + + private static IKey formatRecipeData(IKey name, IKey amount, IKey rate) { + return IKey.comp(name, KeyUtil.string(TextFormatting.WHITE, " x "), amount, IKey.SPACE, rate); + } + /** Insert an empty line into the text list. */ public MultiblockUIBuilder addEmptyLine() { addKey(IKey.LINE_FEED); diff --git a/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java index cc96b789138..58cb0eee1bf 100644 --- a/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java +++ b/src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java @@ -1,10 +1,11 @@ package gregtech.api.metatileentity.multiblock.ui; +import gregtech.api.mui.GTByteBufAdapters; + import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters; import com.cleanroommc.modularui.utils.serialization.IByteBufAdapter; import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer; import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer; -import com.cleanroommc.modularui.utils.serialization.IEquals; import org.jetbrains.annotations.NotNull; import java.math.BigInteger; @@ -16,11 +17,6 @@ public interface UISyncer { - IByteBufAdapter BIG_INT = ByteBufAdapters.makeAdapter( - buffer -> new BigInteger(buffer.readByteArray()), - (buffer, value) -> buffer.writeByteArray(value.toByteArray()), - IEquals.defaultTester()); - /** * Calls the supplier server side only so there's no potential NPEs for the client * @@ -98,7 +94,7 @@ default float syncFloat(float initial) { } default BigInteger syncBigInt(BigInteger initial) { - return syncObject(initial, BIG_INT); + return syncObject(initial, GTByteBufAdapters.BIG_INT); } T syncObject(T initial, IByteBufSerializer serializer, IByteBufDeserializer deserializer); diff --git a/src/main/java/gregtech/api/mui/GTByteBufAdapters.java b/src/main/java/gregtech/api/mui/GTByteBufAdapters.java new file mode 100644 index 00000000000..86757aae225 --- /dev/null +++ b/src/main/java/gregtech/api/mui/GTByteBufAdapters.java @@ -0,0 +1,54 @@ +package gregtech.api.mui; + +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; + +import net.minecraft.network.PacketBuffer; + +import com.cleanroommc.modularui.utils.serialization.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.math.BigInteger; + +public class GTByteBufAdapters { + + public static final IByteBufAdapter CHANCED_ITEM_OUTPUT = makeAdapter( + ChancedItemOutput::fromBuffer, ChancedItemOutput::toBuffer); + + public static final IByteBufAdapter CHANCED_FLUID_OUTPUT = makeAdapter( + ChancedFluidOutput::fromBuffer, ChancedFluidOutput::toBuffer); + + public static final IByteBufAdapter BIG_INT = makeAdapter( + buffer -> new BigInteger(buffer.readByteArray()), + (buffer, value) -> buffer.writeByteArray(value.toByteArray())); + + public static IByteBufAdapter makeAdapter(@NotNull IByteBufDeserializer deserializer, + @NotNull IByteBufSerializer serializer) { + return makeAdapter(deserializer, serializer, IEquals.defaultTester()); + } + + public static IByteBufAdapter makeAdapter(@NotNull IByteBufDeserializer deserializer, + @NotNull IByteBufSerializer serializer, + @Nullable IEquals equals) { + final IEquals tester = equals != null ? equals : IEquals.defaultTester(); + return new IByteBufAdapter<>() { + + @Override + public T deserialize(PacketBuffer buffer) throws IOException { + return deserializer.deserialize(buffer); + } + + @Override + public void serialize(PacketBuffer buffer, T u) throws IOException { + serializer.serialize(buffer, u); + } + + @Override + public boolean areEqual(@NotNull T t1, @NotNull T t2) { + return tester.areEqual(t1, t2); + } + }; + } +} diff --git a/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java b/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java new file mode 100644 index 00000000000..775c1b2cd24 --- /dev/null +++ b/src/main/java/gregtech/api/mui/drawable/GTObjectDrawable.java @@ -0,0 +1,91 @@ +package gregtech.api.mui.drawable; + +import gregtech.api.mui.GTGuiTextures; +import gregtech.api.recipes.chance.boost.BoostableChanceEntry; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.GuiDraw; +import com.cleanroommc.modularui.drawable.Icon; +import com.cleanroommc.modularui.drawable.text.TextRenderer; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.NumberFormat; +import com.cleanroommc.modularui.widget.Widget; + +import java.util.function.Function; + +public class GTObjectDrawable implements IDrawable { + + private static final TextRenderer renderer = new TextRenderer(); + + private final Object object; + private final long amount; + private Function, Integer> boostFunction; + + public GTObjectDrawable(Object object, long amount) { + this.object = object; + this.amount = amount; + } + + public GTObjectDrawable setBoostFunction(Function, Integer> boostFunction) { + this.boostFunction = boostFunction; + return this; + } + + static { + renderer.setScale(0.5f); + renderer.setShadow(true); + renderer.setColor(Color.WHITE.main); + } + + @Override + public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + renderer.setAlignment(Alignment.BottomRight, width - 1, height - 1); + drawObject(object, context, x, y, width, height, widgetTheme); + if (amount > 1) { + renderer.setPos(x + 1, y + 1); + String amount = NumberFormat.formatWithMaxDigits(this.amount, 3); + if (object instanceof FluidStack) amount += "L"; + renderer.draw(amount); + } + } + + private void drawObject(Object object, GuiContext context, int x, int y, int width, int height, + WidgetTheme widgetTheme) { + if (object instanceof ItemStack stack) { + GTGuiTextures.SLOT.draw(context, x, y, width, height, widgetTheme); + GuiDraw.drawItem(stack, x + 1, y + 1, width - 2, height - 2); + } else if (object instanceof FluidStack stack) { + GTGuiTextures.FLUID_SLOT.draw(context, x, y, width, height, widgetTheme); + GuiDraw.drawFluidTexture(stack, x + 1, y + 1, width - 2, height - 2, 0); + } else if (object instanceof BoostableChanceEntryentry) { + drawObject(entry.getIngredient(), context, x, y, width, height, widgetTheme); + String chance = "~" + this.boostFunction.apply(entry) / 100 + "%"; + if (amount > 1) y -= 4; + renderer.setPos(x + 1, y + 1); + renderer.draw(chance); + } + } + + @Override + public Icon asIcon() { + return IDrawable.super.asIcon().size(18); + } + + @Override + public Widget asWidget() { + return IDrawable.super.asWidget().size(18); + } + + public Object getObject() { + if (object instanceof BoostableChanceEntryentry) { + return entry.getIngredient(); + } + return object; + } +} diff --git a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java index 81a145fe1c8..5d59e6cebfa 100644 --- a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java +++ b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedFluidOutput.java @@ -2,10 +2,14 @@ import gregtech.api.recipes.chance.output.BoostableChanceOutput; +import net.minecraft.network.PacketBuffer; import net.minecraftforge.fluids.FluidStack; +import com.cleanroommc.modularui.network.NetworkUtils; import org.jetbrains.annotations.NotNull; +import java.util.Objects; + /** * Implementation for a chanced fluid output */ @@ -29,4 +33,15 @@ public String toString() { ", chanceBoost=" + getChanceBoost() + '}'; } + + public static ChancedFluidOutput fromBuffer(PacketBuffer buffer) { + return new ChancedFluidOutput(Objects.requireNonNull(NetworkUtils.readFluidStack(buffer)), buffer.readVarInt(), + buffer.readVarInt()); + } + + public static void toBuffer(PacketBuffer buffer, ChancedFluidOutput value) { + NetworkUtils.writeFluidStack(buffer, value.getIngredient()); + buffer.writeVarInt(value.getChance()); + buffer.writeVarInt(value.getChanceBoost()); + } } diff --git a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java index b49e640d942..a186a01b392 100644 --- a/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java +++ b/src/main/java/gregtech/api/recipes/chance/output/impl/ChancedItemOutput.java @@ -4,7 +4,9 @@ import gregtech.api.util.GTStringUtils; import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketBuffer; +import com.cleanroommc.modularui.network.NetworkUtils; import org.jetbrains.annotations.NotNull; /** @@ -29,4 +31,14 @@ public String toString() { ", chanceBoost=" + getChanceBoost() + '}'; } + + public static ChancedItemOutput fromBuffer(PacketBuffer buffer) { + return new ChancedItemOutput(NetworkUtils.readItemStack(buffer), buffer.readVarInt(), buffer.readVarInt()); + } + + public static void toBuffer(PacketBuffer buffer, ChancedItemOutput value) { + NetworkUtils.writeItemStack(buffer, value.getIngredient()); + buffer.writeVarInt(value.getChance()); + buffer.writeVarInt(value.getChanceBoost()); + } } diff --git a/src/main/java/gregtech/api/util/FluidStackHashStrategy.java b/src/main/java/gregtech/api/util/FluidStackHashStrategy.java new file mode 100644 index 00000000000..8aacb2d39d5 --- /dev/null +++ b/src/main/java/gregtech/api/util/FluidStackHashStrategy.java @@ -0,0 +1,68 @@ +package gregtech.api.util; + +import net.minecraftforge.fluids.FluidStack; + +import it.unimi.dsi.fastutil.Hash; + +import java.util.Objects; + +public interface FluidStackHashStrategy extends Hash.Strategy { + + static FluidStackHashStrategyBuilder builder() { + return new FluidStackHashStrategyBuilder(); + } + + FluidStackHashStrategy comparingAll = builder() + .compareFluid() + .compareAmount() + .compareNBT() + .build(); + + FluidStackHashStrategy comparingAllButAmount = builder() + .compareFluid() + .compareNBT() + .build(); + + class FluidStackHashStrategyBuilder { + + private boolean fluid, amount, nbt = false; + + public FluidStackHashStrategyBuilder compareFluid() { + this.fluid = true; + return this; + } + + public FluidStackHashStrategyBuilder compareAmount() { + this.amount = true; + return this; + } + + public FluidStackHashStrategyBuilder compareNBT() { + this.nbt = true; + return this; + } + + public FluidStackHashStrategy build() { + return new FluidStackHashStrategy() { + + @Override + public int hashCode(FluidStack other) { + return other == null ? 0 : Objects.hash( + fluid ? other.getFluid() : null, + amount ? other.amount : null, + nbt ? other.tag : null); + } + + @Override + public boolean equals(FluidStack a, FluidStack b) { + if (a == null) return b == null; + if (b == null) return false; + + return (!fluid || a.getFluid() == b.getFluid()) && + (!amount || a.amount == b.amount) && + (!nbt || Objects.equals(a.tag, b.tag)); + } + }; + } + } +} diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java index 0b1edad8de7..f4584488be9 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCleanroom.java @@ -504,7 +504,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { cleanState)); } }) - .addProgressLine(getProgressPercent() / 100.0) + .addProgressLine(getProgress(), getMaxProgress()) .addWorkingStatusLine(); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java index 62d05fd2aa2..a20c11176c1 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityCrackingUnit.java @@ -102,7 +102,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { }) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java index d57007262aa..b70680943f2 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityElectricBlastFurnace.java @@ -78,7 +78,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { .addCustom(this::addHeatCapacity) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } private void addHeatCapacity(KeyManager keyManager, UISyncer syncer) { diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java index b0ac1aa59ec..d6ce9f408e1 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityFluidDrill.java @@ -195,7 +195,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { } } }) - .addProgressLine(minerLogic.getProgressPercent()) + .addProgressLine(minerLogic.getProgressTime(), FluidDrillLogic.MAX_PROGRESS) .addWorkingStatusLine(); } diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java index 41ebaeb15a6..382a54719b5 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityMultiSmelter.java @@ -91,7 +91,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { } }) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java index 7355e0c91eb..fbb5d7faeab 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityProcessingArray.java @@ -170,7 +170,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { }) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @SideOnly(Side.CLIENT) diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java index 8734ee35a67..6213867dd68 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityPyrolyseOven.java @@ -128,7 +128,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { }) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()) + .addRecipeOutputLine(recipeMapWorkable); } @Override diff --git a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java index db325ef9524..5e2bed97a52 100644 --- a/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java +++ b/src/main/java/gregtech/common/metatileentities/multi/electric/MetaTileEntityResearchStation.java @@ -233,7 +233,7 @@ protected void configureDisplayText(MultiblockUIBuilder builder) { .addComputationUsageExactLine(getRecipeMapWorkable().getCurrentDrawnCWUt()) .addParallelsLine(recipeMapWorkable.getParallelLimit()) .addWorkingStatusLine() - .addProgressLine(recipeMapWorkable.getProgressPercent()); + .addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress()); } @Override diff --git a/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java index 55e82d4f2b0..12ee6435302 100644 --- a/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java +++ b/src/main/java/gregtech/common/mui/widget/ScrollableTextWidget.java @@ -1,5 +1,8 @@ package gregtech.common.mui.widget; +import gregtech.api.mui.drawable.GTObjectDrawable; +import gregtech.mixins.mui2.IconAccessor; + import com.cleanroommc.modularui.api.GuiAxis; import com.cleanroommc.modularui.api.drawable.IHoverable; import com.cleanroommc.modularui.api.drawable.IRichTextBuilder; @@ -7,9 +10,11 @@ import com.cleanroommc.modularui.api.layout.IViewportStack; import com.cleanroommc.modularui.api.widget.IGuiAction; import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.DelegateIcon; import com.cleanroommc.modularui.drawable.Stencil; import com.cleanroommc.modularui.drawable.text.RichText; import com.cleanroommc.modularui.drawable.text.TextRenderer; +import com.cleanroommc.modularui.integration.jei.JeiIngredientProvider; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.screen.RichTooltip; import com.cleanroommc.modularui.screen.viewport.ModularGuiContext; @@ -24,12 +29,14 @@ import java.util.function.Consumer; public class ScrollableTextWidget extends Widget - implements IRichTextBuilder, Interactable, IViewport { + implements IRichTextBuilder, Interactable, IViewport, + JeiIngredientProvider { private final RichText text = new RichText(); private Consumer builder; private boolean dirty = false; private boolean autoUpdate = false; + private Object lastIngredient; private final ScrollArea scroll = new ScrollArea(); private final TextRenderer renderer = new TextRenderer(); @@ -66,6 +73,13 @@ public void drawForeground(ModularGuiContext context) { tooltip.draw(context); } } + if (getHoveredElement() instanceof DelegateIcon delegateIcon && + delegateIcon.findRootDelegate() instanceof IconAccessor accessor && + accessor.getDrawable() instanceof GTObjectDrawable objectDrawable) { + lastIngredient = objectDrawable.getObject(); + } else { + lastIngredient = null; + } } @Override @@ -207,4 +221,9 @@ public ScrollableTextWidget textBuilder(Consumer builder) { markDirty(); return this; } + + @Override + public @Nullable Object getIngredient() { + return this.lastIngredient; + } } diff --git a/src/main/java/gregtech/mixins/mui2/IconAccessor.java b/src/main/java/gregtech/mixins/mui2/IconAccessor.java new file mode 100644 index 00000000000..59e2ba009e4 --- /dev/null +++ b/src/main/java/gregtech/mixins/mui2/IconAccessor.java @@ -0,0 +1,13 @@ +package gregtech.mixins.mui2; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.Icon; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = Icon.class, remap = false) +public interface IconAccessor { + + @Accessor() + IDrawable getDrawable(); +} diff --git a/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java b/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java new file mode 100644 index 00000000000..79c44c7d20b --- /dev/null +++ b/src/main/java/gregtech/mixins/mui2/RichTextCompilerMixin.java @@ -0,0 +1,41 @@ +package gregtech.mixins.mui2; + +import net.minecraft.client.gui.FontRenderer; + +import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.drawable.text.RichTextCompiler; +import com.llamalad7.mixinextras.sugar.Local; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; + +@Mixin(value = RichTextCompiler.class, remap = false) +public class RichTextCompilerMixin { + + @Shadow + private int x; + + @Shadow + private FontRenderer fr; + + @ModifyArg(method = "trimRight", + at = @At(value = "INVOKE", + target = "Ljava/lang/String;substring(II)Ljava/lang/String;"), + index = 1) + private static int fixTrim(int beginIndex) { + return beginIndex + 1; + } + + @Inject(method = "compile", + at = @At(value = "INVOKE", + target = "Lcom/cleanroommc/modularui/drawable/text/RichTextCompiler;addLineElement(Ljava/lang/Object;)V", + ordinal = 0)) + private void moveXString(List raw, CallbackInfo ci, @Local IKey key) { + x += fr.getStringWidth(key.get()); + } +} diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index ff66c289421..4e5c8778bb3 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -5734,6 +5734,7 @@ gregtech.multiblock.idling=Idling. gregtech.multiblock.not_enough_energy=Machine needs more energy! gregtech.multiblock.not_enough_energy_output=Energy Dynamo Tier Too Low! gregtech.multiblock.progress=Progress: %s%% +gregtech.multiblock.recipe_progress=Progress: %ss / %ss (%s%%) gregtech.multiblock.invalid_structure=§cInvalid structure. gregtech.multiblock.invalid_structure.tooltip=This block is a controller of the multiblock structure. For building help, see structure template in JEI. gregtech.multiblock.validation_failed=Invalid amount of inputs/outputs. diff --git a/src/main/resources/mixins.gregtech.mui2.json b/src/main/resources/mixins.gregtech.mui2.json index f313fa2b32d..dc9772d257f 100644 --- a/src/main/resources/mixins.gregtech.mui2.json +++ b/src/main/resources/mixins.gregtech.mui2.json @@ -8,7 +8,9 @@ "maxShiftBy": 10 }, "mixins": [ - "ModularPanelMixin" + "IconAccessor", + "ModularPanelMixin", + "RichTextCompilerMixin" ], "client": [ "LangKeyMixin"