From 0571efe2f908920af4ce4a59269fc6fcdfe1f027 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:04:46 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=91=E9=81=93=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/items/ChannelCardItem.java | 66 +++++ .../bridge/InterfaceWirelessLinkBridge.java | 50 ++++ .../extendedae_plus/init/ModCreativeTabs.java | 1 + .../com/extendedae_plus/init/ModItems.java | 7 + .../InterfaceLogicChannelCardMixin.java | 225 ++++++++++++++++++ .../automation/IOBusPartChannelCardMixin.java | 145 +++++++++++ .../StorageBusPartChannelCardMixin.java | 141 +++++++++++ .../endpoint/GenericNodeEndpointImpl.java | 49 ++++ .../endpoint/InterfaceNodeEndpointImpl.java | 49 ++++ .../assets/extendedae_plus/lang/en_us.json | 4 + .../assets/extendedae_plus/lang/zh_cn.json | 4 + .../models/item/channel_card.json | 6 + .../extendedae_plus/recipes/channel_card.json | 8 + .../resources/extendedae_plus.mixins.json | 3 + 14 files changed, 758 insertions(+) create mode 100644 src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java create mode 100644 src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java create mode 100644 src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java create mode 100644 src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java create mode 100644 src/main/resources/assets/extendedae_plus/models/item/channel_card.json create mode 100644 src/main/resources/data/extendedae_plus/recipes/channel_card.json diff --git a/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java new file mode 100644 index 0000000..6a922c4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java @@ -0,0 +1,66 @@ +package com.extendedae_plus.ae.items; + +import appeng.items.materials.UpgradeCardItem; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.component.CustomData; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * 频道卡(MVP):仅存储一个 long 类型的频道号到 NBT:"channel"。 + * 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。 + */ +public class ChannelCardItem extends UpgradeCardItem { + public static final String TAG_CHANNEL = "channel"; + + public ChannelCardItem(Item.Properties properties) { + super(properties); + } + + public static void setChannel(ItemStack stack, long channel) { + CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + tag.putLong(TAG_CHANNEL, channel); + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } + + public static long getChannel(ItemStack stack) { + CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + return tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L; + } + + @Override + public void appendHoverText(ItemStack stack, Item.TooltipContext context, List lines, TooltipFlag flag) { + super.appendHoverText(stack, context, lines, flag); + long ch = getChannel(stack); + if (ch == 0L) { + lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset")); + } else { + lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch)); + } + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + if (!level.isClientSide) { + long ch = getChannel(stack); + boolean dec = player.isShiftKeyDown(); + long next = dec ? Math.max(0L, ch - 1L) : ch + 1L; + if (next != ch) { + setChannel(stack, next); + player.displayClientMessage(Component.translatable("item.extendedae_plus.channel_card.set", next), true); + } + } + return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); + } +} diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java new file mode 100644 index 0000000..d200f77 --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java @@ -0,0 +1,50 @@ +package com.extendedae_plus.bridge; + +/** + * 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。 + */ +public interface InterfaceWirelessLinkBridge { + void eap$updateWirelessLink(); + + /** + * 获取无线连接状态(服务端返回真实状态,客户端返回同步状态) + */ + default boolean eap$isWirelessConnected() { + return false; + } + + /** + * 设置客户端的无线连接状态(仅在客户端使用) + */ + default void eap$setClientWirelessState(boolean connected) { + // 默认实现为空 + } + + /** + * 检查是否已经进行过tick初始化 + */ + default boolean eap$hasTickInitialized() { + return true; // 默认认为已初始化 + } + + /** + * 设置tick初始化状态 + */ + default void eap$setTickInitialized(boolean initialized) { + // 默认实现为空 + } + + /** + * 执行频道链接初始化 + */ + default void eap$initializeChannelLink() { + // 默认实现为空 + } + + /** + * 检查并处理延迟初始化 + */ + default void eap$handleDelayedInit() { + // 默认实现为空 + } +} diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 2b5cb0b..77d059a 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -24,6 +24,7 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); output.accept(ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get()); + output.accept(ModItems.CHANNEL_CARD.get()); output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 output.accept(ModItems.createEntitySpeedCardStack((byte) 2)); diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 8678005..5189c54 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; +import com.extendedae_plus.ae.items.ChannelCardItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -62,6 +63,12 @@ public final class ModItems { () -> new EntitySpeedCardItem(new Item.Properties()) ); + // 频道卡:用于AE机器的无线频道连接 + public static final DeferredItem CHANNEL_CARD = ITEMS.register( + "channel_card", + () -> new ChannelCardItem(new Item.Properties()) + ); + /** * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) */ diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java new file mode 100644 index 0000000..15fd6f3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java @@ -0,0 +1,225 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.helpers.InterfaceLogic; +import appeng.helpers.InterfaceLogicHost; +import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +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(InterfaceLogic.class) +public abstract class InterfaceLogicChannelCardMixin implements InterfaceWirelessLinkBridge { + + @Shadow(remap = false) public abstract IUpgradeInventory getUpgrades(); + + @Shadow(remap = false) public abstract appeng.api.networking.IGridNode getActionableNode(); + + @Shadow(remap = false) protected InterfaceLogicHost host; + + @Shadow(remap = false) protected appeng.api.networking.IManagedGridNode mainNode; + + @Unique + private WirelessSlaveLink eap$link; + + @Unique + private long eap$lastChannel = -1; + + @Unique + private boolean eap$clientConnected = false; + + @Unique + private boolean eap$hasInitialized = false; + + @Unique + private int eap$delayedInitTicks = 0; + + static { + // InterfaceLogicChannelCardMixin 已加载 + } + + @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false) + private void eap$onUpgradesChangedTail(CallbackInfo ci) { + // 升级变更时重置标志并尝试初始化 + eap$lastChannel = -1; + eap$hasInitialized = false; + eap$initializeChannelLink(); + } + + @Inject(method = "gridChanged", at = @At("TAIL"), remap = false) + private void eap$afterGridChanged(CallbackInfo ci) { + // 网格状态变化时重置标志并设置延迟初始化 + eap$lastChannel = -1; + eap$hasInitialized = false; + eap$delayedInitTicks = 10; // 适当增加延迟tick,等待网格完成引导 + // 尝试唤醒设备,确保后续还能继续tick + if (mainNode != null) { + mainNode.ifPresent((grid, node) -> { + try { + grid.getTickManager().wakeDevice(node); + } catch (Throwable ignored) { + } + }); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL"), remap = false) + private void eap$afterReadNBT(net.minecraft.nbt.CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从 NBT加载时重置标志 + eap$lastChannel = -1; + eap$hasInitialized = false; + // 直接尝试初始化 + eap$initializeChannelLink(); + } + + @Inject(method = "clearContent", at = @At("HEAD"), remap = false) + private void eap$onClearContent(CallbackInfo ci) { + if (eap$link != null) { + eap$link.onUnloadOrRemove(); + } + } + + @Unique + public void eap$initializeChannelLink() { + // 仅在服务端执行,避免在渲染线程/客户端触发任何初始化路径 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + + // 避免重复初始化 + if (eap$hasInitialized) { + return; + } + + // 检查网格节点是否在线(1.21.1版本使用isActive替代hasGridBooted) + if (mainNode == null || !mainNode.isActive()) { + return; + } + + try { + long channel = 0L; + boolean found = false; + for (ItemStack stack : getUpgrades()) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + found = true; + break; + } + } + + if (!found) { + // 无频道卡:断开并视为初始化完成 + if (eap$link != null) { + eap$link.setFrequency(0L); + eap$link.updateStatus(); + } + eap$hasInitialized = true; + return; + } + + if (eap$link == null) { + var endpoint = new InterfaceNodeEndpointImpl(host, () -> this.mainNode.getNode()); + eap$link = new WirelessSlaveLink(endpoint); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + + if (eap$link.isConnected()) { + eap$hasInitialized = true; // 设置初始化完成标志 + } else { + // 不标记为完成,允许后续tick重试 + eap$hasInitialized = false; + // 设置一个短延迟窗口,避免每tick刷屏 + eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5); + try { + mainNode.ifPresent((grid, node) -> { + try { + grid.getTickManager().wakeDevice(node); + } catch (Throwable ignored) { + } + }); + } catch (Throwable ignored) { + } + } + } catch (Exception ignored) { + } + } + + @Override + public void eap$updateWirelessLink() { + if (eap$link != null) { + eap$link.updateStatus(); + } + } + + @Override + public boolean eap$isWirelessConnected() { + // InterfaceLogic没有isClientSide方法,需要通过host判断 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return eap$clientConnected; + } else { + return eap$link != null && eap$link.isConnected(); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$clientConnected = connected; + } + + @Override + public boolean eap$hasTickInitialized() { + return eap$hasInitialized; + } + + @Override + public void eap$setTickInitialized(boolean initialized) { + eap$hasInitialized = initialized; + } + + @Override + public void eap$handleDelayedInit() { + // 仅在服务端执行延迟初始化,避免在渲染线程/客户端触发任何初始化路径 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + + // 若尚未初始化,则持续尝试,直到网格完成引导 + if (!eap$hasInitialized) { + if (mainNode == null || !mainNode.isActive()) { + // 仍在引导,消耗计时器 + if (eap$delayedInitTicks > 0) { + eap$delayedInitTicks--; + } + if (eap$delayedInitTicks == 0) { + // 重新设定一个短延迟窗口,并唤醒设备,以保证后续还能继续 tick + eap$delayedInitTicks = 5; + try { + mainNode.ifPresent((grid, node) -> { + try { + grid.getTickManager().wakeDevice(node); + } catch (Throwable ignored) { + } + }); + } catch (Throwable ignored) { + } + } + } else { + // 网格已引导完成,执行初始化 + eap$initializeChannelLink(); + } + } + } + + // eap$initializeChannelLink方法已在上面实现 +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java new file mode 100644 index 0000000..8f15d70 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java @@ -0,0 +1,145 @@ +package com.extendedae_plus.mixin.ae2.parts.automation; + +import appeng.api.networking.security.IActionHost; +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.helpers.InterfaceLogicHost; +import appeng.parts.automation.IOBusPart; +import net.minecraft.nbt.CompoundTag; +import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; +import net.minecraft.network.FriendlyByteBuf; +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.CallbackInfo; + +/** + * 给 AE2 的 I/O 总线注入频道卡联动:在升级变更时读取频道并更新无线链接。 + */ +@Mixin(value = IOBusPart.class, remap = false) +public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject { + + @Unique + private WirelessSlaveLink eap$link; + + @Unique + private long eap$lastChannel = -1; + + @Unique + private boolean eap$clientConnected = false; + + @Unique + private boolean eap$hasTickInitialized = false; + + @Inject(method = "upgradesChanged", at = @At("TAIL")) + private void eap$onUpgradesChanged(CallbackInfo ci) { + // 只在服务端初始化频道链接 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "tickingRequest", at = @At("HEAD")) + private void eap$beforeTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall, org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) { + // 在第一次tick时初始化频道链接(此时网格节点已经在线) + if (!eap$hasTickInitialized && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$hasTickInitialized = true; + eap$initializeChannelLink(); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$afterReadFromNBT(CompoundTag extra, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从NBT加载时重置频道缓存和tick初始化标志 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$lastChannel = -1; + eap$hasTickInitialized = false; // 重置标志,允许再次初始化 + } + } + + @Unique + public void eap$initializeChannelLink() { + // 防止重复调用 + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return; + } + + try { + IUpgradeInventory inv = this.getUpgrades(); + long channel = 0L; + boolean found = false; + for (var stack : inv) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + found = true; + break; + } + } + + // 频道没有变化则跳过 + if (eap$lastChannel == channel) { + return; + } + eap$lastChannel = channel; + + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel); + + if (!found) { + // 无频道卡则断开 + if (eap$link != null) { + eap$link.setFrequency(0L); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接"); + // 立即通知客户端状态变化(断开连接无需延迟) + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } + return; + } + + if (eap$link == null) { + var endpoint = new GenericNodeEndpointImpl( + () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(), + () -> ((IActionHost)(Object)this).getActionableNode() + ); + eap$link = new WirelessSlaveLink(endpoint); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接"); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("[服务端] IOBus 初始化频道链接失败", e); + } + } + + @Override + public void eap$updateWirelessLink() { + if (eap$link != null) { + eap$link.updateStatus(); + } + } + + @Override + public boolean eap$isWirelessConnected() { + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return eap$clientConnected; + } else { + return eap$link != null && eap$link.isConnected(); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$clientConnected = connected; + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java new file mode 100644 index 0000000..54a0bdb --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java @@ -0,0 +1,141 @@ +package com.extendedae_plus.mixin.ae2.parts.storagebus; + +import appeng.api.networking.IGridNodeListener; +import appeng.api.networking.security.IActionHost; +import appeng.api.upgrades.IUpgradeInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.parts.storagebus.StorageBusPart; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; +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.CallbackInfo; + +/** + * 给 AE2 的存储总线注入频道卡联动:在升级变更时读取频道并更新无线链接。 + */ +@Mixin(value = StorageBusPart.class, remap = false) +public abstract class StorageBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject { + + @Unique + private WirelessSlaveLink eap$link; + + @Unique + private long eap$lastChannel = -1; + + @Unique + private boolean eap$clientConnected = false; + + @Inject(method = "upgradesChanged", at = @At("TAIL")) + private void eap$onUpgradesChanged(CallbackInfo ci) { + // 只在服务端初始化频道链接 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "onMainNodeStateChanged", at = @At("TAIL")) + private void eap$onMainNodeStateChanged(IGridNodeListener.State reason, CallbackInfo ci) { + // 在节点状态变化时(包括加载后的GRID_BOOT)重新初始化频道链接 + if (reason == IGridNodeListener.State.GRID_BOOT && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$afterReadFromNBT(CompoundTag extra, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从NBT加载后也重新初始化频道链接(只在服务端) + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + // 从NBT加载时重置频道缓存,强制重新初始化 + eap$lastChannel = -1; + eap$initializeChannelLink(); + } + } + + @Unique + public void eap$initializeChannelLink() { + // 防止重复调用 + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return; + } + + try { + IUpgradeInventory inv = this.getUpgrades(); + long channel = 0L; + boolean found = false; + for (var stack : inv) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + found = true; + break; + } + } + + // 频道没有变化则跳过 + if (eap$lastChannel == channel) { + return; + } + eap$lastChannel = channel; + + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel); + + if (!found) { + if (eap$link != null) { + eap$link.setFrequency(0L); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接"); + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } + return; + } + + if (eap$link == null) { + var endpoint = new GenericNodeEndpointImpl( + () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(), + () -> ((IActionHost)(Object)this).getActionableNode() + ); + eap$link = new WirelessSlaveLink(endpoint); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接"); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e); + } + } + + @Override + public void eap$updateWirelessLink() { + if (eap$link != null) { + eap$link.updateStatus(); + } + } + + @Override + public boolean eap$isWirelessConnected() { + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return eap$clientConnected; + } else { + return eap$link != null && eap$link.isConnected(); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$clientConnected = connected; + } +} diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java new file mode 100644 index 0000000..84cd191 --- /dev/null +++ b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.wireless.endpoint; + +import appeng.api.networking.IGridNode; +import com.extendedae_plus.wireless.IWirelessEndpoint; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * 通用 IWirelessEndpoint:通过提供方块实体与节点的 Supplier 实现。 + */ +public class GenericNodeEndpointImpl implements IWirelessEndpoint { + private final Supplier blockEntitySupplier; + private final Supplier nodeSupplier; + + public GenericNodeEndpointImpl(Supplier blockEntitySupplier, Supplier nodeSupplier) { + this.blockEntitySupplier = Objects.requireNonNull(blockEntitySupplier); + this.nodeSupplier = Objects.requireNonNull(nodeSupplier); + } + + @Override + public ServerLevel getServerLevel() { + var be = blockEntitySupplier.get(); + if (be == null) return null; + Level lvl = be.getLevel(); + return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null; + } + + @Override + public BlockPos getBlockPos() { + var be = blockEntitySupplier.get(); + return be != null ? be.getBlockPos() : BlockPos.ZERO; + } + + @Override + public IGridNode getGridNode() { + return nodeSupplier.get(); + } + + @Override + public boolean isEndpointRemoved() { + var be = blockEntitySupplier.get(); + return be == null || be.isRemoved(); + } +} diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java new file mode 100644 index 0000000..6736f4c --- /dev/null +++ b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.wireless.endpoint; + +import appeng.api.networking.IGridNode; +import appeng.helpers.InterfaceLogicHost; +import com.extendedae_plus.wireless.IWirelessEndpoint; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * IWirelessEndpoint 实现:基于 InterfaceLogicHost 与节点提供者。 + */ +public class InterfaceNodeEndpointImpl implements IWirelessEndpoint { + private final InterfaceLogicHost host; + private final Supplier nodeSupplier; + + public InterfaceNodeEndpointImpl(InterfaceLogicHost host, Supplier nodeSupplier) { + this.host = Objects.requireNonNull(host); + this.nodeSupplier = Objects.requireNonNull(nodeSupplier); + } + + @Override + public ServerLevel getServerLevel() { + var be = host.getBlockEntity(); + if (be == null) return null; + Level lvl = be.getLevel(); + return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null; + } + + @Override + public BlockPos getBlockPos() { + var be = host.getBlockEntity(); + return be != null ? be.getBlockPos() : BlockPos.ZERO; + } + + @Override + public IGridNode getGridNode() { + return nodeSupplier.get(); + } + + @Override + public boolean isEndpointRemoved() { + var be = host.getBlockEntity(); + return be == null || be.isRemoved(); + } +} diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index 30340f1..764c9ae 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -18,6 +18,10 @@ "item.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator", "item.extendedae_plus.network_pattern_controller": "Pattern Provider Status Controller", "item.extendedae_plus.assembler_matrix_upload_core": "Assembly Matrix Upload Core", + "item.extendedae_plus.channel_card": "Channel Card", + "item.extendedae_plus.channel_card.channel": "Channel: %d", + "item.extendedae_plus.channel_card.channel.unset": "Channel: Unset", + "item.extendedae_plus.channel_card.set": "Channel set to: %d", "item.extendedae_plus.entity_speed_card": "Entity Speed Card", "item.extendedae_plus.entity_speed_card.x2": "Entity Speed Card (x2)", "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index b026489..e7cb381 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -18,6 +18,10 @@ "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", "item.extendedae_plus.assembler_matrix_upload_core": "装配矩阵上传核心", + "item.extendedae_plus.channel_card": "频道卡", + "item.extendedae_plus.channel_card.channel": "频道: %d", + "item.extendedae_plus.channel_card.channel.unset": "频道: 未设置", + "item.extendedae_plus.channel_card.set": "频道已设置为: %d", "item.extendedae_plus.entity_speed_card": "实体加速卡", "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", diff --git a/src/main/resources/assets/extendedae_plus/models/item/channel_card.json b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json new file mode 100644 index 0000000..8699938 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "extendedae_plus:item/channel_card" + } +} diff --git a/src/main/resources/data/extendedae_plus/recipes/channel_card.json b/src/main/resources/data/extendedae_plus/recipes/channel_card.json new file mode 100644 index 0000000..d6b23ce --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipes/channel_card.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { "item": "ae2:advanced_card" }, + { "item": "extendedae_plus:wireless_transceiver" } + ], + "result": { "item": "extendedae_plus:channel_card", "count": 1 } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 034dc6d..1e725ba 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -25,8 +25,11 @@ "ae2.autopattern.CraftingTreeNodeMixin", "ae2.autopattern.CraftingTreeProcessMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", + "ae2.helpers.InterfaceLogicChannelCardMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.parts.automation.IOBusPartChannelCardMixin", + "ae2.parts.storagebus.StorageBusPartChannelCardMixin", "ae2.menu.ContainerPatternEncodingTermMenuMixin", "ae2.menu.MEStorageMenuMixin", "ae2.menu.PatternEncodingTermMenuMixin",