修改全网批量切换样板供应器逻辑为使用ConfigManager管理

This commit is contained in:
C-H716 2025-11-25 19:28:33 +08:00
parent 46781d485f
commit 713e16c453
5 changed files with 219 additions and 213 deletions

View File

@ -19,7 +19,7 @@ license = "LGPL-3.0-or-later"
# A list of mods - how many allowed here is determined by the individual mod loader # A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] [[mods]]
modId = "extendedae_plus" modId = "extendedae_plus"
version = "1.21.1-1.4.3" version = "1.21.1-1.4.4"
displayName = "ExtendedAE-Plus" displayName = "ExtendedAE-Plus"
issueTrackerURL = "https://github.com/GaLicn/ExtendedAE_Plus/issues" issueTrackerURL = "https://github.com/GaLicn/ExtendedAE_Plus/issues"
displayURL = "https://github.com/GaLicn/ExtendedAE_Plus" displayURL = "https://github.com/GaLicn/ExtendedAE_Plus"
@ -40,7 +40,7 @@ authors = "GaLi" #optional
# The description text for the mod (multi line!) (#mandatory) # The description text for the mod (multi line!) (#mandatory)
description = '''Add more practical features and auxiliary operations to the Applied Energistics 2 mod. description = '''Add more practical features and auxiliary operations to the Applied Energistics 2 mod.
Special thanks to contributor C-H716.''' Special thanks to contributor C-H716.'''
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. # The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
[[mixins]] [[mixins]]

View File

@ -28,27 +28,27 @@ public class GlobalProviderModesScreen extends AbstractContainerScreen<NetworkPa
int x = this.leftPos + (this.imageWidth - totalW3) / 2; int x = this.leftPos + (this.imageWidth - totalW3) / 2;
// 行1三个单项切换 // 行1三个单项切换
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_blocking"), b -> this.addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_blocking"), b ->
PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket( PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket(
GlobalToggleProviderModesC2SPacket.Op.TOGGLE, GlobalToggleProviderModesC2SPacket.Operation.TOGGLE,
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
this.menu.getBlockEntityPos() this.menu.getBlockEntityPos()
))).bounds(x, y, w, h).build()); ))).bounds(x, y, w, h).build());
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_adv_blocking"), b -> this.addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_adv_blocking"), b ->
PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket( PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket(
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
GlobalToggleProviderModesC2SPacket.Op.TOGGLE, GlobalToggleProviderModesC2SPacket.Operation.TOGGLE,
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
this.menu.getBlockEntityPos() this.menu.getBlockEntityPos()
))).bounds(x + w + s, y, w, h).build()); ))).bounds(x + w + s, y, w, h).build());
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_smart_doubling"), b -> this.addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_smart_doubling"), b ->
PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket( PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket(
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
GlobalToggleProviderModesC2SPacket.Op.NOOP, GlobalToggleProviderModesC2SPacket.Operation.NOOP,
GlobalToggleProviderModesC2SPacket.Op.TOGGLE, GlobalToggleProviderModesC2SPacket.Operation.TOGGLE,
this.menu.getBlockEntityPos() this.menu.getBlockEntityPos()
))).bounds(x + (w + s) * 2, y, w, h).build()); ))).bounds(x + (w + s) * 2, y, w, h).build());
@ -57,23 +57,36 @@ public class GlobalProviderModesScreen extends AbstractContainerScreen<NetworkPa
// 第二行两列按钮总宽并居中 // 第二行两列按钮总宽并居中
int totalW2 = w * 2 + s; int totalW2 = w * 2 + s;
int x2 = this.leftPos + (this.imageWidth - totalW2) / 2; int x2 = this.leftPos + (this.imageWidth - totalW2) / 2;
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_on"), b -> this.addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_on"), b ->
PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket( PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket(
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE, GlobalToggleProviderModesC2SPacket.Operation.SET_TRUE,
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE, GlobalToggleProviderModesC2SPacket.Operation.SET_TRUE,
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE, GlobalToggleProviderModesC2SPacket.Operation.SET_TRUE,
this.menu.getBlockEntityPos() this.menu.getBlockEntityPos()
))).bounds(x2, y2, w, h).build()); ))).bounds(x2, y2, w, h).build());
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_off"), b -> this.addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_off"), b ->
PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket( PacketDistributor.sendToServer(new GlobalToggleProviderModesC2SPacket(
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE, GlobalToggleProviderModesC2SPacket.Operation.SET_FALSE,
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE, GlobalToggleProviderModesC2SPacket.Operation.SET_FALSE,
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE, GlobalToggleProviderModesC2SPacket.Operation.SET_FALSE,
this.menu.getBlockEntityPos() this.menu.getBlockEntityPos()
))).bounds(x2 + w + s, y2, w, h).build()); ))).bounds(x2 + w + s, y2, w, h).build());
} }
@Override
public void render(net.minecraft.client.gui.GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(gfx, mouseX, mouseY, partialTicks);
super.render(gfx, mouseX, mouseY, partialTicks);
gfx.drawString(this.font, CUSTOM_TITLE, this.leftPos + 10, this.topPos + 8, 0xFFFFFF, false);
}
@Override
protected void renderLabels(net.minecraft.client.gui.GuiGraphics gfx, int mouseX, int mouseY) {
// 不绘制默认的玩家物品栏标题例如物品栏避免与自定义面板重叠
// 标题已在 render() 中手动绘制
}
@Override @Override
protected void renderBg(net.minecraft.client.gui.GuiGraphics gfx, float partialTicks, int mouseX, int mouseY) { protected void renderBg(net.minecraft.client.gui.GuiGraphics gfx, float partialTicks, int mouseX, int mouseY) {
// 半透明全屏遮罩避免底层 HUD准星/物品栏文字透出 // 半透明全屏遮罩避免底层 HUD准星/物品栏文字透出
@ -92,17 +105,4 @@ public class GlobalProviderModesScreen extends AbstractContainerScreen<NetworkPa
gfx.fill(panelLeft, panelTop, panelLeft + 1, panelBottom, 0x80FFFFFF); gfx.fill(panelLeft, panelTop, panelLeft + 1, panelBottom, 0x80FFFFFF);
gfx.fill(panelRight - 1, panelTop, panelRight, panelBottom, 0x80000000); gfx.fill(panelRight - 1, panelTop, panelRight, panelBottom, 0x80000000);
} }
@Override
public void render(net.minecraft.client.gui.GuiGraphics gfx, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(gfx, mouseX, mouseY, partialTicks);
super.render(gfx, mouseX, mouseY, partialTicks);
gfx.drawString(this.font, CUSTOM_TITLE, this.leftPos + 10, this.topPos + 8, 0xFFFFFF, false);
}
@Override
protected void renderLabels(net.minecraft.client.gui.GuiGraphics gfx, int mouseX, int mouseY) {
// 不绘制默认的玩家物品栏标题例如物品栏避免与自定义面板重叠
// 标题已在 render() 中手动绘制
}
} }

View File

@ -12,7 +12,6 @@ public class ModNetwork {
registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle); registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle);
registrar.playToServer(InterfaceAdjustConfigAmountC2SPacket.TYPE, InterfaceAdjustConfigAmountC2SPacket.STREAM_CODEC, InterfaceAdjustConfigAmountC2SPacket::handle); registrar.playToServer(InterfaceAdjustConfigAmountC2SPacket.TYPE, InterfaceAdjustConfigAmountC2SPacket.STREAM_CODEC, InterfaceAdjustConfigAmountC2SPacket::handle);
registrar.playToClient(SetPatternHighlightS2CPacket.TYPE, SetPatternHighlightS2CPacket.STREAM_CODEC, SetPatternHighlightS2CPacket::handle); registrar.playToClient(SetPatternHighlightS2CPacket.TYPE, SetPatternHighlightS2CPacket.STREAM_CODEC, SetPatternHighlightS2CPacket::handle);
registrar.playToClient(AdvancedBlockingSyncS2CPacket.TYPE, AdvancedBlockingSyncS2CPacket.STREAM_CODEC, AdvancedBlockingSyncS2CPacket::handle);
registrar.playToClient(ProvidersListS2CPacket.TYPE, ProvidersListS2CPacket.STREAM_CODEC, ProvidersListS2CPacket::handle); registrar.playToClient(ProvidersListS2CPacket.TYPE, ProvidersListS2CPacket.STREAM_CODEC, ProvidersListS2CPacket::handle);
registrar.playToServer(RequestProvidersListC2SPacket.TYPE, RequestProvidersListC2SPacket.STREAM_CODEC, RequestProvidersListC2SPacket::handle); registrar.playToServer(RequestProvidersListC2SPacket.TYPE, RequestProvidersListC2SPacket.STREAM_CODEC, RequestProvidersListC2SPacket::handle);
registrar.playToClient(SetProviderPageS2CPacket.TYPE, SetProviderPageS2CPacket.STREAM_CODEC, SetProviderPageS2CPacket::handle); registrar.playToClient(SetProviderPageS2CPacket.TYPE, SetProviderPageS2CPacket.STREAM_CODEC, SetProviderPageS2CPacket::handle);

View File

@ -1,48 +0,0 @@
package com.extendedae_plus.network;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.client.ClientAdvancedBlockingState;
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.neoforge.network.handling.IPayloadContext;
/**
* S2C同步某个 Provider 的高级阻挡状态到客户端本地存储
*/
public class AdvancedBlockingSyncS2CPacket implements CustomPacketPayload {
public static final Type<AdvancedBlockingSyncS2CPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "adv_blocking_sync"));
public static final StreamCodec<RegistryFriendlyByteBuf, AdvancedBlockingSyncS2CPacket> STREAM_CODEC = StreamCodec.of(
(buf, pkt) -> {
buf.writeUtf(pkt.dimensionId);
buf.writeLong(pkt.blockPosLong);
buf.writeBoolean(pkt.enabled);
},
buf -> new AdvancedBlockingSyncS2CPacket(buf.readUtf(), buf.readLong(), buf.readBoolean())
);
private final String dimensionId;
private final long blockPosLong;
private final boolean enabled;
public AdvancedBlockingSyncS2CPacket(String dimensionId, long blockPosLong, boolean enabled) {
this.dimensionId = dimensionId;
this.blockPosLong = blockPosLong;
this.enabled = enabled;
}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
public static void handle(final AdvancedBlockingSyncS2CPacket msg, final IPayloadContext ctx) {
ctx.enqueueWork(() -> {
String key = ClientAdvancedBlockingState.key(msg.dimensionId, msg.blockPosLong);
ClientAdvancedBlockingState.set(key, msg.enabled);
});
}
}

View File

@ -9,8 +9,7 @@ import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.helpers.patternprovider.PatternProviderLogicHost;
import appeng.parts.crafting.PatternProviderPart; import appeng.parts.crafting.PatternProviderPart;
import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.api.advancedBlocking.IAdvancedBlocking; import com.extendedae_plus.api.config.EAPSettings;
import com.extendedae_plus.api.smartDoubling.ISmartDoubling;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -19,188 +18,244 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.handling.IPayloadContext; import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
* C2S全网批量切换样板供应器的三种模式 * C2S 全局批量切换样板供应器的三种模式
* - 阻挡模式AE2 内置 BLOCKING_MODE 设置 * - 普通阻挡模式AE2 原生 BLOCKING_MODE
* - 高级阻挡模式IAdvancedBlocking mixin * - 高级阻挡模式
* - 智能翻倍模式ISmartDoubling mixin * - 智能翻倍模式
* * <p>
* 负载为三个操作码各1字节分别对应blockingadvancedBlockingsmartDoubling * 包体包含三个操作码每个 1 byte以及控制器方块位置用于定位所属 ME 网络
*/ */
public class GlobalToggleProviderModesC2SPacket implements CustomPacketPayload { public class GlobalToggleProviderModesC2SPacket implements CustomPacketPayload {
public static final Type<GlobalToggleProviderModesC2SPacket> TYPE = new Type<>( public static final Type<GlobalToggleProviderModesC2SPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "global_toggle_provider_modes")); ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "global_toggle_provider_modes"));
public static final StreamCodec<FriendlyByteBuf, GlobalToggleProviderModesC2SPacket> STREAM_CODEC = StreamCodec.of( public static final StreamCodec<FriendlyByteBuf, GlobalToggleProviderModesC2SPacket> STREAM_CODEC = StreamCodec.of(
(buf, pkt) -> { (buf, packet) -> {
buf.writeByte(pkt.opBlocking.id); buf.writeByte(packet.blockingModeOperation.id);
buf.writeByte(pkt.opAdvancedBlocking.id); buf.writeByte(packet.advancedBlockingOperation.id);
buf.writeByte(pkt.opSmartDoubling.id); buf.writeByte(packet.smartDoublingOperation.id);
buf.writeBlockPos(pkt.controllerPos); buf.writeBlockPos(packet.controllerBlockPos);
}, },
buf -> new GlobalToggleProviderModesC2SPacket(Op.byId(buf.readByte()), Op.byId(buf.readByte()), Op.byId(buf.readByte()), buf.readBlockPos()) buf -> new GlobalToggleProviderModesC2SPacket(
Operation.byId(buf.readByte()),
Operation.byId(buf.readByte()),
Operation.byId(buf.readByte()),
buf.readBlockPos())
); );
private final Op opBlocking;
private final Op opAdvancedBlocking; private final Operation blockingModeOperation;
private final Op opSmartDoubling; private final Operation advancedBlockingOperation;
private final BlockPos controllerPos; private final Operation smartDoublingOperation;
public GlobalToggleProviderModesC2SPacket(Op opBlocking, Op opAdvancedBlocking, Op opSmartDoubling, BlockPos controllerPos) {
this.opBlocking = opBlocking; /**
this.opAdvancedBlocking = opAdvancedBlocking; * 发起请求的玩家的控制器位置用于获取对应的 IGrid
this.opSmartDoubling = opSmartDoubling; */
this.controllerPos = controllerPos; private final BlockPos controllerBlockPos;
public GlobalToggleProviderModesC2SPacket(Operation blockingModeOperation,
Operation advancedBlockingOperation,
Operation smartDoublingOperation,
BlockPos controllerBlockPos) {
this.blockingModeOperation = blockingModeOperation;
this.advancedBlockingOperation = advancedBlockingOperation;
this.smartDoublingOperation = smartDoublingOperation;
this.controllerBlockPos = controllerBlockPos;
} }
public static void handle(final GlobalToggleProviderModesC2SPacket msg, final IPayloadContext ctx) { public static void handle(final GlobalToggleProviderModesC2SPacket message, final IPayloadContext context) {
ctx.enqueueWork(() -> { context.enqueueWork(() -> {
if (!(ctx.player() instanceof ServerPlayer player)) return; if (!(context.player() instanceof ServerPlayer player)) return;
if (player == null) return;
// 从控制方块实体的 AE2 节点确定 AE 网络上下文
var level = player.serverLevel(); var level = player.serverLevel();
var be = level.getBlockEntity(msg.controllerPos); var blockEntity = level.getBlockEntity(message.controllerBlockPos);
if (!(be instanceof IInWorldGridNodeHost host)) return; if (!(blockEntity instanceof IInWorldGridNodeHost gridNodeHost)) return;
var node = host.getGridNode(null);
if (node == null) return; var gridNode = gridNodeHost.getGridNode(null);
IGrid grid = node.getGrid(); if (gridNode == null) return;
IGrid grid = gridNode.getGrid();
if (grid == null) return; if (grid == null) return;
int affected = applyToAllProviders(grid, msg); int affectedCount = applyToAllPatternProviders(grid, message);
// 向发起玩家反馈影响数量便于判断按钮是否生效
player.displayClientMessage(Component.literal("E+ 全局切换已应用到 " + affected + " 个样板供应器"), true); // 给发起者一个短暂的行动条提示方便知道本次操作实际影响了多少个供应器
player.displayClientMessage(
Component.literal("E+ 全局切换已应用到 " + affectedCount + " 个样板供应器"),
true);
}); });
} }
private static int applyToAllProviders(IGrid grid, GlobalToggleProviderModesC2SPacket msg) { /**
int affected = 0; * 遍历当前 ME 网络中所有样板供应器方块零件第三方实现并应用切换操作
// 去重集合避免同一逻辑重复计数 */
Set<PatternProviderLogic> all = new HashSet<>(); private static int applyToAllPatternProviders(IGrid grid, GlobalToggleProviderModesC2SPacket message) {
int affectedCount = 0;
// Set 去重因为同一个 Logic 实例可能被多种方式收集到
Set<PatternProviderLogic> uniqueLogics = new HashSet<>();
// 方块形式的样板供应器全部/在线 // 1. AE2 原生的方块实体形式Pattern Provider BlockEntity
try { collectLogicsFromMachineSet(grid.getMachines(PatternProviderBlockEntity.class), uniqueLogics);
Set<PatternProviderBlockEntity> blocksAll = grid.getMachines(PatternProviderBlockEntity.class); collectLogicsFromMachineSet(grid.getActiveMachines(PatternProviderBlockEntity.class), uniqueLogics);
Set<PatternProviderBlockEntity> blocksActive = grid.getActiveMachines(PatternProviderBlockEntity.class);
for (PatternProviderBlockEntity be : blocksAll) if (be != null && be.getLogic() != null) all.add(be.getLogic());
for (PatternProviderBlockEntity be : blocksActive) if (be != null && be.getLogic() != null) all.add(be.getLogic());
} catch (Throwable ignored) {}
// Part 形式的样板供应器全部/在线 // 2. AE2 原生的电缆零件形式Pattern Provider Part
try { collectLogicsFromMachineSet(grid.getMachines(PatternProviderPart.class), uniqueLogics);
Set<PatternProviderPart> partsAll = grid.getMachines(PatternProviderPart.class); collectLogicsFromMachineSet(grid.getActiveMachines(PatternProviderPart.class), uniqueLogics);
Set<PatternProviderPart> partsActive = grid.getActiveMachines(PatternProviderPart.class);
for (PatternProviderPart part : partsAll) if (part != null && part.getLogic() != null) all.add(part.getLogic());
for (PatternProviderPart part : partsActive) if (part != null && part.getLogic() != null) all.add(part.getLogic());
} catch (Throwable ignored) {}
// 兼容任意实现了 PatternProviderLogicHost 的机器例如 ExtendedAE PartExPatternProvider // 3. 任何实现了 PatternProviderLogicHost 接口的机器包括 ExtendedAE 自己的扩展
try { collectLogicsFromMachineSet(grid.getMachines(PatternProviderLogicHost.class), uniqueLogics);
Set<PatternProviderLogicHost> hostsAll = grid.getMachines(PatternProviderLogicHost.class); collectLogicsFromMachineSet(grid.getActiveMachines(PatternProviderLogicHost.class), uniqueLogics);
Set<PatternProviderLogicHost> hostsActive = grid.getActiveMachines(PatternProviderLogicHost.class);
for (PatternProviderLogicHost host : hostsAll) if (host != null && host.getLogic() != null) all.add(host.getLogic());
for (PatternProviderLogicHost host : hostsActive) if (host != null && host.getLogic() != null) all.add(host.getLogic());
} catch (Throwable ignored) {}
// 兼容显式匹配第三方具体类通过反射避免 AE2 仅按精确类型匹配导致 interface 不返回的问题 // 4. 兼容 ExtendedAEglodblock自己的 ExPatternProvider因为 AE2 getMachines 只按精确类匹配接口会漏
collectByClassName(grid, all, "com.glodblock.github.extendedae.common.parts.PartExPatternProvider"); collectByReflection(grid, uniqueLogics, "com.glodblock.github.extendedae.common.parts.PartExPatternProvider");
collectByClassName(grid, all, "com.glodblock.github.extendedae.common.tileentities.TileExPatternProvider"); collectByReflection(grid, uniqueLogics, "com.glodblock.github.extendedae.common.tileentities.TileExPatternProvider");
for (PatternProviderLogic logic : all) { // 真正执行切换
if (applyToLogic(logic, msg)) affected++; for (PatternProviderLogic logic : uniqueLogics) {
if (applyOperationToLogic(logic, message)) {
affectedCount++;
}
} }
return affected; return affectedCount;
} }
private static void collectByClassName(IGrid grid, Set<PatternProviderLogic> out, String className) { /**
try { * 工具方法把一个 Set<? extends SomeMachine> 中的 Logic 加入去重集合
Class<?> cls = Class.forName(className); */
// 收集全部与在线两类机器 private static void collectLogicsFromMachineSet(Set<?> machineSet, Set<PatternProviderLogic> target) {
Set<?> all = grid.getMachines((Class) cls); if (machineSet == null) return;
Set<?> active = grid.getActiveMachines((Class) cls); for (Object obj : machineSet) {
for (Object o : all) addLogicIfPresent(out, o); addLogicIfPresent(target, obj);
for (Object o : active) addLogicIfPresent(out, o); }
} catch (Throwable ignored) {}
} }
private static void addLogicIfPresent(Set<PatternProviderLogic> out, Object o) { /**
* 通过反射兼容第三方精确类防止接口匹配漏掉
*/
private static void collectByReflection(IGrid grid, Set<PatternProviderLogic> target, String className) {
try { try {
if (o instanceof PatternProviderLogicHost host) { Class<?> clazz = Class.forName(className);
var logic = host.getLogic(); collectLogicsFromMachineSet(grid.getMachines(clazz), target);
if (logic != null) out.add(logic); collectLogicsFromMachineSet(grid.getActiveMachines(clazz), target);
} catch (Throwable ignored) {
// 如果类不存在比如玩家没装 ExtendedAE直接忽略
}
}
/**
* 从任意对象里尝试取出 PatternProviderLogic兼容多种实现
*/
private static void addLogicIfPresent(Set<PatternProviderLogic> target, Object obj) {
if (obj == null) return;
try {
if (obj instanceof PatternProviderLogicHost host && host.getLogic() != null) {
target.add(host.getLogic());
return; return;
} }
// 兜底若对象有 getLogic 方法且返回 PatternProviderLogic // 兜底反射调用 getLogic()
var m = o.getClass().getMethod("getLogic"); var method = obj.getClass().getMethod("getLogic");
Object ret = m.invoke(o); Object result = method.invoke(obj);
if (ret instanceof PatternProviderLogic logic) out.add(logic); if (result instanceof PatternProviderLogic logic) {
} catch (Throwable ignored) {} target.add(logic);
}
} catch (Throwable ignored) {
}
} }
private static boolean applyToLogic(PatternProviderLogic logic, GlobalToggleProviderModesC2SPacket msg) { /**
* 对单个 PatternProviderLogic 应用本次包里携带的三种操作
*/
private static boolean applyOperationToLogic(PatternProviderLogic logic, GlobalToggleProviderModesC2SPacket message) {
if (logic == null) return false; if (logic == null) return false;
boolean changed = false; var configManager = logic.getConfigManager();
// 1) 阻挡模式AE2 内置设置 if (configManager == null) return false;
if (msg.opBlocking != Op.NOOP) {
boolean current = safeIsBlocking(logic); boolean anyChanged = false;
boolean target = computeTarget(current, msg.opBlocking);
var cm = logic.getConfigManager(); // 1. AE2 原生阻挡模式
if (cm != null) { if (message.blockingModeOperation != Operation.NOOP) {
cm.putSetting(Settings.BLOCKING_MODE, target ? YesNo.YES : YesNo.NO); boolean current = isBlockingModeEnabled(logic);
changed = changed || (current != target); boolean target = calculateTargetState(current, message.blockingModeOperation);
configManager.putSetting(Settings.BLOCKING_MODE, target ? YesNo.YES : YesNo.NO);
anyChanged |= (current != target);
}
// 2. 高级阻挡模式
if (message.advancedBlockingOperation != Operation.NOOP) {
boolean current = configManager.getSetting(EAPSettings.ADVANCED_BLOCKING) == YesNo.YES;
boolean target = calculateTargetState(current, message.advancedBlockingOperation);
configManager.putSetting(EAPSettings.ADVANCED_BLOCKING, target ? YesNo.YES : YesNo.NO);
anyChanged |= (current != target);
}
// 3. 智能翻倍模式
if (message.smartDoublingOperation != Operation.NOOP) {
boolean current = configManager.getSetting(EAPSettings.SMART_DOUBLING) == YesNo.YES;
boolean target = calculateTargetState(current, message.smartDoublingOperation);
configManager.putSetting(EAPSettings.SMART_DOUBLING, target ? YesNo.YES : YesNo.NO);
anyChanged |= (current != target);
}
// 有改动时保存并让 AE2 同步到客户端
if (anyChanged) {
try {
logic.saveChanges();
} catch (Throwable ignored) {
} }
} }
// 2) 高级阻挡mixin 接口 return anyChanged;
if (msg.opAdvancedBlocking != Op.NOOP && logic instanceof IAdvancedBlocking adv) {
boolean current = adv.eap$getAdvancedBlocking();
boolean target = computeTarget(current, msg.opAdvancedBlocking);
adv.eap$setAdvancedBlocking(target);
changed = changed || (current != target);
}
// 3) 智能翻倍mixin 接口
if (msg.opSmartDoubling != Op.NOOP && logic instanceof ISmartDoubling sd) {
boolean current = sd.eap$getSmartDoubling();
boolean target = computeTarget(current, msg.opSmartDoubling);
sd.eap$setSmartDoubling(target);
changed = changed || (current != target);
}
// 保存更改并让 AE2 同步
if (changed) {
try { logic.saveChanges(); } catch (Throwable ignored) {}
}
return changed;
} }
private static boolean computeTarget(boolean current, Op op) { /**
return switch (op) { * 根据当前状态和操作码计算目标状态
*/
private static boolean calculateTargetState(boolean currentValue, Operation operation) {
return switch (operation) {
case SET_TRUE -> true; case SET_TRUE -> true;
case SET_FALSE -> false; case SET_FALSE -> false;
case TOGGLE -> !current; case TOGGLE -> !currentValue;
default -> current; case NOOP -> currentValue;
}; };
} }
private static boolean safeIsBlocking(PatternProviderLogic logic) { /**
try { return logic.isBlocking(); } catch (Throwable t) { return false; } * 安全获取 AE2 原生阻挡模式状态防止旧版本抛异常
*/
private static boolean isBlockingModeEnabled(PatternProviderLogic logic) {
try {
return logic.isBlocking();
} catch (Throwable t) {
return false;
}
} }
@Override @Override
public Type<? extends CustomPacketPayload> type() { public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE; return TYPE;
} }
public enum Op { /**
NOOP((byte) 0), * 操作类型枚举对应客户端发来的 1 byte
SET_TRUE((byte) 1), */
SET_FALSE((byte) 2), public enum Operation {
TOGGLE((byte) 3); NOOP((byte) 0), // 不执行任何操作
SET_TRUE((byte) 1), // 强制开启
SET_FALSE((byte) 2), // 强制关闭
TOGGLE((byte) 3); // 切换当前状态
public final byte id; public final byte id;
Op(byte id) {this.id = id;} Operation(byte id) {
this.id = id;
}
static Op byId(byte id) { static Operation byId(byte id) {
return switch (id) { return switch (id) {
case 1 -> SET_TRUE; case 1 -> SET_TRUE;
case 2 -> SET_FALSE; case 2 -> SET_FALSE;