This commit is contained in:
1820982382
2026-08-22 16:59:38 +08:00
parent 0aa6db226e
commit c83e34c1d6
33 changed files with 1146 additions and 23 deletions
@@ -0,0 +1,262 @@
package thaumicenergistics.common.tiles;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.IEssentiaTransport;
import thaumcraft.common.essentia.EssentiaTransport;
import thaumicenergistics.init.ModBlockEntities;
import thaumicenergistics.init.ModBlocks;
/**
* 源质无线接收器:通过无线连接器与源质供应器建立连接后,
* 作为纯中继(无 AE 节点),把 TC 管道/炼金塔的取源质请求转发到连接的供应器。
*/
public class TileEssentiaProviderConnection extends BlockEntity implements IEssentiaTransport, EssentiaTransport {
/** 连接的源质供应器坐标(null = 未连接)。 */
private BlockPos linkedProviderPos;
/** 接收器每次输入/输出源质上限。 */
public static final int TRANSFER_LIMIT = 16;
private int linkCheckCounter = 0;
private int pullCounter = 0;
private static final int PULL_INTERVAL = 10;
/** 模拟管道的最大扫描距离(格):接收器可隔空拉取该距离内的源质源。 */
private static final int PIPE_SCAN_RANGE = 16;
/** 服务端 tick:周期校验连接 + 主动沿模拟管道拉取源质(蒸馏器/罐/管道不主动推,需接收器拉)。 */
public static void serverTick(net.minecraft.world.level.Level level, BlockPos pos, BlockState state, TileEssentiaProviderConnection be) {
if (level.isClientSide()) return;
if (be.linkedProviderPos != null && ++be.linkCheckCounter >= 40) {
be.linkCheckCounter = 0;
be.getLinkedProvider(); // 无效连接会被清除
}
if (++be.pullCounter >= PULL_INTERVAL) {
be.pullCounter = 0;
be.takeEssentiaFromNeighbors();
}
}
/** 模拟管道:沿 6 个方向扫描,最近的有效源质源(蒸馏器/罐/管道)隔空拉取,经连接供应器注入 AE 网络。 */
private void takeEssentiaFromNeighbors() {
if (level == null || level.isClientSide()) return;
TileEssentiaProvider provider = getLinkedProvider();
if (provider == null || !provider.thE_isActive()) return;
for (Direction side : Direction.values()) {
if (!canInputFrom(side)) continue;
// 沿方向行走:非传输方块 = 管道断;空管道 = 继续;有源质 = 拉取
for (int d = 1; d <= PIPE_SCAN_RANGE; d++) {
BlockPos sourcePos = worldPosition.relative(side, d);
if (!level.isLoaded(sourcePos)) break;
BlockEntity sourceTile = level.getBlockEntity(sourcePos);
if (sourceTile == this) break;
if (sourceTile instanceof TileEssentiaProvider) break; // 到供应器本体,防循环
if (!(sourceTile instanceof IEssentiaTransport source)) break; // 非传输方块 = 管道断
Direction sourceSide = side.getOpposite();
if (!source.canOutputTo(sourceSide)) break; // 不能向接收器方向输出 = 断
int srcAmount = source.getEssentiaAmount(sourceSide);
if (srcAmount <= 0) continue; // 空管道/空源 → 继续走
int dSuck = getSuctionAmount(side);
if (dSuck < source.getMinimumSuction() || dSuck < source.getSuctionAmount(sourceSide)) {
break;
}
Aspect sourceAspect = source.getEssentiaType(sourceSide);
if (sourceAspect == null) break;
// 直接拉取并注入(injectEssentia 返回 0 = 全部接受,无需模拟预检)
int want = Math.min(Math.max(srcAmount, 1), TRANSFER_LIMIT);
int taken = source.takeEssentia(sourceAspect, want, sourceSide);
if (taken > 0) {
provider.thE_inject(sourceAspect, taken, false);
}
break; // 拉到即停
}
}
}
public TileEssentiaProviderConnection(BlockPos pos, BlockState state) {
super(ModBlockEntities.ESSENTIA_PROVIDER_CONNECTION.get(), pos, state);
}
// ===== 连接管理 =====
public BlockPos getLinkedProviderPos() {
return linkedProviderPos;
}
public boolean isLinked() {
return linkedProviderPos != null && getLinkedProvider() != null;
}
/** 设置连接(无线连接器右键供应器时调用)。返回 null = 成功;否则返回失败原因文案。 */
public String setLinkedProvider(BlockPos providerPos) {
// 在供应器侧注册(校验数量/距离)
if (level != null && !level.isClientSide()) {
if (level.getBlockEntity(providerPos) instanceof TileEssentiaProvider provider) {
String error = provider.addLinkedReceiver(worldPosition);
if (error != null) {
return error; // 超限/超距,不建立连接
}
} else {
return "§c目标不是有效的源质供应器";
}
}
this.linkedProviderPos = providerPos;
setChanged();
updateConnectedState();
return null;
}
public void clearLink() {
// 从供应器侧注销
if (level != null && !level.isClientSide() && linkedProviderPos != null
&& level.getBlockEntity(linkedProviderPos) instanceof TileEssentiaProvider provider) {
provider.removeLinkedReceiver(worldPosition);
}
this.linkedProviderPos = null;
setChanged();
updateConnectedState();
}
private void updateConnectedState() {
if (level == null || level.isClientSide()) return;
boolean connected = linkedProviderPos != null;
BlockState st = getBlockState();
if (st.getValue(ModBlocks.CONNECTED) != connected) {
level.setBlock(worldPosition, st.setValue(ModBlocks.CONNECTED, connected), 3);
BlockEntity be = level.getBlockEntity(worldPosition);
if (be != null && be != this) {
be.setChanged();
}
}
}
/** 查找连接的供应器 tile。 */
public TileEssentiaProvider getLinkedProvider() {
if (level == null || level.isClientSide() || linkedProviderPos == null) return null;
if (level.getBlockEntity(linkedProviderPos) instanceof TileEssentiaProvider provider) {
// 自愈:供应器列表缺本接收器时补注册(修复旧存档/列表丢失导致的状态不一致)
if (!provider.getLinkedReceivers().contains(worldPosition)) {
provider.addLinkedReceiver(worldPosition);
}
return provider;
}
// 供应器被移除 → 清除连接
linkedProviderPos = null;
setChanged();
updateConnectedState();
return null;
}
// ===== IEssentiaTransport(转发到连接供应器) =====
@Override
public boolean isConnectable(Direction side) {
return true;
}
@Override
public boolean canInputFrom(Direction side) {
return true; // 支持输入源质到 AE 网络
}
@Override
public boolean canOutputTo(Direction side) {
return true;
}
@Override
public void setSuction(Aspect aspect, int amount) {
}
@Override
public Aspect getSuctionType(Direction side) {
return null;
}
@Override
public int getSuctionAmount(Direction side) {
// 与源质供应器一致:有吸力才能被蒸馏器等设施注入源质(输入功能)
return 8;
}
@Override
public int takeEssentia(Aspect aspect, int amount, Direction side) {
TileEssentiaProvider provider = getLinkedProvider();
if (provider == null || !provider.thE_isActive() || aspect == null || amount <= 0) return 0;
return (int) provider.thE_extract(aspect, Math.min(amount, TRANSFER_LIMIT), false);
}
@Override
public int addEssentia(Aspect aspect, int amount, Direction side) {
// 无线输入:转发到连接供应器注入 AE 网络,每次上限 16
TileEssentiaProvider provider = getLinkedProvider();
if (provider == null || !provider.thE_isActive() || aspect == null || amount <= 0) return 0;
return (int) provider.thE_inject(aspect, Math.min(amount, TRANSFER_LIMIT), false);
}
@Override
public int getEssentiaAmount(Direction side) {
TileEssentiaProvider provider = getLinkedProvider();
if (provider == null || !provider.thE_isActive()) return 0;
Aspect wanted = getNeighborWantedAspect(side);
if (wanted != null) {
return (int) Math.min(provider.thE_queryAmount(wanted), Integer.MAX_VALUE);
}
return 0;
}
@Override
public Aspect getEssentiaType(Direction side) {
TileEssentiaProvider provider = getLinkedProvider();
if (provider == null || !provider.thE_isActive()) return null;
Aspect wanted = getNeighborWantedAspect(side);
if (wanted != null && provider.thE_queryAmount(wanted) > 0) {
return wanted;
}
return provider.thE_firstAvailable();
}
@Override
public int getMinimumSuction() {
return 0;
}
@Override
public boolean renderExtendedTube() {
return false;
}
private Aspect getNeighborWantedAspect(Direction side) {
BlockPos neighborPos = worldPosition.relative(side);
if (level.getBlockEntity(neighborPos) instanceof IEssentiaTransport transport) {
return transport.getSuctionType(side.getOpposite());
}
return null;
}
// ===== NBT =====
@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
if (linkedProviderPos != null) {
tag.putLong("LinkedProvider", linkedProviderPos.asLong());
}
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
if (tag.contains("LinkedProvider")) {
linkedProviderPos = BlockPos.of(tag.getLong("LinkedProvider"));
} else {
linkedProviderPos = null;
}
}
}