缺失强制下单

This commit is contained in:
GaLi 2026-05-29 10:27:41 +08:00
parent 16a5fab01e
commit 9cee5f27e2
17 changed files with 678 additions and 10 deletions

View File

@ -0,0 +1,7 @@
package com.extendedae_plus.api.crafting;
public interface IForceCraftStartSync {
void eap$clientSetForceCraftStart(boolean forceStart);
boolean eap$consumeForceCraftStartFlag();
}

View File

@ -0,0 +1,7 @@
package com.extendedae_plus.api.crafting;
import appeng.api.stacks.KeyCounter;
public interface IForcedCraftingPlan {
KeyCounter eap$getManualMissingItems();
}

View File

@ -0,0 +1,14 @@
package com.extendedae_plus.api.crafting;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.KeyCounter;
import java.util.Map;
public interface IManualCraftingState {
void eap$setManualWaiting(KeyCounter manualWaiting);
long eap$getManualWaitingAmount(AEKey what);
Map<AEKey, Long> eap$getManualWaitingSnapshot();
}

View File

@ -0,0 +1,32 @@
package com.extendedae_plus.content;
import appeng.api.stacks.AEKey;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public final class ClientManualCraftingStatusStore {
private static volatile int containerId = -1;
private static volatile Map<AEKey, Long> manualWaiting = Collections.emptyMap();
private ClientManualCraftingStatusStore() {
}
public static void setStatus(int menuContainerId, Map<AEKey, Long> snapshot) {
containerId = menuContainerId;
manualWaiting = Collections.unmodifiableMap(new LinkedHashMap<>(snapshot));
}
public static long getManualWaitingAmount(int menuContainerId, AEKey key) {
if (key == null || menuContainerId != containerId) {
return 0;
}
return manualWaiting.getOrDefault(key, 0L);
}
public static void clear() {
containerId = -1;
manualWaiting = Collections.emptyMap();
}
}

View File

@ -0,0 +1,72 @@
package com.extendedae_plus.crafting;
import appeng.api.crafting.IPatternDetails;
import appeng.api.networking.crafting.ICraftingPlan;
import appeng.api.stacks.GenericStack;
import appeng.api.stacks.KeyCounter;
import com.extendedae_plus.api.crafting.IForcedCraftingPlan;
import java.util.Map;
public class ForcedCraftingPlan implements ICraftingPlan, IForcedCraftingPlan {
private final ICraftingPlan delegate;
private final KeyCounter manualMissingItems;
public ForcedCraftingPlan(ICraftingPlan delegate) {
this.delegate = delegate;
this.manualMissingItems = copy(delegate.missingItems());
}
@Override
public KeyCounter eap$getManualMissingItems() {
return copy(this.manualMissingItems);
}
@Override
public GenericStack finalOutput() {
return this.delegate.finalOutput();
}
@Override
public long bytes() {
return this.delegate.bytes();
}
@Override
public boolean simulation() {
return false;
}
@Override
public boolean multiplePaths() {
return this.delegate.multiplePaths();
}
@Override
public KeyCounter usedItems() {
return this.delegate.usedItems();
}
@Override
public KeyCounter emittedItems() {
return this.delegate.emittedItems();
}
@Override
public KeyCounter missingItems() {
return new KeyCounter();
}
@Override
public Map<IPatternDetails, Long> patternTimes() {
return this.delegate.patternTimes();
}
private static KeyCounter copy(KeyCounter source) {
var copy = new KeyCounter();
for (var entry : source) {
copy.add(entry.getKey(), entry.getLongValue());
}
return copy;
}
}

View File

@ -2,6 +2,8 @@ package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.network.*;
import com.extendedae_plus.network.crafting.ForceCraftStartFlagC2SPacket;
import com.extendedae_plus.network.crafting.ManualCraftingStatusS2CPacket;
import com.extendedae_plus.network.packet.EAPConfigButtonPacket;
import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
@ -57,6 +59,12 @@ public class ModNetwork {
registrar.playToClient(com.extendedae_plus.network.LabelNetworkListS2CPacket.TYPE,
com.extendedae_plus.network.LabelNetworkListS2CPacket.STREAM_CODEC,
com.extendedae_plus.network.LabelNetworkListS2CPacket::handle);
registrar.playToServer(ForceCraftStartFlagC2SPacket.TYPE,
ForceCraftStartFlagC2SPacket.STREAM_CODEC,
ForceCraftStartFlagC2SPacket::handle);
registrar.playToClient(ManualCraftingStatusS2CPacket.TYPE,
ManualCraftingStatusS2CPacket.STREAM_CODEC,
ManualCraftingStatusS2CPacket::handle);
registrar.playToServer(EAPConfigButtonPacket.TYPE, EAPConfigButtonPacket.STREAM_CODEC, EAPConfigButtonPacket::handleOnServer);
}

View File

@ -1,6 +1,8 @@
package com.extendedae_plus.mixin.ae2.accessor;
import appeng.api.crafting.IPatternDetails;
import appeng.api.stacks.GenericStack;
import appeng.crafting.CraftingLink;
import appeng.crafting.execution.ExecutingCraftingJob;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@ -11,4 +13,16 @@ import java.util.Map;
public interface ExecutingCraftingJobAccessor {
@Accessor("tasks")
Map<IPatternDetails, ExecutingCraftingJobTaskProgressAccessor> eap$getTasks();
@Accessor("finalOutput")
GenericStack eap$getFinalOutput();
@Accessor("remainingAmount")
long eap$getRemainingAmount();
@Accessor("remainingAmount")
void eap$setRemainingAmount(long remainingAmount);
@Accessor("link")
CraftingLink eap$getLink();
}

View File

@ -3,14 +3,17 @@ package com.extendedae_plus.mixin.ae2.client.gui;
import appeng.client.gui.WidgetContainer;
import appeng.client.gui.me.crafting.CraftConfirmScreen;
import appeng.core.localization.GuiText;
import appeng.menu.me.crafting.CraftingPlanSummary;
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
import com.extendedae_plus.mixin.ae2.accessor.WidgetContainerAccessor;
import com.extendedae_plus.network.crafting.ForceCraftStartFlagC2SPacket;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.neoforged.fml.ModList;
import net.neoforged.neoforge.network.PacketDistributor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@ -27,22 +30,48 @@ public class CraftConfirmScreenMixin {
private static final Component EAP_BOOKMARK_TEXT = Component.translatable("gui.extendedae_plus.add_bookmark");
@Unique
private static final Component EAP_BOOKMARK_TOOLTIP = Component.translatable("tooltip.extendedae_plus.add_missing_to_jei_bookmark");
@Unique
private static final Component EAP_START_TEXT = GuiText.Start.text();
@Unique
private static final Component EAP_FORCE_START_TEXT = Component.translatable("gui.extendedae_plus.force_start");
@Unique
private static final Component EAP_FORCE_START_TOOLTIP = Component.translatable("tooltip.extendedae_plus.force_start");
@Inject(method = "updateBeforeRender", at = @At("TAIL"), remap = false)
private void eap$updateCancelButtonText(CallbackInfo ci) {
if (!ModList.get().isLoaded("jei")){
return;
}
private void eap$updateButtons(CallbackInfo ci) {
CraftConfirmScreen self = (CraftConfirmScreen) (Object) this;
try {
WidgetContainer widgets = ((AEBaseScreenAccessor<?>) self).eap$getWidgets();
if (widgets == null) return;
AbstractWidget cancelWidget = ((WidgetContainerAccessor) widgets).eap$getWidgetsMap().get("cancel");
if (!(cancelWidget instanceof Button cancelButton)) return;
var widgetsMap = ((WidgetContainerAccessor) widgets).eap$getWidgetsMap();
boolean shiftDown = Screen.hasShiftDown();
CraftingPlanSummary plan = self.getMenu().getPlan();
boolean forceStart = shiftDown && plan != null && plan.isSimulation();
AbstractWidget startWidget = widgetsMap.get("start");
if (startWidget instanceof Button startButton) {
if (forceStart) {
startButton.active = !self.getMenu().hasNoCPU();
startButton.setMessage(EAP_FORCE_START_TEXT);
startButton.setTooltip(Tooltip.create(EAP_FORCE_START_TOOLTIP));
} else {
startButton.setMessage(EAP_START_TEXT);
startButton.setTooltip(null);
}
}
AbstractWidget selectCpuWidget = widgetsMap.get("selectCpu");
if (forceStart && selectCpuWidget instanceof Button selectCpuButton) {
selectCpuButton.active = true;
}
if (!ModList.get().isLoaded("jei")) {
return;
}
AbstractWidget cancelWidget = widgetsMap.get("cancel");
if (!(cancelWidget instanceof Button cancelButton)) return;
if (shiftDown) {
cancelButton.setMessage(EAP_BOOKMARK_TEXT);
@ -54,4 +83,12 @@ public class CraftConfirmScreenMixin {
} catch (Throwable ignored) {
}
}
@Inject(method = "start", at = @At("HEAD"), remap = false)
private void eap$syncForceStartFlagBeforeStart(CallbackInfo ci) {
CraftConfirmScreen self = (CraftConfirmScreen) (Object) this;
var plan = self.getMenu().getPlan();
boolean forceStart = Screen.hasShiftDown() && plan != null && plan.isSimulation();
PacketDistributor.sendToServer(new ForceCraftStartFlagC2SPacket(forceStart));
}
}

View File

@ -0,0 +1,68 @@
package com.extendedae_plus.mixin.ae2.client.gui;
import appeng.api.stacks.AmountFormat;
import appeng.api.util.AEColor;
import appeng.client.gui.me.crafting.CraftingCPUScreen;
import appeng.client.gui.me.crafting.CraftingStatusTableRenderer;
import appeng.core.AEConfig;
import appeng.menu.me.crafting.CraftingStatusEntry;
import com.extendedae_plus.content.ClientManualCraftingStatusStore;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
import java.util.List;
@Mixin(value = CraftingStatusTableRenderer.class, remap = false)
public abstract class CraftingStatusTableRendererMixin {
@Unique
private static final int EAP_BACKGROUND_ALPHA = 0x5A000000;
@Inject(method = "getEntryBackgroundColor", at = @At("RETURN"), cancellable = true)
private void eap$markManualWaitingEntries(CraftingStatusEntry entry, CallbackInfoReturnable<Integer> cir) {
if (!AEConfig.instance().isUseColoredCraftingStatus()) {
return;
}
if (this.eap$getManualWaitingAmount(entry) > 0) {
cir.setReturnValue(AEColor.PURPLE.blackVariant | EAP_BACKGROUND_ALPHA);
}
}
@Inject(method = "getEntryTooltip", at = @At("RETURN"), cancellable = true)
private void eap$appendManualWaitingTooltip(CraftingStatusEntry entry,
CallbackInfoReturnable<List<Component>> cir) {
long manualWaiting = this.eap$getManualWaitingAmount(entry);
if (manualWaiting <= 0 || entry.getWhat() == null) {
return;
}
List<Component> lines = new ArrayList<>(cir.getReturnValue());
lines.add(Component.translatable(
"tooltip.extendedae_plus.crafting.manual_waiting",
entry.getWhat().formatAmount(manualWaiting, AmountFormat.FULL)).withStyle(ChatFormatting.AQUA));
cir.setReturnValue(lines);
}
@Unique
private long eap$getManualWaitingAmount(CraftingStatusEntry entry) {
if (entry == null || entry.getWhat() == null) {
return 0;
}
var mc = Minecraft.getInstance();
if (mc == null || !(mc.screen instanceof CraftingCPUScreen<?> craftingScreen)) {
return 0;
}
return ClientManualCraftingStatusStore.getManualWaitingAmount(
craftingScreen.getMenu().containerId,
entry.getWhat());
}
}

View File

@ -0,0 +1,171 @@
package com.extendedae_plus.mixin.ae2.crafting;
import appeng.api.config.Actionable;
import appeng.api.networking.IGrid;
import appeng.api.networking.crafting.ICraftingPlan;
import appeng.api.networking.crafting.ICraftingRequester;
import appeng.api.networking.crafting.ICraftingSubmitResult;
import appeng.api.networking.security.IActionSource;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.KeyCounter;
import appeng.crafting.execution.CraftingCpuLogic;
import appeng.crafting.execution.ExecutingCraftingJob;
import appeng.crafting.inv.ListCraftingInventory;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import com.extendedae_plus.api.crafting.IForcedCraftingPlan;
import com.extendedae_plus.api.crafting.IManualCraftingState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
@Mixin(value = CraftingCpuLogic.class, remap = false)
public abstract class CraftingCpuLogicManualWaitingMixin implements IManualCraftingState {
@Shadow
private CraftingCPUCluster cluster;
@Shadow
private ExecutingCraftingJob job;
@Shadow
private ListCraftingInventory inventory;
@Shadow
protected abstract void postChange(AEKey what);
@Unique
private final Map<AEKey, Long> eap$manualWaitingFor = new LinkedHashMap<>();
@Override
public void eap$setManualWaiting(KeyCounter manualWaiting) {
this.eap$clearManualWaitingInternal(false);
for (var entry : manualWaiting) {
if (entry.getKey() != null && entry.getLongValue() > 0) {
this.eap$manualWaitingFor.put(entry.getKey(), entry.getLongValue());
this.postChange(entry.getKey());
}
}
if (!this.eap$manualWaitingFor.isEmpty()) {
this.cluster.markDirty();
}
}
@Override
public long eap$getManualWaitingAmount(AEKey what) {
if (what == null) {
return 0;
}
return this.eap$manualWaitingFor.getOrDefault(what, 0L);
}
@Override
public Map<AEKey, Long> eap$getManualWaitingSnapshot() {
return new LinkedHashMap<>(this.eap$manualWaitingFor);
}
@Inject(method = "trySubmitJob", at = @At("RETURN"))
private void eap$initManualWaitingForForcedPlan(IGrid grid, ICraftingPlan plan, IActionSource src,
@Nullable ICraftingRequester requester, CallbackInfoReturnable<ICraftingSubmitResult> cir) {
if (!cir.getReturnValue().successful()) {
return;
}
if (plan instanceof IForcedCraftingPlan forcedPlan) {
this.eap$setManualWaiting(forcedPlan.eap$getManualMissingItems());
} else {
this.eap$clearManualWaitingInternal(false);
}
}
@Inject(method = "insert", at = @At("RETURN"), cancellable = true)
private void eap$consumeManualWaitingAfterVanilla(AEKey what, long amount, Actionable type,
CallbackInfoReturnable<Long> cir) {
long vanillaInserted = cir.getReturnValue();
if (what == null || this.job == null) {
return;
}
long remainingAmount = amount - vanillaInserted;
if (remainingAmount <= 0) {
return;
}
long manualWaiting = this.eap$getManualWaitingAmount(what);
if (manualWaiting <= 0) {
return;
}
long consumed = Math.min(remainingAmount, manualWaiting);
if (type == Actionable.MODULATE) {
this.eap$decreaseManualWaiting(what, consumed);
this.cluster.markDirty();
this.postChange(what);
this.inventory.insert(what, consumed, Actionable.MODULATE);
}
cir.setReturnValue(vanillaInserted + consumed);
}
@Inject(method = "getWaitingFor", at = @At("RETURN"), cancellable = true)
private void eap$appendManualWaitingToStatus(AEKey template, CallbackInfoReturnable<Long> cir) {
if (template == null) {
return;
}
long manualWaiting = this.eap$getManualWaitingAmount(template);
if (manualWaiting > 0) {
cir.setReturnValue(cir.getReturnValue() + manualWaiting);
}
}
@Inject(method = "getAllWaitingFor", at = @At("TAIL"))
private void eap$appendManualWaitingKeys(Set<AEKey> waitingFor, CallbackInfo ci) {
waitingFor.addAll(this.eap$manualWaitingFor.keySet());
}
@Inject(method = "getAllItems", at = @At("TAIL"))
private void eap$appendManualWaitingItems(KeyCounter out, CallbackInfo ci) {
for (var entry : this.eap$manualWaitingFor.entrySet()) {
out.add(entry.getKey(), entry.getValue());
}
}
@Inject(method = "finishJob", at = @At("HEAD"))
private void eap$clearManualWaitingOnFinish(boolean success, CallbackInfo ci) {
this.eap$clearManualWaitingInternal(true);
}
@Unique
private void eap$decreaseManualWaiting(AEKey what, long amount) {
long current = this.eap$manualWaitingFor.getOrDefault(what, 0L);
if (current <= amount) {
this.eap$manualWaitingFor.remove(what);
} else {
this.eap$manualWaitingFor.put(what, current - amount);
}
}
@Unique
private void eap$clearManualWaitingInternal(boolean notifyChanges) {
if (this.eap$manualWaitingFor.isEmpty()) {
return;
}
var previousKeys = new ArrayList<>(this.eap$manualWaitingFor.keySet());
this.eap$manualWaitingFor.clear();
if (notifyChanges) {
for (var key : previousKeys) {
this.postChange(key);
}
}
this.cluster.markDirty();
}
}

View File

@ -0,0 +1,62 @@
package com.extendedae_plus.mixin.ae2.menu;
import appeng.api.networking.crafting.ICraftingPlan;
import appeng.menu.me.crafting.CraftConfirmMenu;
import com.extendedae_plus.api.crafting.IForceCraftStartSync;
import com.extendedae_plus.crafting.ForcedCraftingPlan;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = CraftConfirmMenu.class, remap = false)
public abstract class CraftConfirmMenuForceStartMixin implements IForceCraftStartSync {
@Shadow
private ICraftingPlan result;
@Unique
private boolean eap$pendingForceCraftStart;
@Unique
private ICraftingPlan eap$originalSimulationResult;
@Override
public void eap$clientSetForceCraftStart(boolean forceStart) {
this.eap$pendingForceCraftStart = forceStart;
}
@Override
public boolean eap$consumeForceCraftStartFlag() {
boolean flag = this.eap$pendingForceCraftStart;
this.eap$pendingForceCraftStart = false;
return flag;
}
@Inject(method = "startJob", at = @At("HEAD"))
private void eap$wrapSimulationPlanForForceStart(CallbackInfo ci) {
CraftConfirmMenu self = (CraftConfirmMenu) (Object) this;
if (self.isClientSide()) {
return;
}
if (!this.eap$consumeForceCraftStartFlag()) {
return;
}
if (this.result == null || !this.result.simulation()) {
return;
}
this.eap$originalSimulationResult = this.result;
this.result = new ForcedCraftingPlan(this.result);
}
@Inject(method = "startJob", at = @At("RETURN"))
private void eap$restoreOriginalPlanAfterSubmit(CallbackInfo ci) {
if (this.eap$originalSimulationResult == null) {
return;
}
this.result = this.eap$originalSimulationResult;
this.eap$originalSimulationResult = null;
}
}

View File

@ -0,0 +1,47 @@
package com.extendedae_plus.mixin.ae2.menu;
import appeng.api.stacks.AEKey;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import appeng.menu.me.crafting.CraftingCPUMenu;
import com.extendedae_plus.api.crafting.IManualCraftingState;
import com.extendedae_plus.network.crafting.ManualCraftingStatusS2CPacket;
import net.minecraft.server.level.ServerPlayer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@Mixin(value = CraftingCPUMenu.class, remap = false)
public abstract class CraftingCPUMenuManualStatusMixin {
@Shadow
private CraftingCPUCluster cpu;
@Unique
private Map<AEKey, Long> eap$lastManualWaitingSnapshot = Collections.emptyMap();
@Inject(method = "broadcastChanges", at = @At("TAIL"))
private void eap$syncManualWaitingStatus(CallbackInfo ci) {
CraftingCPUMenu self = (CraftingCPUMenu) (Object) this;
if (self.isClientSide() || !(self.getPlayer() instanceof ServerPlayer serverPlayer)) {
return;
}
Map<AEKey, Long> snapshot = Collections.emptyMap();
if (this.cpu != null && this.cpu.craftingLogic instanceof IManualCraftingState manualState) {
snapshot = manualState.eap$getManualWaitingSnapshot();
}
if (snapshot.equals(this.eap$lastManualWaitingSnapshot)) {
return;
}
this.eap$lastManualWaitingSnapshot = new LinkedHashMap<>(snapshot);
serverPlayer.connection.send(new ManualCraftingStatusS2CPacket(self.containerId, snapshot));
}
}

View File

@ -0,0 +1,48 @@
package com.extendedae_plus.network.crafting;
import appeng.menu.me.crafting.CraftConfirmMenu;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.api.crafting.IForceCraftStartSync;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.handling.IPayloadContext;
public class ForceCraftStartFlagC2SPacket implements CustomPacketPayload {
public static final Type<ForceCraftStartFlagC2SPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "force_craft_start_flag"));
public static final StreamCodec<FriendlyByteBuf, ForceCraftStartFlagC2SPacket> STREAM_CODEC = StreamCodec.of(
(buf, pkt) -> buf.writeBoolean(pkt.forceStart),
buf -> new ForceCraftStartFlagC2SPacket(buf.readBoolean())
);
private final boolean forceStart;
public ForceCraftStartFlagC2SPacket(boolean forceStart) {
this.forceStart = forceStart;
}
public boolean forceStart() {
return this.forceStart;
}
public static void handle(final ForceCraftStartFlagC2SPacket msg, final IPayloadContext ctx) {
ctx.enqueueWork(() -> {
if (!(ctx.player() instanceof ServerPlayer player)) {
return;
}
if (player.containerMenu instanceof CraftConfirmMenu menu
&& menu instanceof IForceCraftStartSync sync) {
sync.eap$clientSetForceCraftStart(msg.forceStart());
}
});
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@ -0,0 +1,71 @@
package com.extendedae_plus.network.crafting;
import appeng.api.stacks.AEKey;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.ClientManualCraftingStatusStore;
import net.minecraft.client.Minecraft;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import java.util.LinkedHashMap;
import java.util.Map;
public class ManualCraftingStatusS2CPacket implements CustomPacketPayload {
public static final Type<ManualCraftingStatusS2CPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "manual_crafting_status"));
public static final StreamCodec<RegistryFriendlyByteBuf, ManualCraftingStatusS2CPacket> STREAM_CODEC = StreamCodec.of(
(buf, pkt) -> {
buf.writeInt(pkt.containerId);
buf.writeVarInt(pkt.manualWaiting.size());
for (var entry : pkt.manualWaiting.entrySet()) {
AEKey.writeKey(buf, entry.getKey());
buf.writeVarLong(entry.getValue());
}
},
buf -> {
int containerId = buf.readInt();
int size = buf.readVarInt();
Map<AEKey, Long> snapshot = new LinkedHashMap<>(size);
for (int i = 0; i < size; i++) {
snapshot.put(AEKey.readKey(buf), buf.readVarLong());
}
return new ManualCraftingStatusS2CPacket(containerId, snapshot);
}
);
private final int containerId;
private final Map<AEKey, Long> manualWaiting;
public ManualCraftingStatusS2CPacket(int containerId, Map<AEKey, Long> manualWaiting) {
this.containerId = containerId;
this.manualWaiting = manualWaiting;
}
public static void handle(final ManualCraftingStatusS2CPacket msg, final IPayloadContext ctx) {
ctx.enqueueWork(() -> handleClient(msg));
}
@OnlyIn(Dist.CLIENT)
private static void handleClient(ManualCraftingStatusS2CPacket msg) {
var mc = Minecraft.getInstance();
if (mc == null || mc.player == null || mc.player.containerMenu == null) {
ClientManualCraftingStatusStore.clear();
return;
}
if (mc.player.containerMenu.containerId != msg.containerId) {
return;
}
ClientManualCraftingStatusStore.setStatus(msg.containerId, msg.manualWaiting);
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@ -303,5 +303,8 @@
"extendedae_plus.screen.global_controller_title": "Pattern Provider Status Controller",
"gui.extendedae_plus.add_bookmark": "Add Bookmark",
"tooltip.extendedae_plus.add_missing_to_jei_bookmark": "Add missing items to JEI bookmarks"
"tooltip.extendedae_plus.add_missing_to_jei_bookmark": "Add missing items to JEI bookmarks",
"gui.extendedae_plus.force_start": "Force Start",
"tooltip.extendedae_plus.force_start": "Submit this job immediately and keep missing entries as manual waiting",
"tooltip.extendedae_plus.crafting.manual_waiting": "Manual waiting: %s"
}

View File

@ -299,5 +299,8 @@
"extendedae_plus.screen.global_controller_title": "样板供应器状态控制器",
"gui.extendedae_plus.add_bookmark": "添加书签",
"tooltip.extendedae_plus.add_missing_to_jei_bookmark": "添加缺失物品到JEI书签"
"tooltip.extendedae_plus.add_missing_to_jei_bookmark": "添加缺失物品到JEI书签",
"gui.extendedae_plus.force_start": "强制开始",
"tooltip.extendedae_plus.force_start": "忽略当前缺失项并直接提交任务",
"tooltip.extendedae_plus.crafting.manual_waiting": "手动等待: %s"
}

View File

@ -46,6 +46,8 @@
"ae2.helpers.PatternProviderLogicUpgradesMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
"ae2.menu.AEBaseMenuUpgradesDedupMixin",
"ae2.menu.CraftConfirmMenuForceStartMixin",
"ae2.menu.CraftingCPUMenuManualStatusMixin",
"ae2.menu.ContainerPatternEncodingTermMenuMixin",
"ae2.menu.MEStorageMenuMixin",
"ae2.menu.PatternEncodingTermMenuMixin",
@ -53,6 +55,7 @@
"ae2.menu.PatternProviderMenuDoublingMixin",
"ae2.menu.PatternProviderMenuUpgradesMixin",
"ae2.network.PatternAccessTerminalPacketMixin",
"ae2.crafting.CraftingCpuLogicManualWaitingMixin",
"ae2.parts.automation.IOBusPartChannelCardMixin",
"ae2.parts.storagebus.StorageBusPartChannelCardMixin",
"ae2WTlib.ContainerUWirelessExPatternTerminalMixin",
@ -81,6 +84,7 @@
"ae2.accessor.WidgetContainerAccessor",
"ae2.client.gui.AEBaseScreenMixin",
"ae2.client.gui.CraftConfirmScreenMixin",
"ae2.client.gui.CraftingStatusTableRendererMixin",
"ae2.client.gui.InterfaceScreenMixin",
"ae2.client.gui.PatternEncodingTermScreenMixin",
"ae2.client.gui.PatternEncodingTermUploadMixin",