添加了无线收发器方块,支持一对多连接提供me频道

This commit is contained in:
GaLicn 2025-08-12 23:42:53 +08:00
parent 319253d6f9
commit 3d2e7b7165
16 changed files with 641 additions and 2 deletions

View File

@ -7,6 +7,9 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.config.ModConfig;
import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModBlocks;
import com.extendedae_plus.init.ModBlockEntities;
import com.extendedae_plus.init.ModItems;
/**
* ExtendedAE Plus 主mod类
@ -22,6 +25,11 @@ public class ExtendedAEPlus {
// 注册mod初始化事件
modEventBus.addListener(this::commonSetup);
// 注册方块与方块实体
ModBlocks.BLOCKS.register(modEventBus);
ModBlockEntities.BLOCK_ENTITY_TYPES.register(modEventBus);
ModItems.ITEMS.register(modEventBus);
// 注册到Forge事件总线
MinecraftForge.EVENT_BUS.register(this);

View File

@ -5,6 +5,7 @@ import net.minecraftforge.common.ForgeConfigSpec;
public final class ModConfigs {
public static final ForgeConfigSpec COMMON_SPEC;
public static final ForgeConfigSpec.IntValue PAGE_MULTIPLIER;
public static final ForgeConfigSpec.DoubleValue WIRELESS_MAX_RANGE;
static {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
@ -15,6 +16,14 @@ public final class ModConfigs {
"基础为36每页仍显示36格倍率会增加总页数/总容量。",
"建议范围 1-16")
.defineInRange("pageMultiplier", 1, 1, 64);
// 无线收发器最大连接距离单位方块
// 一对多从端连接主端时将以该值作为范围限制
WIRELESS_MAX_RANGE = builder
.comment(
"无线收发器最大连接距离(单位:方块)",
"从端与主端的直线距离需小于等于该值才会建立连接。")
.defineInRange("wirelessMaxRange", 256.0D, 1.0D, 4096.0D);
builder.pop();
COMMON_SPEC = builder.build();
}

View File

@ -0,0 +1,70 @@
package com.extendedae_plus.content.wireless;
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.entity.player.Player;
import net.minecraft.world.level.BlockGetter;
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.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.Nullable;
import com.extendedae_plus.init.ModBlockEntities;
public class WirelessTransceiverBlock extends Block implements EntityBlock {
public WirelessTransceiverBlock(Properties props) {
super(props);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new WirelessTransceiverBlockEntity(pos, state);
}
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (level.isClientSide) {
return InteractionResult.SUCCESS;
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof WirelessTransceiverBlockEntity te) {
boolean sneaking = player.isShiftKeyDown();
if (sneaking) {
// 简单演示Shift+右键 频率+1
long f = te.getFrequency();
te.setFrequency(f + 1);
player.displayClientMessage(net.minecraft.network.chat.Component.literal("Freq: " + te.getFrequency()), true);
} else {
te.setMasterMode(!te.isMasterMode());
player.displayClientMessage(net.minecraft.network.chat.Component.literal(te.isMasterMode() ? "Mode: MASTER" : "Mode: SLAVE"), true);
}
return InteractionResult.CONSUME;
}
return InteractionResult.PASS;
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (!state.is(newState.getBlock())) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof WirelessTransceiverBlockEntity te) {
te.onRemoved();
}
}
super.onRemove(state, level, pos, newState, isMoving);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
if (level.isClientSide) return null;
return type == ModBlockEntities.WIRELESS_TRANSCEIVER_BE.get()
? (lvl, pos, st, be) -> WirelessTransceiverBlockEntity.serverTick(lvl, pos, st, (WirelessTransceiverBlockEntity) be)
: null;
}
}

View File

@ -0,0 +1,200 @@
package com.extendedae_plus.content.wireless;
import appeng.api.networking.GridHelper;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridNodeListener;
import appeng.api.networking.IManagedGridNode;
import appeng.api.networking.IInWorldGridNodeHost;
import com.extendedae_plus.wireless.IWirelessEndpoint;
import com.extendedae_plus.wireless.WirelessMasterLink;
import com.extendedae_plus.wireless.WirelessSlaveLink;
import com.extendedae_plus.init.ModBlockEntities;
import com.extendedae_plus.init.ModItems;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.core.Direction;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
/**
* 无线收发器方块实体骨架
* - /从模式切换
* - 频率设置
* - 集成 AE2 节点
* - 集成无线主/从逻辑
*
* 注意本类不包含注册常量需在你的注册系统中完成 BlockEntityType 的创建与绑定
*/
public class WirelessTransceiverBlockEntity extends BlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost {
private IManagedGridNode managedNode;
private long frequency = 0L;
private boolean masterMode = true;
private WirelessMasterLink masterLink;
private WirelessSlaveLink slaveLink;
public WirelessTransceiverBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.WIRELESS_TRANSCEIVER_BE.get(), pos, state);
// 创建 AE2 管理节点
this.managedNode = GridHelper.createManagedNode(this, NodeListener.INSTANCE);
this.managedNode.setIdlePowerUsage(1.0); // 可按需调整基础待机功耗
this.managedNode.setTagName("wireless_node");
this.managedNode.setInWorldNode(true);
this.managedNode.setExposedOnSides(EnumSet.allOf(Direction.class));
// 可见表示方便在 AE2 界面中识别可选
this.managedNode.setVisualRepresentation(ModItems.WIRELESS_TRANSCEIVER.get().getDefaultInstance());
// 初始化无线逻辑
this.masterLink = new WirelessMasterLink(this);
this.slaveLink = new WirelessSlaveLink(this);
}
/* ===================== IInWorldGridNodeHost ===================== */
@Override
public @Nullable IGridNode getGridNode(Direction dir) {
return getGridNode();
}
/* ===================== IWirelessEndpoint ===================== */
@Override
public ServerLevel getServerLevel() {
Level lvl = super.getLevel();
return lvl instanceof ServerLevel sl ? sl : null;
}
@Override
public BlockPos getBlockPos() {
return this.worldPosition;
}
@Override
public IGridNode getGridNode() {
return managedNode == null ? null : managedNode.getNode();
}
@Override
public boolean isEndpointRemoved() {
return super.isRemoved();
}
/* ===================== 公共方法(交互调用) ===================== */
public long getFrequency() {
return frequency;
}
public void setFrequency(long frequency) {
if (this.frequency == frequency) return;
this.frequency = frequency;
if (isMasterMode()) {
masterLink.setFrequency(frequency);
} else {
slaveLink.setFrequency(frequency);
}
setChanged();
}
public boolean isMasterMode() {
return masterMode;
}
public void setMasterMode(boolean masterMode) {
if (this.masterMode == masterMode) return;
// 切换前清理原模式状态
if (this.masterMode) {
masterLink.onUnloadOrRemove();
} else {
slaveLink.onUnloadOrRemove();
}
this.masterMode = masterMode;
// 切换后应用频率
if (this.masterMode) {
masterLink.setFrequency(frequency);
} else {
slaveLink.setFrequency(frequency);
}
setChanged();
}
public void onRemoved() {
if (this.masterMode) {
masterLink.onUnloadOrRemove();
} else {
slaveLink.onUnloadOrRemove();
}
if (managedNode != null) {
managedNode.destroy();
}
}
/* ===================== Tick ===================== */
public static void serverTick(Level level, BlockPos pos, BlockState state, WirelessTransceiverBlockEntity be) {
if (!(level instanceof ServerLevel)) return;
if (!be.masterMode) {
// 从端需要周期检查与维护连接
be.slaveLink.updateStatus();
}
}
@Override
public void onLoad() {
super.onLoad();
// 仅服务端创建节点
ServerLevel sl = getServerLevel();
if (sl == null) return;
// 在首个 tick 创建以保证区块已就绪
GridHelper.onFirstTick(this, be -> {
be.managedNode.create(be.getLevel(), be.getBlockPos());
});
}
/* ===================== NBT ===================== */
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putLong("frequency", frequency);
tag.putBoolean("master", masterMode);
if (managedNode != null) {
managedNode.saveToNBT(tag);
}
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
this.frequency = tag.getLong("frequency");
this.masterMode = tag.getBoolean("master");
if (managedNode != null) {
managedNode.loadFromNBT(tag);
}
// 应用到链接器
if (masterMode) {
masterLink.setFrequency(frequency);
} else {
slaveLink.setFrequency(frequency);
}
}
/* ===================== AE2 节点监听 ===================== */
enum NodeListener implements IGridNodeListener<WirelessTransceiverBlockEntity> {
INSTANCE;
@Override
public void onSaveChanges(WirelessTransceiverBlockEntity host, IGridNode node) {
host.setChanged();
}
@Override
public void onStateChanged(WirelessTransceiverBlockEntity host, IGridNode node, State state) {
// 可在此响应 POWER/CHANNEL 等变化刷新显示等
}
@Override
public void onInWorldConnectionChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
@Override
public void onGridChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
@Override
public void onOwnerChanged(WirelessTransceiverBlockEntity host, IGridNode node) {}
}
}

View File

@ -0,0 +1,20 @@
package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public final class ModBlockEntities {
private ModBlockEntities() {}
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITY_TYPES =
DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, ExtendedAEPlus.MODID);
public static final RegistryObject<BlockEntityType<WirelessTransceiverBlockEntity>> WIRELESS_TRANSCEIVER_BE =
BLOCK_ENTITY_TYPES.register("wireless_transceiver",
() -> BlockEntityType.Builder.of(WirelessTransceiverBlockEntity::new,
ModBlocks.WIRELESS_TRANSCEIVER.get()).build(null));
}

View File

@ -0,0 +1,21 @@
package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.MapColor;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public final class ModBlocks {
private ModBlocks() {}
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, ExtendedAEPlus.MODID);
public static final RegistryObject<Block> WIRELESS_TRANSCEIVER = BLOCKS.register(
"wireless_transceiver",
() -> new WirelessTransceiverBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).strength(2.0F, 6.0F))
);
}

View File

@ -0,0 +1,20 @@
package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public final class ModItems {
private ModItems() {}
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, ExtendedAEPlus.MODID);
public static final RegistryObject<Item> WIRELESS_TRANSCEIVER = ITEMS.register(
"wireless_transceiver",
() -> new BlockItem(ModBlocks.WIRELESS_TRANSCEIVER.get(), new Item.Properties())
);
}

View File

@ -0,0 +1,24 @@
package com.extendedae_plus.wireless;
import appeng.api.networking.IGridNode;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
/**
* 无线端点最小接口
* 你的无线收发器方块实体需实现该接口
* 以便无线逻辑能够获取世界位置与 AE2 节点
*/
public interface IWirelessEndpoint {
/** 返回方块所在的服务端世界(避免与 BlockEntity#getLevel 冲突) */
ServerLevel getServerLevel();
/** 返回方块位置 */
BlockPos getBlockPos();
/** 返回可用于 AE2 连接的节点(通常为主节点) */
IGridNode getGridNode();
/** 是否已移除/销毁(端点视角),用于在卸载或破坏时停止连接 */
boolean isEndpointRemoved();
}

View File

@ -0,0 +1,51 @@
package com.extendedae_plus.wireless;
import net.minecraft.server.level.ServerLevel;
/**
* 主收发器端逻辑负责在频率变化/加载时向注册中心登记唯一主端卸载时反注册
* 方块实体应在合适的生命周期中调用 register/unregister
*/
public class WirelessMasterLink {
private final IWirelessEndpoint host;
private long frequency; // 0 为未设置
private boolean registered;
public WirelessMasterLink(IWirelessEndpoint host) {
this.host = host;
}
public long getFrequency() { return frequency; }
public void setFrequency(long frequency) {
if (this.frequency == frequency) return;
// 先反注册旧频率
if (registered) {
unregister();
}
this.frequency = frequency;
// 再尝试注册新频率
if (frequency != 0L && !host.isEndpointRemoved()) {
register();
}
}
public boolean register() {
ServerLevel level = host.getServerLevel();
if (level == null || frequency == 0L) return false;
boolean ok = WirelessMasterRegistry.register(level, frequency, host);
this.registered = ok;
return ok;
}
public void unregister() {
ServerLevel level = host.getServerLevel();
if (!registered || level == null || frequency == 0L) return;
WirelessMasterRegistry.unregister(level, frequency, host);
registered = false;
}
public void onUnloadOrRemove() {
unregister();
}
}

View File

@ -0,0 +1,67 @@
package com.extendedae_plus.wireless;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 无线主端注册中心 维度 + 频率 唯一注册一个主收发器端点
* 从端通过本注册中心按频率查找主端实现一对多连接
*/
public final class WirelessMasterRegistry {
private WirelessMasterRegistry() {}
private static final Map<Key, WeakReference<IWirelessEndpoint>> MASTERS = new HashMap<>();
public static synchronized boolean register(ServerLevel level, long frequency, IWirelessEndpoint endpoint) {
Objects.requireNonNull(level, "level");
Objects.requireNonNull(endpoint, "endpoint");
if (frequency == 0L) return false;
final Key key = new Key(level.dimension(), frequency);
cleanupIfCleared(key);
var existing = MASTERS.get(key);
var existingVal = existing == null ? null : existing.get();
if (existingVal != null && !existingVal.isEndpointRemoved()) {
// 同维度同频率已经有主端
return false;
}
MASTERS.put(key, new WeakReference<>(endpoint));
return true;
}
public static synchronized void unregister(ServerLevel level, long frequency, IWirelessEndpoint endpoint) {
if (frequency == 0L || level == null) return;
final Key key = new Key(level.dimension(), frequency);
var ref = MASTERS.get(key);
if (ref != null) {
var cur = ref.get();
if (cur == null || cur == endpoint) {
MASTERS.remove(key);
}
}
}
public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency) {
if (frequency == 0L || level == null) return null;
final Key key = new Key(level.dimension(), frequency);
cleanupIfCleared(key);
var ref = MASTERS.get(key);
return ref == null ? null : ref.get();
}
private static void cleanupIfCleared(Key key) {
var ref = MASTERS.get(key);
if (ref != null && ref.get() == null) {
MASTERS.remove(key);
}
}
private record Key(ResourceKey<Level> dim, long freq) {
@Override public String toString() { return dim.location() + "#" + freq; }
}
}

View File

@ -0,0 +1,119 @@
package com.extendedae_plus.wireless;
import appeng.api.networking.GridHelper;
import appeng.api.networking.IGridNode;
import appeng.me.service.helpers.ConnectionWrapper;
import com.extendedae_plus.config.ModConfigs;
import net.minecraft.server.level.ServerLevel;
import java.util.Objects;
/**
* 从收发器连接器
* - 通过频率查找同维度主收发器
* - 校验距离<= ModConfigs.WIRELESS_MAX_RANGE
* - 动态创建/销毁 AE2 连接GridConnection实现一主多从
*/
public class WirelessSlaveLink {
private final IWirelessEndpoint host;
private long frequency; // 0 未设置
private ConnectionWrapper connection = new ConnectionWrapper(null);
private boolean shutdown = true;
private double distance;
public WirelessSlaveLink(IWirelessEndpoint host) {
this.host = Objects.requireNonNull(host);
}
public void setFrequency(long frequency) {
if (this.frequency != frequency) {
this.frequency = frequency;
// 频率变更立即尝试重连/断开
updateStatus();
}
}
public long getFrequency() {
return frequency;
}
public boolean isConnected() {
return !shutdown && connection.getConnection() != null;
}
public double getDistance() {
return distance;
}
/**
* 建议在 BE serverTick 或者频率/加载状态变化时调用
*/
public void updateStatus() {
if (host.isEndpointRemoved()) {
destroyConnection();
return;
}
final ServerLevel level = host.getServerLevel();
if (level == null || frequency == 0L) {
destroyConnection();
return;
}
IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency);
shutdown = false;
distance = 0.0D;
if (master != null && !master.isEndpointRemoved() && master.getServerLevel() == level) {
distance = Math.sqrt(master.getBlockPos().distSqr(host.getBlockPos()));
double maxRange = ModConfigs.WIRELESS_MAX_RANGE.get();
if (distance <= maxRange) {
// 保持/建立连接
try {
var current = connection.getConnection();
IGridNode a = host.getGridNode(); // 从端
IGridNode b = master.getGridNode(); // 主端
if (a == null || b == null) {
shutdown = true;
} else {
if (current != null) {
// 如果已连且目标相同则维持
var ca = current.a();
var cb = current.b();
if ((ca == a || cb == a) && (ca == b || cb == b)) {
return; // 连接已正确
}
// 否则先断开再重建
current.destroy();
connection = new ConnectionWrapper(null);
}
connection = new ConnectionWrapper(GridHelper.createConnection(a, b));
return;
}
} catch (IllegalStateException ignore) {
// 连接非法如重复连接等落入重建/关闭逻辑
}
} else {
shutdown = true; // 超出范围
}
} else {
shutdown = true; // 无主或主端不可用/不同维度
}
// 需要关闭连接
destroyConnection();
}
public void onUnloadOrRemove() {
destroyConnection();
}
private void destroyConnection() {
var current = connection.getConnection();
if (current != null) {
current.destroy();
connection.setConnection(null);
}
connection = new ConnectionWrapper(null);
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "extendedae_plus:block/wireless_transceiver" }
}
}

View File

@ -2,5 +2,7 @@
"gui.expatternprovider.toggle_slots": "切换槽位显示",
"gui.expatternprovider.hide_slots": "隐藏槽位",
"gui.expatternprovider.show_slots": "显示槽位",
"gui.expatternprovider.clear_selection": "取消选择"
}
"gui.expatternprovider.clear_selection": "取消选择",
"block.extendedae_plus.wireless_transceiver": "无线收发器",
"item.extendedae_plus.wireless_transceiver": "无线收发器"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "minecraft:block/iron_block"
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "extendedae_plus:block/wireless_transceiver"
}

View File

@ -0,0 +1,14 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "extendedae_plus:wireless_transceiver"
}
]
}
]
}