Skip to content

Commit 1c82647

Browse files
Zorbatronghzdude
andcommitted
Display recipe outputs in multiblocks - MUI2 (#2714)
Co-authored-by: Ghzdude <44148655+ghzdude@users.noreply.github.com>
1 parent e6cc483 commit 1c82647

24 files changed

+570
-26
lines changed

src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ protected IMultipleTankHandler getOutputTank() {
187187
return metaTileEntity.getExportFluids();
188188
}
189189

190+
public Recipe getParallelRecipe() {
191+
if (previousRecipe == null) return null;
192+
return findParallelRecipe(previousRecipe, getInputInventory(), getInputTank(),
193+
getOutputInventory(), getOutputTank(), getMaxParallelVoltage(), getParallelLimit());
194+
}
195+
190196
/**
191197
* @return true if energy is consumed by this Recipe Logic, otherwise false
192198
*/

src/main/java/gregtech/api/metatileentity/multiblock/MultiMapMultiblockController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
166166
.addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage()))
167167
.addParallelsLine(recipeMapWorkable.getParallelLimit())
168168
.addWorkingStatusLine()
169-
.addProgressLine(recipeMapWorkable.getProgressPercent());
169+
.addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
170+
.addRecipeOutputLine(recipeMapWorkable);
170171
}
171172

172173
@Override

src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapMultiblockController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
151151
.addEnergyTierLine(GTUtility.getTierByVoltage(recipeMapWorkable.getMaxVoltage()))
152152
.addParallelsLine(recipeMapWorkable.getParallelLimit())
153153
.addWorkingStatusLine()
154-
.addProgressLine(recipeMapWorkable.getProgressPercent());
154+
.addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
155+
.addRecipeOutputLine(recipeMapWorkable);
155156
}
156157

157158
protected void configureWarningText(MultiblockUIBuilder builder) {

src/main/java/gregtech/api/metatileentity/multiblock/RecipeMapSteamMultiblockController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ protected void configureDisplayText(MultiblockUIBuilder builder) {
118118
})
119119
.addParallelsLine(recipeMapWorkable.getParallelLimit())
120120
.addWorkingStatusLine()
121-
.addProgressLine(recipeMapWorkable.getProgressPercent());
121+
.addProgressLine(recipeMapWorkable.getProgress(), recipeMapWorkable.getMaxProgress())
122+
.addRecipeOutputLine(recipeMapWorkable);
122123
}
123124

124125
@Override

src/main/java/gregtech/api/metatileentity/multiblock/ui/MultiblockUIBuilder.java

Lines changed: 224 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,38 @@
22

33
import gregtech.api.GTValues;
44
import gregtech.api.capability.IEnergyContainer;
5+
import gregtech.api.capability.impl.AbstractRecipeLogic;
6+
import gregtech.api.mui.GTByteBufAdapters;
7+
import gregtech.api.mui.drawable.GTObjectDrawable;
8+
import gregtech.api.recipes.Recipe;
59
import gregtech.api.recipes.RecipeMap;
10+
import gregtech.api.recipes.chance.boost.ChanceBoostFunction;
11+
import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput;
12+
import gregtech.api.recipes.chance.output.impl.ChancedItemOutput;
13+
import gregtech.api.util.FluidStackHashStrategy;
14+
import gregtech.api.util.GTUtility;
15+
import gregtech.api.util.ItemStackHashStrategy;
616
import gregtech.api.util.KeyUtil;
717
import gregtech.api.util.TextFormattingUtil;
818
import gregtech.common.ConfigHolder;
919

20+
import net.minecraft.item.ItemStack;
1021
import net.minecraft.network.PacketBuffer;
1122
import net.minecraft.util.text.TextFormatting;
23+
import net.minecraftforge.fluids.FluidStack;
1224

1325
import com.cleanroommc.modularui.api.drawable.IDrawable;
1426
import com.cleanroommc.modularui.api.drawable.IKey;
1527
import com.cleanroommc.modularui.api.drawable.IRichTextBuilder;
28+
import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters;
1629
import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer;
1730
import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer;
1831
import com.cleanroommc.modularui.value.sync.PanelSyncManager;
1932
import com.cleanroommc.modularui.value.sync.SyncHandler;
2033
import io.netty.buffer.ByteBuf;
2134
import io.netty.buffer.Unpooled;
35+
import it.unimi.dsi.fastutil.objects.Object2LongLinkedOpenCustomHashMap;
36+
import it.unimi.dsi.fastutil.objects.Object2LongMap;
2237
import org.jetbrains.annotations.NotNull;
2338
import org.jetbrains.annotations.Nullable;
2439

@@ -46,6 +61,8 @@ public class MultiblockUIBuilder {
4661
private final InternalSyncHandler syncHandler = new InternalSyncHandler();
4762
private final KeyManager manager = new InternalKeyManager();
4863

64+
private static final int DEFAULT_MAX_RECIPE_LINES = 25;
65+
4966
@Nullable
5067
private InternalSyncer syncer;
5168

@@ -313,17 +330,23 @@ public MultiblockUIBuilder addIdlingLine(boolean checkState) {
313330
}
314331

315332
/**
316-
* Adds a simple progress line that displays progress as a percentage.
333+
* Adds a progress line that displays recipe progress as "time / total time (percentage)".
317334
* <br>
318335
* Added if structure is formed and the machine is active.
319336
*
320-
* @param progressPercent Progress formatted as a range of [0,1] representing the progress of the recipe.
337+
* @param progress current progress.
338+
* @param maxProgress total progress to be made.
321339
*/
322-
public MultiblockUIBuilder addProgressLine(double progressPercent) {
340+
public MultiblockUIBuilder addProgressLine(int progress, int maxProgress) {
323341
if (!isStructureFormed || !isActive) return this;
324-
addKey(KeyUtil.lang(TextFormatting.GRAY,
325-
"gregtech.multiblock.progress",
326-
(int) (getSyncer().syncDouble(progressPercent) * 100)));
342+
343+
progress = getSyncer().syncInt(progress);
344+
maxProgress = getSyncer().syncInt(maxProgress);
345+
346+
addKey(KeyUtil.lang(TextFormatting.WHITE, "gregtech.multiblock.recipe_progress",
347+
String.format("%,3.2f", (float) progress / 20),
348+
String.format("%,3.2f", (float) maxProgress / 20),
349+
String.format("%,3.1f", (float) progress / maxProgress * 100f)));
327350
return this;
328351
}
329352

@@ -484,6 +507,201 @@ public MultiblockUIBuilder addRecipeMapLine(RecipeMap<?> map) {
484507
return this;
485508
}
486509

510+
/**
511+
* Adds the current outputs of a recipe from recipe logic. Items then fluids.
512+
*
513+
* @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from.
514+
*/
515+
public MultiblockUIBuilder addRecipeOutputLine(@NotNull AbstractRecipeLogic arl) {
516+
return addRecipeOutputLine(arl, DEFAULT_MAX_RECIPE_LINES);
517+
}
518+
519+
/**
520+
* Adds the current outputs of a recipe from recipe logic. Items then fluids.
521+
*
522+
* @param arl an instance of an {@link AbstractRecipeLogic} to gather the outputs from.
523+
* @param maxLines the maximum number of lines to print until truncating with {@code ...}
524+
*/
525+
public MultiblockUIBuilder addRecipeOutputLine(AbstractRecipeLogic arl, int maxLines) {
526+
// todo doing this every tick on the server is probably not good
527+
Recipe recipe = arl.getParallelRecipe();
528+
529+
if (getSyncer().syncBoolean(recipe == null)) return this;
530+
if (getSyncer().syncBoolean(arl.getRecipeMap() == null)) return this;
531+
532+
// noinspection DataFlowIssue
533+
long eut = getSyncer().syncLong(() -> recipe.getEUt());
534+
long maxVoltage = getSyncer().syncLong(arl.getMaximumOverclockVoltage());
535+
536+
List<ItemStack> itemOutputs = new ArrayList<>();
537+
List<ChancedItemOutput> chancedItemOutputs = new ArrayList<>();
538+
List<FluidStack> fluidOutputs = new ArrayList<>();
539+
List<ChancedFluidOutput> chancedFluidOutputs = new ArrayList<>();
540+
541+
if (isServer()) {
542+
// recipe is checked indirectly for null
543+
// noinspection DataFlowIssue
544+
itemOutputs.addAll(recipe.getOutputs());
545+
chancedItemOutputs.addAll(recipe.getChancedOutputs().getChancedEntries());
546+
fluidOutputs.addAll(recipe.getFluidOutputs());
547+
chancedFluidOutputs.addAll(recipe.getChancedFluidOutputs().getChancedEntries());
548+
}
549+
550+
itemOutputs = getSyncer().syncCollection(itemOutputs, ByteBufAdapters.ITEM_STACK);
551+
fluidOutputs = getSyncer().syncCollection(fluidOutputs, ByteBufAdapters.FLUID_STACK);
552+
chancedItemOutputs = getSyncer().syncCollection(chancedItemOutputs, GTByteBufAdapters.CHANCED_ITEM_OUTPUT);
553+
chancedFluidOutputs = getSyncer().syncCollection(chancedFluidOutputs, GTByteBufAdapters.CHANCED_FLUID_OUTPUT);
554+
555+
addKey(KeyUtil.string(TextFormatting.GRAY, "Producing: "), Operation.NEW_LINE);
556+
557+
var chanceFunction = arl.getRecipeMap().getChanceFunction();
558+
int recipeTier = GTUtility.getTierByVoltage(eut);
559+
int machineTier = GTUtility.getOCTierByVoltage(maxVoltage);
560+
561+
// items
562+
563+
Object2LongMap<ItemStack> itemMap = new Object2LongLinkedOpenCustomHashMap<>(
564+
ItemStackHashStrategy.comparingAllButCount());
565+
566+
for (ItemStack stack : itemOutputs) {
567+
itemMap.merge(stack, (long) stack.getCount(), Long::sum);
568+
}
569+
570+
for (var stack : itemMap.keySet()) {
571+
addItemOutputLine(stack, itemMap.getLong(stack), arl.getMaxProgress());
572+
}
573+
574+
for (var chancedItemOutput : chancedItemOutputs) {
575+
addChancedItemOutputLine(chancedItemOutput, chanceFunction, recipeTier, machineTier, arl.getMaxProgress());
576+
}
577+
578+
// fluids
579+
580+
Object2LongMap<FluidStack> fluidMap = new Object2LongLinkedOpenCustomHashMap<>(
581+
FluidStackHashStrategy.comparingAllButAmount);
582+
583+
for (FluidStack stack : fluidOutputs) {
584+
fluidMap.merge(stack, (long) stack.amount, Long::sum);
585+
}
586+
587+
for (var stack : fluidMap.keySet()) {
588+
addFluidOutputLine(stack, fluidMap.getLong(stack), arl.getMaxProgress());
589+
}
590+
591+
for (var chancedFluidOutput : chancedFluidOutputs) {
592+
addChancedFluidOutputLine(chancedFluidOutput, chanceFunction, recipeTier, machineTier,
593+
arl.getMaxProgress());
594+
}
595+
return this;
596+
}
597+
598+
/**
599+
* Add an item output of a recipe to the display.
600+
*
601+
* @param stack the {@link ItemStack} to display.
602+
* @param recipeLength the recipe length, in ticks.
603+
*/
604+
private void addItemOutputLine(@NotNull ItemStack stack, long count, int recipeLength) {
605+
IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName());
606+
IKey amount = KeyUtil.number(TextFormatting.GOLD, count);
607+
IKey rate = KeyUtil.string(TextFormatting.WHITE,
608+
formatRecipeRate(getSyncer().syncInt(recipeLength), count));
609+
610+
addKey(new GTObjectDrawable(stack, count)
611+
.asIcon()
612+
.asHoverable()
613+
.addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
614+
// addKey(IKey.SPACE, Operation.ADD);
615+
}
616+
617+
/**
618+
* Add the fluid outputs of a recipe to the display.
619+
*
620+
* @param stack a {@link FluidStack}s to display.
621+
* @param recipeLength the recipe length, in ticks.
622+
*/
623+
private void addFluidOutputLine(@NotNull FluidStack stack, long count, int recipeLength) {
624+
IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack);
625+
IKey amount = KeyUtil.number(TextFormatting.GOLD, count);
626+
IKey rate = KeyUtil.string(TextFormatting.WHITE,
627+
formatRecipeRate(getSyncer().syncInt(recipeLength), count));
628+
629+
addKey(new GTObjectDrawable(stack, count)
630+
.asIcon()
631+
.asHoverable()
632+
.addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
633+
// addKey(IKey.SPACE, Operation.ADD);
634+
}
635+
636+
/**
637+
* Add a chanced item output of a recipe to the display.
638+
*
639+
* @param chancedItemOutput chanced item output
640+
* @param boostFunction function to calculate boosted chance
641+
* @param recipeTier voltage tier of the recipe
642+
* @param machineTier machine tier
643+
* @param recipeLength max duration of the recipe
644+
*/
645+
private void addChancedItemOutputLine(@NotNull ChancedItemOutput chancedItemOutput,
646+
ChanceBoostFunction boostFunction,
647+
int recipeTier, int machineTier, int recipeLength) {
648+
var stack = chancedItemOutput.getIngredient();
649+
IKey name = KeyUtil.string(TextFormatting.AQUA, stack.getDisplayName());
650+
IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.getCount());
651+
IKey rate = KeyUtil.string(TextFormatting.WHITE,
652+
formatRecipeRate(getSyncer().syncInt(recipeLength), stack.getCount()));
653+
654+
addKey(new GTObjectDrawable(chancedItemOutput, stack.getCount())
655+
.setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier))
656+
.asIcon()
657+
.asHoverable()
658+
.addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
659+
// addKey(IKey.SPACE, Operation.ADD);
660+
}
661+
662+
/**
663+
* Add a chanced fluid output of a recipe to the display.
664+
*
665+
* @param chancedFluidOutput chanced fluid output
666+
* @param boostFunction function to calculate boosted chance
667+
* @param recipeTier voltage tier of the recipe
668+
* @param machineTier machine tier
669+
* @param recipeLength max duration of the recipe
670+
*/
671+
private void addChancedFluidOutputLine(@NotNull ChancedFluidOutput chancedFluidOutput,
672+
ChanceBoostFunction boostFunction,
673+
int recipeTier, int machineTier, int recipeLength) {
674+
var stack = chancedFluidOutput.getIngredient();
675+
IKey name = KeyUtil.fluid(TextFormatting.AQUA, stack);
676+
IKey amount = KeyUtil.number(TextFormatting.GOLD, stack.amount);
677+
IKey rate = KeyUtil.string(TextFormatting.WHITE,
678+
formatRecipeRate(getSyncer().syncInt(recipeLength), stack.amount));
679+
680+
addKey(new GTObjectDrawable(chancedFluidOutput, stack.amount)
681+
.setBoostFunction(entry -> boostFunction.getBoostedChance(entry, recipeTier, machineTier))
682+
.asIcon()
683+
.asHoverable()
684+
.addTooltipLine(formatRecipeData(name, amount, rate)), Operation.ADD);
685+
// addKey(IKey.SPACE, Operation.ADD);
686+
}
687+
688+
private static String formatRecipeRate(int recipeLength, long amount) {
689+
float perSecond = ((float) amount / recipeLength) * 20f;
690+
691+
String rate;
692+
if (perSecond > 1) {
693+
rate = "(" + String.format("%,.2f", perSecond).replaceAll("\\.?0+$", "") + "/s)";
694+
} else {
695+
rate = "(" + String.format("%,.2f", 1 / (perSecond)).replaceAll("\\.?0+$", "") + "s/ea)";
696+
}
697+
698+
return rate;
699+
}
700+
701+
private static IKey formatRecipeData(IKey name, IKey amount, IKey rate) {
702+
return IKey.comp(name, KeyUtil.string(TextFormatting.WHITE, " x "), amount, IKey.SPACE, rate);
703+
}
704+
487705
/** Insert an empty line into the text list. */
488706
public MultiblockUIBuilder addEmptyLine() {
489707
addKey(IKey.LINE_FEED);

src/main/java/gregtech/api/metatileentity/multiblock/ui/UISyncer.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package gregtech.api.metatileentity.multiblock.ui;
22

3+
import gregtech.api.mui.GTByteBufAdapters;
4+
35
import com.cleanroommc.modularui.utils.serialization.ByteBufAdapters;
46
import com.cleanroommc.modularui.utils.serialization.IByteBufAdapter;
57
import com.cleanroommc.modularui.utils.serialization.IByteBufDeserializer;
68
import com.cleanroommc.modularui.utils.serialization.IByteBufSerializer;
7-
import com.cleanroommc.modularui.utils.serialization.IEquals;
89
import org.jetbrains.annotations.NotNull;
910

1011
import java.math.BigInteger;
@@ -16,11 +17,6 @@
1617

1718
public interface UISyncer {
1819

19-
IByteBufAdapter<BigInteger> BIG_INT = ByteBufAdapters.makeAdapter(
20-
buffer -> new BigInteger(buffer.readByteArray()),
21-
(buffer, value) -> buffer.writeByteArray(value.toByteArray()),
22-
IEquals.defaultTester());
23-
2420
/**
2521
* Calls the supplier server side only so there's no potential NPEs for the client
2622
*
@@ -98,7 +94,7 @@ default float syncFloat(float initial) {
9894
}
9995

10096
default BigInteger syncBigInt(BigInteger initial) {
101-
return syncObject(initial, BIG_INT);
97+
return syncObject(initial, GTByteBufAdapters.BIG_INT);
10298
}
10399

104100
<T> T syncObject(T initial, IByteBufSerializer<T> serializer, IByteBufDeserializer<T> deserializer);

0 commit comments

Comments
 (0)