添加样板供应器状态控制器方块
This commit is contained in:
parent
8bebfbe4ac
commit
0fc9959ee7
|
|
@ -6,6 +6,7 @@ import com.extendedae_plus.init.ModBlockEntities;
|
|||
import com.extendedae_plus.init.ModBlocks;
|
||||
import com.extendedae_plus.init.ModCreativeTabs;
|
||||
import com.extendedae_plus.init.ModItems;
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.menu.locator.CuriosItemLocator;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import net.minecraftforge.client.ConfigScreenHandler;
|
||||
|
|
@ -21,6 +22,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||
import com.extendedae_plus.client.ClientProxy;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
/**
|
||||
* ExtendedAE Plus 主mod类
|
||||
|
|
@ -30,13 +32,7 @@ public class ExtendedAEPlus {
|
|||
|
||||
public static final String MODID = "extendedae_plus";
|
||||
|
||||
// 在类加载时(尽可能早)在客户端注册内置模型,避免首次资源加载时错过。
|
||||
static {
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
System.out.println("[ExtendedAE_Plus] Static init: register built-in models");
|
||||
ClientProxy.init();
|
||||
});
|
||||
}
|
||||
// 注意:避免在静态初始化阶段访问注册对象,相关客户端注册改在 FMLClientSetupEvent 中执行。
|
||||
|
||||
public ExtendedAEPlus() {
|
||||
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
|
|
@ -49,6 +45,7 @@ public class ExtendedAEPlus {
|
|||
ModBlockEntities.BLOCK_ENTITY_TYPES.register(modEventBus);
|
||||
ModItems.ITEMS.register(modEventBus);
|
||||
ModCreativeTabs.TABS.register(modEventBus);
|
||||
ModMenuTypes.MENUS.register(modEventBus);
|
||||
|
||||
// 注册到Forge事件总线
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
|
|
@ -56,11 +53,8 @@ public class ExtendedAEPlus {
|
|||
// 注册通用配置
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC);
|
||||
|
||||
// 构造期在客户端再确保一次注册(幂等)
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientProxy::init);
|
||||
|
||||
// 在 Mods 菜单中注册配置界面入口(仅客户端,由 ClientProxy 执行以避免服务端类加载 Screen)
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientProxy::registerConfigScreen);
|
||||
// 客户端侧延迟注册:在 FMLClientSetupEvent 阶段执行(包含 MenuScreens 绑定等)
|
||||
modEventBus.addListener((FMLClientSetupEvent e) -> ClientProxy.onClientSetup(e));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,10 +3,14 @@ package com.extendedae_plus.client;
|
|||
import appeng.client.render.crafting.CraftingCubeModel;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.client.render.crafting.EPlusCraftingCubeModelProvider;
|
||||
import com.extendedae_plus.client.screen.GlobalProviderModesScreen;
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.content.crafting.EPlusCraftingUnitType;
|
||||
import com.extendedae_plus.hooks.BuiltInModelHooks;
|
||||
import net.minecraftforge.client.ConfigScreenHandler;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraft.client.gui.screens.MenuScreens;
|
||||
|
||||
/**
|
||||
* 客户端模型注册,将 formed 模型注册为内置模型。
|
||||
|
|
@ -39,6 +43,19 @@ public final class ClientProxy {
|
|||
BuiltInModelHooks.addBuiltInModel(
|
||||
ExtendedAEPlus.id("block/crafting/1024x_accelerator_formed_v2"),
|
||||
new CraftingCubeModel(new EPlusCraftingCubeModelProvider(EPlusCraftingUnitType.ACCELERATOR_1024x)));
|
||||
|
||||
// 菜单 -> 屏幕 绑定
|
||||
MenuScreens.register(ModMenuTypes.NETWORK_PATTERN_CONTROLLER.get(), GlobalProviderModesScreen::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端设置阶段:延迟执行需要访问注册对象的客户端注册。
|
||||
*/
|
||||
public static void onClientSetup(final FMLClientSetupEvent event) {
|
||||
event.enqueueWork(() -> {
|
||||
init();
|
||||
registerConfigScreen();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
package com.extendedae_plus.client.screen;
|
||||
|
||||
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
||||
import com.extendedae_plus.network.GlobalToggleProviderModesC2SPacket;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
public class GlobalProviderModesScreen extends AbstractContainerScreen<NetworkPatternControllerMenu> {
|
||||
private static final Component CUSTOM_TITLE = Component.literal("样板供应器状态控制器");
|
||||
public GlobalProviderModesScreen(NetworkPatternControllerMenu menu, Inventory inv, Component title) {
|
||||
super(menu, inv, title);
|
||||
this.imageWidth = 240;
|
||||
this.imageHeight = 140;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
int w = 70; // 按钮宽
|
||||
int h = 20; // 按钮高
|
||||
int s = 8; // 按钮间距
|
||||
int y = this.topPos + 28; // 第一行 Y
|
||||
// 计算三列按钮的左侧起点,使其在面板内水平居中
|
||||
int totalW3 = w * 3 + s * 2;
|
||||
int x = this.leftPos + (this.imageWidth - totalW3) / 2;
|
||||
|
||||
// 行1:三个单项切换
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_blocking"), b ->
|
||||
ModNetwork.CHANNEL.sendToServer(new GlobalToggleProviderModesC2SPacket(
|
||||
GlobalToggleProviderModesC2SPacket.Op.TOGGLE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
this.menu.getBlockEntityPos()
|
||||
))).bounds(x, y, w, h).build());
|
||||
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_adv_blocking"), b ->
|
||||
ModNetwork.CHANNEL.sendToServer(new GlobalToggleProviderModesC2SPacket(
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
GlobalToggleProviderModesC2SPacket.Op.TOGGLE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
this.menu.getBlockEntityPos()
|
||||
))).bounds(x + w + s, y, w, h).build());
|
||||
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.toggle_smart_doubling"), b ->
|
||||
ModNetwork.CHANNEL.sendToServer(new GlobalToggleProviderModesC2SPacket(
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
GlobalToggleProviderModesC2SPacket.Op.NOOP,
|
||||
GlobalToggleProviderModesC2SPacket.Op.TOGGLE,
|
||||
this.menu.getBlockEntityPos()
|
||||
))).bounds(x + (w + s) * 2, y, w, h).build());
|
||||
|
||||
// 行2:一键全开/全关
|
||||
int y2 = y + h + 12;
|
||||
// 第二行:两列按钮,总宽并居中
|
||||
int totalW2 = w * 2 + s;
|
||||
int x2 = this.leftPos + (this.imageWidth - totalW2) / 2;
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_on"), b ->
|
||||
ModNetwork.CHANNEL.sendToServer(new GlobalToggleProviderModesC2SPacket(
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_TRUE,
|
||||
this.menu.getBlockEntityPos()
|
||||
))).bounds(x2, y2, w, h).build());
|
||||
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.extendedae_plus.global.all_off"), b ->
|
||||
ModNetwork.CHANNEL.sendToServer(new GlobalToggleProviderModesC2SPacket(
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE,
|
||||
GlobalToggleProviderModesC2SPacket.Op.SET_FALSE,
|
||||
this.menu.getBlockEntityPos()
|
||||
))).bounds(x2 + w + s, y2, w, h).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(net.minecraft.client.gui.GuiGraphics gfx, float partialTicks, int mouseX, int mouseY) {
|
||||
// 半透明全屏遮罩,避免底层 HUD(准星/物品栏文字)透出
|
||||
gfx.fill(0, 0, this.width, this.height, 0xC0000000);
|
||||
|
||||
// 在按钮区域绘制一个半透明面板,提升可读性
|
||||
int pad = 6;
|
||||
int panelLeft = this.leftPos - pad;
|
||||
int panelTop = this.topPos - pad;
|
||||
int panelRight = this.leftPos + this.imageWidth + pad;
|
||||
int panelBottom = this.topPos + this.imageHeight + pad;
|
||||
gfx.fill(panelLeft, panelTop, panelRight, panelBottom, 0xA01E1E1E);
|
||||
// 边框
|
||||
gfx.fill(panelLeft, panelTop, panelRight, panelTop + 1, 0x80FFFFFF);
|
||||
gfx.fill(panelLeft, panelBottom - 1, panelRight, panelBottom, 0x80000000);
|
||||
gfx.fill(panelLeft, panelTop, panelLeft + 1, panelBottom, 0x80FFFFFF);
|
||||
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);
|
||||
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() 中手动绘制
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.extendedae_plus.content.controller;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
|
||||
public class NetworkPatternControllerBlock extends Block implements EntityBlock {
|
||||
public NetworkPatternControllerBlock(Properties props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new NetworkPatternControllerBlockEntity(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
if (!level.isClientSide) {
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be instanceof MenuProvider provider && player instanceof ServerPlayer sp) {
|
||||
NetworkHooks.openScreen(sp, provider, pos);
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
}
|
||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.extendedae_plus.content.controller;
|
||||
|
||||
import appeng.api.networking.GridHelper;
|
||||
import appeng.api.networking.GridFlags;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridNodeListener;
|
||||
import appeng.api.networking.IInWorldGridNodeHost;
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import com.extendedae_plus.init.ModBlockEntities;
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class NetworkPatternControllerBlockEntity extends BlockEntity implements IInWorldGridNodeHost, MenuProvider {
|
||||
|
||||
private final IManagedGridNode managedNode;
|
||||
|
||||
public NetworkPatternControllerBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(ModBlockEntities.NETWORK_PATTERN_CONTROLLER_BE.get(), pos, state);
|
||||
this.managedNode = GridHelper.createManagedNode(this, NodeListener.INSTANCE);
|
||||
this.managedNode.setIdlePowerUsage(1.0);
|
||||
this.managedNode.setInWorldNode(true);
|
||||
this.managedNode.setFlags(GridFlags.REQUIRE_CHANNEL);
|
||||
this.managedNode.setTagName("network_pattern_controller");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IGridNode getGridNode(@Nullable Direction dir) {
|
||||
return managedNode == null ? null : managedNode.getNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
if (this.level != null && !this.level.isClientSide) {
|
||||
GridHelper.onFirstTick(this, be -> be.managedNode.create(be.getLevel(), be.getBlockPos()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
this.managedNode.saveToNBT(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(CompoundTag tag) {
|
||||
super.load(tag);
|
||||
this.managedNode.loadFromNBT(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkUnloaded() {
|
||||
super.onChunkUnloaded();
|
||||
this.managedNode.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
super.setRemoved();
|
||||
this.managedNode.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getDisplayName() {
|
||||
return Component.translatable("block.extendedae_plus.network_pattern_controller");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractContainerMenu createMenu(int id, Inventory inv, Player player) {
|
||||
return new NetworkPatternControllerMenu(id, inv, this.worldPosition);
|
||||
}
|
||||
|
||||
enum NodeListener implements IGridNodeListener<NetworkPatternControllerBlockEntity> {
|
||||
INSTANCE;
|
||||
@Override
|
||||
public void onSaveChanges(NetworkPatternControllerBlockEntity host, IGridNode node) {
|
||||
host.setChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.extendedae_plus.init;
|
|||
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
||||
import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
|
@ -17,4 +18,9 @@ public final class ModBlockEntities {
|
|||
BLOCK_ENTITY_TYPES.register("wireless_transceiver",
|
||||
() -> BlockEntityType.Builder.of(WirelessTransceiverBlockEntity::new,
|
||||
ModBlocks.WIRELESS_TRANSCEIVER.get()).build(null));
|
||||
|
||||
public static final RegistryObject<BlockEntityType<NetworkPatternControllerBlockEntity>> NETWORK_PATTERN_CONTROLLER_BE =
|
||||
BLOCK_ENTITY_TYPES.register("network_pattern_controller",
|
||||
() -> BlockEntityType.Builder.of(NetworkPatternControllerBlockEntity::new,
|
||||
ModBlocks.NETWORK_PATTERN_CONTROLLER.get()).build(null));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,17 @@ public final class ModBlocks {
|
|||
)
|
||||
);
|
||||
|
||||
// AE2 网络模式控制器方块
|
||||
public static final RegistryObject<Block> NETWORK_PATTERN_CONTROLLER = BLOCKS.register(
|
||||
"network_pattern_controller",
|
||||
() -> new com.extendedae_plus.content.controller.NetworkPatternControllerBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.mapColor(MapColor.METAL)
|
||||
.strength(1.5F, 6.0F)
|
||||
.requiresCorrectToolForDrops()
|
||||
)
|
||||
);
|
||||
|
||||
// Crafting Accelerators (reuse MAE2 textures/models)
|
||||
public static final RegistryObject<CraftingUnitBlock> ACCELERATOR_4x = BLOCKS.register(
|
||||
"4x_crafting_accelerator",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public final class ModCreativeTabs {
|
|||
.displayItems((params, output) -> {
|
||||
// 将本模组物品加入创造物品栏
|
||||
output.accept(ModItems.WIRELESS_TRANSCEIVER.get());
|
||||
output.accept(ModItems.NETWORK_PATTERN_CONTROLLER.get());
|
||||
output.accept(ModItems.ACCELERATOR_4x.get());
|
||||
output.accept(ModItems.ACCELERATOR_16x.get());
|
||||
output.accept(ModItems.ACCELERATOR_64x.get());
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ public final class ModItems {
|
|||
() -> new BlockItem(ModBlocks.WIRELESS_TRANSCEIVER.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> NETWORK_PATTERN_CONTROLLER = ITEMS.register(
|
||||
"network_pattern_controller",
|
||||
() -> new BlockItem(ModBlocks.NETWORK_PATTERN_CONTROLLER.get(), new Item.Properties())
|
||||
);
|
||||
|
||||
// Crafting Accelerators
|
||||
public static final RegistryObject<Item> ACCELERATOR_4x = ITEMS.register(
|
||||
"4x_crafting_accelerator",
|
||||
|
|
|
|||
20
src/main/java/com/extendedae_plus/init/ModMenuTypes.java
Normal file
20
src/main/java/com/extendedae_plus/init/ModMenuTypes.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package com.extendedae_plus.init;
|
||||
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraftforge.common.extensions.IForgeMenuType;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
public final class ModMenuTypes {
|
||||
private ModMenuTypes() {}
|
||||
|
||||
public static final DeferredRegister<MenuType<?>> MENUS =
|
||||
DeferredRegister.create(ForgeRegistries.MENU_TYPES, ExtendedAEPlus.MODID);
|
||||
|
||||
public static final RegistryObject<MenuType<NetworkPatternControllerMenu>> NETWORK_PATTERN_CONTROLLER =
|
||||
MENUS.register("network_pattern_controller",
|
||||
() -> IForgeMenuType.create(NetworkPatternControllerMenu::new));
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.extendedae_plus.menu;
|
||||
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class NetworkPatternControllerMenu extends AbstractContainerMenu {
|
||||
private final BlockPos bePos;
|
||||
|
||||
public NetworkPatternControllerMenu(int id, Inventory inv, BlockPos bePos) {
|
||||
super(ModMenuTypes.NETWORK_PATTERN_CONTROLLER.get(), id);
|
||||
this.bePos = bePos;
|
||||
}
|
||||
|
||||
public NetworkPatternControllerMenu(int id, Inventory inv, FriendlyByteBuf buf) {
|
||||
this(id, inv, buf.readBlockPos());
|
||||
}
|
||||
|
||||
public BlockPos getBlockEntityPos() { return bePos; }
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) { return true; }
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
// 无物品槽的容器,直接返回空堆以禁用快速转移
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.extendedae_plus.network;
|
||||
|
||||
import appeng.api.config.Settings;
|
||||
import appeng.api.config.YesNo;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.blockentity.crafting.PatternProviderBlockEntity;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.parts.crafting.PatternProviderPart;
|
||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
||||
import com.extendedae_plus.api.SmartDoublingHolder;
|
||||
import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* C2S:全网批量切换样板供应器的三种模式:
|
||||
* - 阻挡模式(AE2 内置 BLOCKING_MODE 设置)
|
||||
* - 高级阻挡模式(AdvancedBlockingHolder mixin)
|
||||
* - 智能翻倍模式(SmartDoublingHolder mixin)
|
||||
*
|
||||
* 负载为三个操作码(各1字节),分别对应:blocking、advancedBlocking、smartDoubling。
|
||||
*/
|
||||
public class GlobalToggleProviderModesC2SPacket {
|
||||
public enum Op {
|
||||
NOOP((byte) 0),
|
||||
SET_TRUE((byte) 1),
|
||||
SET_FALSE((byte) 2),
|
||||
TOGGLE((byte) 3);
|
||||
public final byte id;
|
||||
Op(byte id) { this.id = id; }
|
||||
public static Op byId(byte id) {
|
||||
return switch (id) {
|
||||
case 1 -> SET_TRUE;
|
||||
case 2 -> SET_FALSE;
|
||||
case 3 -> TOGGLE;
|
||||
default -> NOOP;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private final Op opBlocking;
|
||||
private final Op opAdvancedBlocking;
|
||||
private final Op opSmartDoubling;
|
||||
private final BlockPos controllerPos;
|
||||
|
||||
public GlobalToggleProviderModesC2SPacket(Op opBlocking, Op opAdvancedBlocking, Op opSmartDoubling, BlockPos controllerPos) {
|
||||
this.opBlocking = opBlocking;
|
||||
this.opAdvancedBlocking = opAdvancedBlocking;
|
||||
this.opSmartDoubling = opSmartDoubling;
|
||||
this.controllerPos = controllerPos;
|
||||
}
|
||||
|
||||
public static void encode(GlobalToggleProviderModesC2SPacket msg, FriendlyByteBuf buf) {
|
||||
buf.writeByte(msg.opBlocking.id);
|
||||
buf.writeByte(msg.opAdvancedBlocking.id);
|
||||
buf.writeByte(msg.opSmartDoubling.id);
|
||||
buf.writeBlockPos(msg.controllerPos);
|
||||
}
|
||||
|
||||
public static GlobalToggleProviderModesC2SPacket decode(FriendlyByteBuf buf) {
|
||||
Op b = Op.byId(buf.readByte());
|
||||
Op ab = Op.byId(buf.readByte());
|
||||
Op sd = Op.byId(buf.readByte());
|
||||
BlockPos pos = buf.readBlockPos();
|
||||
return new GlobalToggleProviderModesC2SPacket(b, ab, sd, pos);
|
||||
}
|
||||
|
||||
public static void handle(GlobalToggleProviderModesC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
ctx.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.getSender();
|
||||
if (player == null) return;
|
||||
|
||||
// 从控制方块实体的 AE2 节点确定 AE 网络上下文
|
||||
var level = player.serverLevel();
|
||||
var be = level.getBlockEntity(msg.controllerPos);
|
||||
if (!(be instanceof NetworkPatternControllerBlockEntity controller)) return;
|
||||
var node = controller.getGridNode(null);
|
||||
if (node == null) return;
|
||||
IGrid grid = node.getGrid();
|
||||
if (grid == null) return;
|
||||
|
||||
int affected = applyToAllProviders(grid, msg);
|
||||
// 向发起玩家反馈影响数量,便于判断按钮是否生效
|
||||
player.displayClientMessage(Component.literal("E+ 全局切换已应用到 " + affected + " 个样板供应器"), true);
|
||||
});
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
|
||||
private static int applyToAllProviders(IGrid grid, GlobalToggleProviderModesC2SPacket msg) {
|
||||
int affected = 0;
|
||||
// 去重集合,避免同一逻辑重复计数
|
||||
Set<PatternProviderLogic> all = new HashSet<>();
|
||||
|
||||
// 方块形式的样板供应器(全部/在线)
|
||||
try {
|
||||
Set<PatternProviderBlockEntity> blocksAll = grid.getMachines(PatternProviderBlockEntity.class);
|
||||
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 形式的样板供应器(全部/在线)
|
||||
try {
|
||||
Set<PatternProviderPart> partsAll = grid.getMachines(PatternProviderPart.class);
|
||||
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)
|
||||
try {
|
||||
Set<PatternProviderLogicHost> hostsAll = grid.getMachines(PatternProviderLogicHost.class);
|
||||
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 不返回的问题
|
||||
collectByClassName(grid, all, "com.glodblock.github.extendedae.common.parts.PartExPatternProvider");
|
||||
collectByClassName(grid, all, "com.glodblock.github.extendedae.common.tileentities.TileExPatternProvider");
|
||||
|
||||
for (PatternProviderLogic logic : all) {
|
||||
if (applyToLogic(logic, msg)) affected++;
|
||||
}
|
||||
return affected;
|
||||
}
|
||||
|
||||
private static void collectByClassName(IGrid grid, Set<PatternProviderLogic> out, String className) {
|
||||
try {
|
||||
Class<?> cls = Class.forName(className);
|
||||
// 收集全部与在线两类机器
|
||||
Set<?> all = grid.getMachines((Class) cls);
|
||||
Set<?> active = grid.getActiveMachines((Class) cls);
|
||||
for (Object o : all) addLogicIfPresent(out, o);
|
||||
for (Object o : active) addLogicIfPresent(out, o);
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
private static void addLogicIfPresent(Set<PatternProviderLogic> out, Object o) {
|
||||
try {
|
||||
if (o instanceof PatternProviderLogicHost host) {
|
||||
var logic = host.getLogic();
|
||||
if (logic != null) out.add(logic);
|
||||
return;
|
||||
}
|
||||
// 兜底:若对象有 getLogic 方法且返回 PatternProviderLogic
|
||||
var m = o.getClass().getMethod("getLogic");
|
||||
Object ret = m.invoke(o);
|
||||
if (ret instanceof PatternProviderLogic logic) out.add(logic);
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
private static boolean applyToLogic(PatternProviderLogic logic, GlobalToggleProviderModesC2SPacket msg) {
|
||||
if (logic == null) return false;
|
||||
boolean changed = false;
|
||||
// 1) 阻挡模式(AE2 内置设置)
|
||||
if (msg.opBlocking != Op.NOOP) {
|
||||
boolean current = safeIsBlocking(logic);
|
||||
boolean target = computeTarget(current, msg.opBlocking);
|
||||
var cm = logic.getConfigManager();
|
||||
if (cm != null) {
|
||||
cm.putSetting(Settings.BLOCKING_MODE, target ? YesNo.YES : YesNo.NO);
|
||||
changed = changed || (current != target);
|
||||
}
|
||||
}
|
||||
// 2) 高级阻挡(mixin 接口)
|
||||
if (msg.opAdvancedBlocking != Op.NOOP && logic instanceof AdvancedBlockingHolder 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 SmartDoublingHolder 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) {
|
||||
case SET_TRUE -> true;
|
||||
case SET_FALSE -> false;
|
||||
case TOGGLE -> !current;
|
||||
default -> current;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean safeIsBlocking(PatternProviderLogic logic) {
|
||||
try { return logic.isBlocking(); } catch (Throwable t) { return false; }
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +72,12 @@ public class ModNetwork {
|
|||
.consumerNetworkThread(ToggleSmartDoublingC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(GlobalToggleProviderModesC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(GlobalToggleProviderModesC2SPacket::encode)
|
||||
.decoder(GlobalToggleProviderModesC2SPacket::decode)
|
||||
.consumerNetworkThread(GlobalToggleProviderModesC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(AdvancedBlockingSyncS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(AdvancedBlockingSyncS2CPacket::encode)
|
||||
.decoder(AdvancedBlockingSyncS2CPacket::decode)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"variants": {
|
||||
"": { "model": "extendedae_plus:block/network_pattern_controller" }
|
||||
}
|
||||
}
|
||||
|
|
@ -43,5 +43,12 @@
|
|||
"config.extendedae_plus.pageMultiplier_with_range": "扩展样板供应器槽位倍率 (1-64)",
|
||||
"config.extendedae_plus.wirelessMaxRange": "无线最大距离",
|
||||
"config.extendedae_plus.wirelessMaxRange_with_range": "无线最大距离 (1-4096)",
|
||||
"config.extendedae_plus.wirelessCrossDimEnable": "无线收发器允许跨维度连接"
|
||||
"config.extendedae_plus.wirelessCrossDimEnable": "无线收发器允许跨维度连接",
|
||||
"block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器",
|
||||
"item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器",
|
||||
"gui.extendedae_plus.global.toggle_blocking": "切换阻挡模式",
|
||||
"gui.extendedae_plus.global.toggle_adv_blocking": "切换高级阻挡",
|
||||
"gui.extendedae_plus.global.toggle_smart_doubling": "切换智能翻倍",
|
||||
"gui.extendedae_plus.global.all_on": "全部开启",
|
||||
"gui.extendedae_plus.global.all_off": "全部关闭"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"parent": "minecraft:block/cube_all",
|
||||
"textures": {
|
||||
"all": "extendedae_plus:block/network_pattern_controller"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"parent": "extendedae_plus:block/network_pattern_controller"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1010 KiB |
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"ingredients": [
|
||||
{ "item": "ae2:semi_dark_monitor" },
|
||||
{ "item": "ae2:pattern_provider" },
|
||||
{ "item": "ae2:network_tool" }
|
||||
],
|
||||
"result": {
|
||||
"item": "extendedae_plus:network_pattern_controller",
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user