Merge remote-tracking branch 'ae/develop/neoforge_iava' into 1.21.1

This commit is contained in:
GaLicn 2025-09-27 21:04:03 +08:00
commit f727b67ba6
13 changed files with 548 additions and 496 deletions

View File

@ -343,7 +343,10 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
return 0; return 0;
} }
// 不允许存储无限单元自身 // 不允许存储无限单元自身
if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { if (what instanceof AEItemKey itemKey &&
itemKey.getItem() instanceof InfinityBigIntegerCellItem &&
itemKey.get(DataComponents.CUSTOM_DATA) != null
) {
return 0; return 0;
} }

View File

@ -9,165 +9,167 @@ import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.util.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
import com.extendedae_plus.util.PowerUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils;
import it.unimi.dsi.fastutil.shorts.ShortSet; import it.unimi.dsi.fastutil.shorts.ShortSet;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.level.block.Block; import net.minecraft.world.inventory.Slot;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
// 实体加速器菜单负责与客户端界面同步数据 /**
* 实体加速器菜单负责管理客户端与服务端的数据同步处理加速卡能量卡和目标方块的状态
*/
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> { public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
@GuiSync(716) @GuiSync(716) public boolean accelerateEnabled = true; // 是否启用加速
public boolean accelerateEnabled = true; @GuiSync(717) public int entitySpeedCardCount; // 已安装的实体加速卡数量
// 已安装的实体加速卡数量用于能耗计算 @GuiSync(718) public int energyCardCount; // 已安装的能量卡数量
@GuiSync(717) @GuiSync(719) public int effectiveSpeed = 1; // 当前生效的加速倍率
public int entitySpeedCardCount; @GuiSync(720) public double multiplier = 1.0; // 目标方块的配置倍率
// 已安装的能量卡数量 @GuiSync(721) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中
@GuiSync(718) @GuiSync(722) public boolean networkEnergySufficient = true; // 网络能量是否充足
public int energyCardCount;
// 当前生效的配置倍率从配置中读取并同步
// 当前计算出的生效速度product of multipliers同步给客户端用于显示
@GuiSync(719)
public int effectiveSpeed = 1;
@GuiSync(720)
public double multiplier = 1.0;
@GuiSync(721)
public boolean targetBlacklisted = false;
// 来自部件的网络能量充足提示服务端设置客户端用于显示警告
@GuiSync(722) public boolean networkEnergySufficient = true;
/**
* 构造函数初始化菜单并绑定部件
* @param id 菜单ID
* @param ip 玩家背包
* @param host 关联的实体加速器部件
*/
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host);
if (host != null) {
host.menu = this; // 绑定菜单到部件
this.accelerateEnabled = host.getAccelerateEnabled(); // 同步初始开关状态
}
}
/**
* 获取加速开关状态
* @return 是否启用加速
*/
public boolean getAccelerateEnabled() { public boolean getAccelerateEnabled() {
return this.accelerateEnabled; return this.accelerateEnabled;
} }
/**
* 设置加速开关状态并同步到部件
* @param enabled 是否启用加速
*/
public void setAccelerateEnabled(boolean enabled) { public void setAccelerateEnabled(boolean enabled) {
this.accelerateEnabled = enabled; this.accelerateEnabled = enabled;
if (getHost() != null) {
getHost().setAccelerateEnabled(enabled); // 同步到部件
}
broadcastChanges(); // 广播状态变化
} }
/** /**
* Part 更新 networkEnergySufficient 的封装方法由服务器调用 * 更新网络能量充足状态并广播到客户端
* 该方法会更新 @GuiSync 字段并广播变化到客户端 * @param sufficient 是否能量充足
*/ */
public void setNetworkEnergySufficient(boolean sufficient) { public void setNetworkEnergySufficient(boolean sufficient) {
this.networkEnergySufficient = sufficient; this.networkEnergySufficient = sufficient;
// 触发一次数据广播使客户端立即接收到最新状态 broadcastChanges();
this.broadcastChanges();
}
// 构造方法初始化菜单并与部件绑定
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host);
// 让部件持有当前菜单实例便于通信
getHost().menu = this;
// 初始同步部件上的开关状态到菜单服务器端构造时保证一致
try {
this.accelerateEnabled = getHost().getAccelerateEnabled();
} catch (Exception ignored) {}
} }
/**
* 服务端数据同步到客户端时调用更新卡数量目标状态和生效速度
*/
@Override @Override
public void onServerDataSync(ShortSet updatedFields) { public void onServerDataSync(ShortSet updatedFields) {
super.onServerDataSync(updatedFields); super.onServerDataSync(updatedFields);
// 重新统计实体加速卡和能量卡数量 updateCardCounts(); // 更新卡数量
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); updateTargetStatus(); // 更新目标方块的黑名单和倍率
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); updateEffectiveSpeed(); // 计算生效速度
updateNetworkEnergyStatus(); // 同步能量状态
// 计算当前面向方块的倍率服务器端并同步给客户端
double mult = 1.0;
try {
BlockEntity target = getHost().getLevel().getBlockEntity(
getHost().getBlockEntity().getBlockPos().relative(getHost().getSide())
);
if (target != null) {
String blockId = BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString();
for (ConfigParsingUtils.MultiplierEntry me :
ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) {
if (me.pattern.matcher(blockId).matches()) {
mult = Math.max(mult, me.multiplier);
}
}
}
} catch (Exception ignored) {
}
this.multiplier = mult;
// 检查目标是否在黑名单中如果是则标记并将生效速度设为 0服务器端计算
boolean blacklisted = false;
try {
BlockEntity target = getHost().getLevel().getBlockEntity(
getHost().getBlockEntity().getBlockPos().relative(getHost().getSide())
);
if (target != null) {
Block block = target.getBlockState().getBlock();
String blockId = BuiltInRegistries.BLOCK.getKey(block).toString(); // 直接拿到 "minecraft:stone"
for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist(
ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) {
if (p.matcher(blockId).matches()) {
blacklisted = true;
break;
}
}
}
} catch (Exception ignored) {
}
this.targetBlacklisted = blacklisted;
// 计算生效速度如果被黑名单则为 0否则进行正常计算使用工具类从菜单直接计算 product with cap最多 8
if (this.targetBlacklisted) {
this.effectiveSpeed = 0;
} else {
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
}
// 从部件同步网络能量充足状态仅在服务器端从部件读取客户端应使用由 @GuiSync 同步过来的值
try {
EntitySpeedTickerPart host = getHost();
if (host != null && !isClientSide()) {
this.networkEnergySufficient = host.isNetworkEnergySufficient();
}
} catch (Exception ignored) {}
// 如果在客户端刷新界面
if (isClientSide()) { if (isClientSide()) {
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { refreshClientGui(); // 客户端刷新界面
screen.refreshGui();
}
} }
} }
// 当任意槽位发生变化时调用 /**
* 当槽位内容变化时调用客户端更新卡数量和生效速度
* @param slot 发生变化的槽位
*/
@Override @Override
public void onSlotChange(net.minecraft.world.inventory.Slot slot) { public void onSlotChange(Slot slot) {
super.onSlotChange(slot); super.onSlotChange(slot);
// 客户端重新统计卡数量并刷新界面
if (isClientSide()) { if (isClientSide()) {
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); updateCardCounts();
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); updateEffectiveSpeed();
// 立即在客户端计算生效速度以便界面即时反馈使用与服务端相同的工具方法最多 8 张卡 refreshClientGui();
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
screen.refreshGui();
}
} }
} }
/**
* 广播数据变化清理未启用槽位的显示堆栈
*/
@Override @Override
public void broadcastChanges() { public void broadcastChanges() {
// 遍历所有槽位清理未启用但有物品显示的 OptionalFakeSlot
for (Object o : this.slots) { for (Object o : this.slots) {
if (o instanceof OptionalFakeSlot fs) { if (o instanceof OptionalFakeSlot fs && !fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) {
if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) { fs.clearStack(); // 清理未启用槽位的显示
fs.clearStack();
}
} }
} }
// 调用标准的同步方法通知监听者数据已更新 standardDetectAndSendChanges();
this.standardDetectAndSendChanges(); }
/**
* 更新加速卡和能量卡的数量
*/
private void updateCardCounts() {
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
}
/**
* 更新目标方块的黑名单状态和倍率
*/
private void updateTargetStatus() {
BlockEntity target = getTargetBlockEntity();
if (target == null) {
this.multiplier = 1.0;
this.targetBlacklisted = false;
return;
}
String blockId = net.minecraft.core.registries.BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString();
this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get());
this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get());
}
/**
* 计算生效速度考虑黑名单和卡数量
*/
private void updateEffectiveSpeed() {
this.effectiveSpeed = targetBlacklisted ? 0 : (int) PowerUtils.computeProductWithCap(getUpgrades(), 8);
}
/**
* 同步网络能量状态仅服务端
*/
private void updateNetworkEnergyStatus() {
if (!isClientSide() && getHost() != null) {
this.networkEnergySufficient = getHost().isNetworkEnergySufficient();
}
}
/**
* 客户端刷新界面
*/
private void refreshClientGui() {
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
screen.refreshGui();
}
}
/**
* 获取目标方块实体
* @return 目标方块实体或 null
*/
private BlockEntity getTargetBlockEntity() {
return getHost() != null ?
getHost().getLevel().getBlockEntity(
getHost().getBlockEntity().getBlockPos().relative(getHost().getSide())
) : null;
} }
} }

View File

@ -4,12 +4,15 @@ import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier; import appeng.api.config.PowerMultiplier;
import appeng.api.networking.GridFlags; import appeng.api.networking.GridFlags;
import appeng.api.networking.IGridNode; import appeng.api.networking.IGridNode;
import appeng.api.networking.energy.IEnergyService;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest; import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPartCollisionHelper; import appeng.api.parts.IPartCollisionHelper;
import appeng.api.parts.IPartItem; import appeng.api.parts.IPartItem;
import appeng.api.parts.IPartModel; import appeng.api.parts.IPartModel;
import appeng.api.storage.MEStorage;
import appeng.api.upgrades.IUpgradeableObject; import appeng.api.upgrades.IUpgradeableObject;
import appeng.core.definitions.AEItems; import appeng.core.definitions.AEItems;
import appeng.items.parts.PartModels; import appeng.items.parts.PartModels;
@ -22,9 +25,8 @@ import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.util.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
import com.extendedae_plus.util.PowerUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -36,11 +38,11 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.neoforged.fml.ModList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.lang.reflect.Method;
import java.util.regex.Pattern;
/** /**
* EntitySpeedTickerPart 是一个可升级的 AE2 部件<p> * EntitySpeedTickerPart 是一个可升级的 AE2 部件<p>
@ -63,24 +65,22 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
} }
public EntitySpeedTickerMenu menu; // 当前打开的菜单实例
private boolean accelerateEnabled = true; // 是否启用加速
private boolean networkEnergySufficient = true; // 网络能量是否充足
/** /**
* 构造函数初始化部件并设置网络节点属性 * 构造函数初始化部件并设置网络节点属性
* @param partItem 部件物品 * @param partItem 部件物品
*/ */
public EntitySpeedTickerPart(IPartItem<?> partItem) { public EntitySpeedTickerPart(IPartItem<?> partItem) {
super(partItem); super(partItem);
// 设置网络节点属性需要通道空闲功耗为1并注册为 IGridTickable 服务
this.getMainNode() this.getMainNode()
.setFlags(GridFlags.REQUIRE_CHANNEL) .setFlags(GridFlags.REQUIRE_CHANNEL)
.setIdlePowerUsage(1) .setIdlePowerUsage(1)
.addService(IGridTickable.class, this); .addService(IGridTickable.class, this);
} }
// 当前打开的菜单实例如果有
public EntitySpeedTickerMenu menu;
// 控制是否启用加速默认启用
private boolean accelerateEnabled = true;
// 标记网络中能量是否充足用于 GUI 提示默认充足
private boolean networkEnergySufficient = true;
public boolean getAccelerateEnabled() { public boolean getAccelerateEnabled() {
return this.accelerateEnabled; return this.accelerateEnabled;
@ -90,29 +90,33 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
return this.networkEnergySufficient; return this.networkEnergySufficient;
} }
public void setAccelerateEnabled(boolean accelerateEnabled) {
this.accelerateEnabled = accelerateEnabled;
}
/** /**
* 更新网络能量充足标记并在菜单存在且状态变化时触发同步 * 设置加速开关状态并通知菜单
* @param sufficient 是否能量充足 * @param enabled 是否启用加速
*/ */
private void updateNetworkEnergySufficient(boolean sufficient) { public void setAccelerateEnabled(boolean enabled) {
// 保持部件内部状态一致部件为权威来源 this.accelerateEnabled = enabled;
this.networkEnergySufficient = sufficient; if (menu != null) {
if (this.menu != null) { menu.setAccelerateEnabled(enabled);
try {
// 使用菜单的封装方法更新并广播以保持封装性
this.menu.setNetworkEnergySufficient(sufficient);
} catch (Exception ignored) {}
} }
} }
/** /**
* 获取当前状态下的静态模型用于渲染 * 更新网络能量充足状态并通知菜单
* @param sufficient 是否能量充足
*/
private void updateNetworkEnergySufficient(boolean sufficient) {
this.networkEnergySufficient = sufficient;
if (menu != null) {
menu.setNetworkEnergySufficient(sufficient);
}
}
/**
* 获取当前状态的渲染模型
* @return 当前状态的模型 * @return 当前状态的模型
*/ */
@Override
public IPartModel getStaticModels() { public IPartModel getStaticModels() {
if (this.isActive() && this.isPowered()) { if (this.isActive() && this.isPowered()) {
return MODELS_HAS_CHANNEL; return MODELS_HAS_CHANNEL;
@ -196,87 +200,149 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
} }
/** /**
* 以指定速度对目标方块实体进行 tick 操作 * 对目标方块实体执行加速 tick 操作
* * @param blockEntity 目标方块实体
* @param blockEntity 需要被 tick 的方块实体 * @param <T> 方块实体类型
* @param <T> 方块实体类型
*/ */
private <T extends BlockEntity> void ticker(@NotNull T blockEntity) { private <T extends BlockEntity> void ticker(@NotNull T blockEntity) {
if (this.getGridNode() == null if (!isValidForTicking()) {
|| this.getMainNode() == null
|| this.getMainNode().getGrid() == null) {
return; return;
} }
// 获取方块实体的位置
BlockPos pos = blockEntity.getBlockPos();
if (blockEntity.getLevel() == null) return;
// 检查黑名单支持通配符/正则
String blockId = BuiltInRegistries.BLOCK.getKey(blockEntity.getBlockState().getBlock()).toString(); String blockId = BuiltInRegistries.BLOCK.getKey(blockEntity.getBlockState().getBlock()).toString();
if (ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) {
// 使用工具类的缓存接口工具类内部负责懒加载/线程安全 return;
List<Pattern> compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.ENTITY_TICKER_BLACK_LIST.get());
for (Pattern p : compiledBlacklist) {
if (p.matcher(blockId).matches()) return;
} }
// 获取该方块实体的 Ticker BlockEntityTicker<T> ticker = getTicker(blockEntity);
@SuppressWarnings("unchecked") if (ticker == null) {
BlockEntityTicker<T> blockEntityTicker = this.getLevel() return;
.getBlockState(pos) }
.getTicker(this.getLevel(), (BlockEntityType<T>) blockEntity.getType());
if (blockEntityTicker == null) return;
// 使用集中定义的 CardDef 列表支持以后添加等级或改倍率而无需修改此逻辑 int speed = calculateSpeed();
int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); if (speed <= 0) {
return;
}
// 使用已注册的单一 Item 计算已安装卡数量总计用于能耗计算 double requiredPower = calculateRequiredPower(speed, blockId);
if (!extractPower(requiredPower)) {
return;
}
performTicks(blockEntity, ticker, speed);
}
/**
* 检查网络节点是否有效
* @return 是否可以执行 tick
*/
private boolean isValidForTicking() {
return getGridNode() != null && getMainNode() != null && getMainNode().getGrid() != null;
}
/**
* 获取目标方块实体的 ticker
* @param blockEntity 目标方块实体
* @return ticker null
*/
private <T extends BlockEntity> BlockEntityTicker<T> getTicker(T blockEntity) {
return getLevel().getBlockState(blockEntity.getBlockPos())
.getTicker(getLevel(), (BlockEntityType<T>) blockEntity.getType());
}
/**
* 计算加速倍率
* @return 生效的加速倍率
*/
private int calculateSpeed() {
int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
if (entitySpeedCardCount <= 0) return 0;
return (int) PowerUtils.computeProductWithCap(getUpgrades(), 8);
}
// 使用工具方法从槽位直接计算乘积并应用 cap最多 8 张卡 /**
long product = PowerUtils.computeProductWithCapFromStacks(this.getUpgrades(), 8); * 计算所需能量
* @param speed 加速倍率
* @param blockId 目标方块ID
* @return 所需能量
*/
private double calculateRequiredPower(int speed, String blockId) {
int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
double multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get());
return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier;
}
// 如果没有任何实体加速卡则不进行加速且不消耗额外能量只保留部件的被动功耗 /**
if (entitySpeedCardCount <= 0) return; * 提取网络能量并更新状态优先从 AE2 网络提取 AE 能量不足时从磁盘提取 FE 能量
* @param requiredPower 所需能量AE 单位
* @return 是否成功提取足够能量
*/
private boolean extractPower(double requiredPower) {
IEnergyService energyService = getMainNode().getGrid().getEnergyService();
MEStorage storage = getMainNode().getGrid().getStorageService().getInventory();
IActionSource source = IActionSource.ofMachine(this);
boolean appFluxLoaded = ModList.get().isLoaded("appflux");
boolean preferDiskEnergy = appFluxLoaded && ModConfigs.PRIORITIZE_DISK_ENERGY.get();
// 计算本次 tick 所需能量使用工具类根据 product 计算最终能耗 // 如果 appflux 存在且优先磁盘能量尝试提取 FE 能量
double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount); if (appFluxLoaded && preferDiskEnergy) {
if (tryExtractFE(energyService, storage, requiredPower, source)) {
int speed = (int) product; return true;
double multiplier = 1.0;
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) {
if (me.pattern.matcher(blockId).matches()) {
multiplier = Math.max(multiplier, me.multiplier);
} }
} }
requiredPower *= multiplier; // 尝试提取 AE 能量 appflux 不存在优先 AE 能量或 FE 提取失败时
double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
// 先模拟提取以检查网络中是否有足够能量再真正抽取 if (simulated >= requiredPower) {
double simulated = getMainNode().getGrid().getEnergyService() double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); boolean sufficient = extracted >= requiredPower;
if (simulated < requiredPower) { updateNetworkEnergySufficient(sufficient);
updateNetworkEnergySufficient(false); // 能量不足 return sufficient;
return;
} }
updateNetworkEnergySufficient(false);
double extractedPower = getMainNode().getGrid().getEnergyService() // 如果 appflux 存在且优先 AE 能量尝试提取 FE 能量作为备用
.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); if (appFluxLoaded && !preferDiskEnergy) {
if (extractedPower < requiredPower) { return tryExtractFE(energyService, storage, requiredPower, source);
updateNetworkEnergySufficient(false); // 能量不足
return;
} }
updateNetworkEnergySufficient(true); // 能量充足 return false;
}
// 计算加速倍数基于 2 的次方并把 8 张映射到最大 1024x2^10 private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) {
// 已由 product 计算得到 speed上面已在没有卡时提前返回 try {
Class<?> helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper");
Method extractMethod = helperClass.getMethod(
"extractFE",
IEnergyService.class,
MEStorage.class,
long.class,
IActionSource.class
);
long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE
long feExtracted = (long) extractMethod.invoke(null, energyService, storage, feRequired, source);
if (feExtracted >= feRequired) {
updateNetworkEnergySufficient(true);
return true;
}
} catch (Exception e) {
// 如果反射失败视为 FE 不可用
}
updateNetworkEnergySufficient(false);
return false;
}
// 执行 tick 操作 /**
* 执行加速 tick 操作
* @param blockEntity 目标方块实体
* @param ticker 方块实体 ticker
* @param speed 加速倍率
*/
private <T extends BlockEntity> void performTicks(T blockEntity,
BlockEntityTicker<T> ticker,
int speed) {
// 执行 speed-1 次额外 tick原生 tick 已包含 1
for (int i = 0; i < speed - 1; i++) { for (int i = 0; i < speed - 1; i++) {
blockEntityTicker.tick( ticker.tick(
blockEntity.getLevel(), blockEntity.getLevel(),
blockEntity.getBlockPos(), blockEntity.getBlockPos(),
blockEntity.getBlockState(), blockEntity.getBlockState(),

View File

@ -10,28 +10,32 @@ import appeng.client.gui.widgets.SettingToggleButton;
import appeng.util.Platform; import appeng.util.Platform;
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
import com.extendedae_plus.network.ToggleEntityTickerC2SPacket; import com.extendedae_plus.network.ToggleEntityTickerC2SPacket;
import com.extendedae_plus.util.PowerUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> { public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
private boolean eap$entitySpeedTickerEnabled = false; private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
private SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; private final SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; // 加速开关按钮
public EntitySpeedTickerScreen( /**
EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { * 构造函数初始化界面和控件
* @param menu 实体加速器菜单
* @param playerInventory 玩家背包
* @param title 界面标题
* @param style 界面样式
*/
public EntitySpeedTickerScreen(EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
super((C) menu, playerInventory, title, style); super((C) menu, playerInventory, title, style);
this.addToLeftToolbar(CommonButtons.togglePowerUnit()); this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮
this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
try{ // 初始化加速开关按钮
this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
}catch (Exception ignored){}
// 使用 SettingToggleButton<YesNo> 的外观原版图标但自定义悬停描述为智能阻挡
// 不做本地切换点击仅发送自定义C2S显示由@GuiSync回传
eap$entitySpeedTickerToggle = new SettingToggleButton<>( eap$entitySpeedTickerToggle = new SettingToggleButton<>(
Settings.BLOCKING_MODE, Settings.BLOCKING_MODE,
this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO, this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO,
@ -43,65 +47,38 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
) { ) {
@Override @Override
public List<Component> getTooltipMessage() { public List<Component> getTooltipMessage() {
// 如果目标在黑名单中直接显示已禁用的提示 if (menu.targetBlacklisted) {
try { return List.of(
if (menu != null && menu.targetBlacklisted) { Component.literal("实体加速"),
var title = Component.literal("实体加速"); Component.literal("已禁用(目标在黑名单)")
var stateLine = Component.literal("已禁用(目标在黑名单)"); );
return List.of(title, stateLine); }
}
} catch (Exception ignored) {}
boolean enabled = eap$entitySpeedTickerEnabled; boolean enabled = eap$entitySpeedTickerEnabled;
var title = Component.literal("实体加速"); return List.of(
var stateLine = enabled Component.literal("实体加速"),
? Component.literal("已启用: 将加速目标方块实体的tick") enabled ? Component.literal("已启用: 将加速目标方块实体的tick") :
: Component.literal("已关闭: 不会对目标方块实体进行加速"); Component.literal("已关闭: 不会对目标方块实体进行加速")
return List.of(title, stateLine); );
} }
@Override @Override
protected Icon getIcon() { protected Icon getIcon() {
try { if (menu.targetBlacklisted) return Icon.INVALID;
if (menu != null && menu.targetBlacklisted) { return this.getCurrentValue() == YesNo.YES ? Icon.VALID : Icon.INVALID;
// 黑名单时显示禁用图标
return Icon.INVALID;
}
} catch (Exception ignored) {}
// 根据当前值显示不同图标可按需替换 Icon 常量
if (this.getCurrentValue() == YesNo.YES) {
return Icon.VALID;
} else {
return Icon.INVALID;
}
} }
}; };
// 初始化后立刻对齐当前@GuiSync状态避免首帧显示不一致
eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO); eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO);
this.addToLeftToolbar(eap$entitySpeedTickerToggle); this.addToLeftToolbar(eap$entitySpeedTickerToggle);
} }
@Override @Override
protected void updateBeforeRender() { protected void updateBeforeRender() {
super.updateBeforeRender(); super.updateBeforeRender();
if (eap$entitySpeedTickerToggle != null && menu != null) {
if (this.eap$entitySpeedTickerToggle != null) { eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
boolean desired = this.eap$entitySpeedTickerEnabled; // 如果目标在黑名单禁用按钮并显示关闭状态
if (this.menu != null) { eap$entitySpeedTickerToggle.set(menu.targetBlacklisted ? YesNo.NO : (eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO));
desired = this.menu.getAccelerateEnabled(); eap$entitySpeedTickerToggle.active = !menu.targetBlacklisted;
}
this.eap$entitySpeedTickerEnabled = desired;
// 如果目标在黑名单中禁用切换并强制显示为关闭
if (this.menu != null && this.menu.targetBlacklisted) {
this.eap$entitySpeedTickerToggle.set(YesNo.NO);
this.eap$entitySpeedTickerToggle.active = false;
} else {
this.eap$entitySpeedTickerToggle.set(desired ? YesNo.YES : YesNo.NO);
this.eap$entitySpeedTickerToggle.active = true;
}
} }
textData(); textData();
} }
@ -110,34 +87,33 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
textData(); textData();
} }
/**
* 更新界面文本内容包括加速状态速度能耗和倍率
*/
private void textData() { private void textData() {
// 如果目标被黑名单禁止则显示禁用状态并把数值显示为 0 Map<String, Component> textContents = new HashMap<>();
if (getMenu().targetBlacklisted) { if (getMenu().targetBlacklisted) {
setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable")); // 黑名单禁用时的默认显示
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0)); textContents.put("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable"));
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false))); textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0));
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false)));
setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0)));
return; textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0)));
} else {
// 正常状态下显示实际数据
int energyCardCount = getMenu().energyCardCount;
double multiplier = getMenu().multiplier;
int effectiveSpeed = getMenu().effectiveSpeed;
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount);
textContents.put("enable", getMenu().networkEnergySufficient ? null :
Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed));
textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier)));
} }
textContents.forEach(this::setTextContent);
int energyCardCount = getMenu().energyCardCount;
double multiplier = getMenu().multiplier;
int effectiveSpeed = getMenu().effectiveSpeed;
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount);
// 如果网络能量不足优先显示警告信息并在能量值处显示 0
if (!getMenu().networkEnergySufficient) {
setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
}else {
setTextContent("enable", null);
}
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed));
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier)));
} }
} }

View File

@ -13,6 +13,7 @@ public final class ModConfigs {
public static final ModConfigSpec CLIENT_SPEC; public static final ModConfigSpec CLIENT_SPEC;
public static final ModConfigSpec.BooleanValue SHOW_ENCODER_PATTERN_PLAYER; public static final ModConfigSpec.BooleanValue SHOW_ENCODER_PATTERN_PLAYER;
public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT;
public static final ModConfigSpec.BooleanValue PRIORITIZE_DISK_ENERGY;
// Server 配置 // Server 配置
public static final ModConfigSpec SERVER_SPEC; public static final ModConfigSpec SERVER_SPEC;
@ -124,6 +125,12 @@ public final class ModConfigs {
() -> "", () -> "",
obj -> obj instanceof String obj -> obj instanceof String
); );
PRIORITIZE_DISK_ENERGY = serverBuilder
.comment(
"是否优先从磁盘提取FE能量仅当Applied Flux模组存在时生效",
"开启后将优先尝试从磁盘提取FE能量反之优先消耗AE网络中的能量"
)
.define("prioritizeDiskEnergy", true);
serverBuilder.pop(); serverBuilder.pop();
SERVER_SPEC = serverBuilder.build(); SERVER_SPEC = serverBuilder.build();
} }

View File

@ -20,11 +20,4 @@ public class CraftingTreeNodeMixin {
// push the requestedAmount before addContainerItems is called // push the requestedAmount before addContainerItems is called
RequestedAmountHolder.push(requestedAmount); RequestedAmountHolder.push(requestedAmount);
} }
@Inject(method = "request(Lappeng/crafting/inv/CraftingSimulationState;JLappeng/api/stacks/KeyCounter;)V",
at = @At(value = "RETURN"))
private void clearRequestedAmountOnReturn(CraftingSimulationState inv, long requestedAmount, KeyCounter containerItems, CallbackInfo ci) {
// pop the pushed requested amount on return
RequestedAmountHolder.pop();
}
} }

View File

@ -55,6 +55,7 @@ public abstract class CraftingTreeProcessMixin {
CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode;
AEKey parentTarget = parentAcc.eap$getWhat(); AEKey parentTarget = parentAcc.eap$getWhat();
long requested = RequestedAmountHolder.get(); long requested = RequestedAmountHolder.get();
RequestedAmountHolder.pop();
// 根据配置决定是否在 provider 间轮询分配请求量默认开启 // 根据配置决定是否在 provider 间轮询分配请求量默认开启
long perProvider = 1L; long perProvider = 1L;

View File

@ -0,0 +1,41 @@
package com.extendedae_plus.util;
import appeng.api.config.Actionable;
import appeng.api.networking.energy.IEnergyService;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.MEStorage;
import appeng.api.storage.StorageHelper;
import com.glodblock.github.appflux.common.me.key.FluxKey;
import com.glodblock.github.appflux.common.me.key.type.EnergyType;
public class FluxEnergyHelper {
/**
* 尝试从 ME 存储提取 FE 能量
* @param energyService AE2 能量服务
* @param storage ME 存储
* @param feRequired 所需 FE
* @param source 操作来源
* @return 提取的 FE
*/
public static long extractFE(
IEnergyService energyService,
MEStorage storage,
long feRequired,
IActionSource source
) {
FluxKey feKey = FluxKey.of(EnergyType.FE);
// 模拟提取 FE
long feExtracted = StorageHelper.poweredExtraction(
energyService, storage, feKey, feRequired, source, Actionable.SIMULATE
);
// 执行实际提取
if (feExtracted >= feRequired) {
return StorageHelper.poweredExtraction(
energyService, storage, feKey, feRequired, source, Actionable.MODULATE
);
}
return 0;
}
}

View File

@ -1,162 +0,0 @@
package com.extendedae_plus.util;
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
import com.extendedae_plus.config.ModConfigs;
/**
* 用于计算实体加速器的能耗与加速倍率的工具类
*/
public final class PowerUtils {
private PowerUtils() {}
// ---- 重构后的 API ----
/**
* card multipliers按插槽序计算乘积并应用 cap 规则 capForHighestMultiplier
* @param multipliers iterable of per-card multipliers
* @param maxCards 最多计入的卡数
* @return cap 约束后的乘积
*/
public static long computeProductWithCap(Iterable<Integer> multipliers, int maxCards) {
long product = 1L;
int considered = 0;
int highest = 1;
for (Integer m : multipliers) {
if (m == null) continue;
if (considered >= maxCards) break;
int mult = m.intValue();
if (mult <= 0) mult = 1;
product *= mult;
highest = Math.max(highest, mult);
considered++;
}
long cap = capForHighestMultiplier(highest);
return Math.min(product, cap);
}
/**
* 根据最高单卡 multiplier 返回 cap
*/
public static long capForHighestMultiplier(int highestMultiplier) {
if (highestMultiplier >= 16) return 1024L;
if (highestMultiplier >= 8) return 256L;
if (highestMultiplier >= 4) return 64L;
if (highestMultiplier >= 2) return 8L;
return 1L;
}
/**
* 从菜单对象读取前 maxCards 个加速卡的 multiplier 并计算 product with cap
*/
public static long computeProductWithCapFromMenu(appeng.menu.implementations.UpgradeableMenu<?> menu, int maxCards) {
java.util.List<Integer> list = new java.util.ArrayList<>();
int considered = 0;
for (var stack : menu.getUpgrades()) {
if (considered >= maxCards) break;
if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) {
int multVal = EntitySpeedCardItem.readMultiplier(stack);
int count = Math.min(stack.getCount(), maxCards - considered);
for (int i = 0; i < count; i++) {
list.add(multVal);
considered++;
if (considered >= maxCards) break;
}
}
}
return computeProductWithCap(list, maxCards);
}
/**
* 从一组 ItemStack升级槽直接计算 product with cap最多 maxCards
*/
public static long computeProductWithCapFromStacks(Iterable<net.minecraft.world.item.ItemStack> stacks, int maxCards) {
java.util.List<Integer> list = new java.util.ArrayList<>();
int considered = 0;
for (var stack : stacks) {
if (considered >= maxCards) break;
if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) {
int multVal = EntitySpeedCardItem.readMultiplier(stack);
int count = Math.min(stack.getCount(), maxCards - considered);
for (int i = 0; i < count; i++) {
list.add(multVal);
considered++;
if (considered >= maxCards) break;
}
}
}
return computeProductWithCap(list, maxCards);
}
/**
* 计算最终消耗 product 转换为等效卡数log2并调用 getFinalPower
*/
public static double computeFinalPowerForProduct(long product, int energyCardCount) {
if (product <= 1L) return 0.0;
double base = ModConfigs.ENTITY_TICKER_COST.getAsInt();
// 计算以2为底的对数用于分档与公式
double log2 = Math.log(product) / Math.log(2.0);
// 分档product==2 为一档4..256 为中档512..1024 为高档
double raw;
if (product == 2L) {
// 轻量档线性小幅增长
raw = base * 4;
} else if (product <= 256L) {
// 中档增长放缓使用 1.5 * log2
raw = base * Math.pow(2.0, 1.5 * log2) * 2;
} else {
// 高档增长较快使用 2.5 * log2
raw = base * Math.pow(2.0, 2.5 * log2);
}
double reduction = getReductionPercent(energyCardCount);
return raw * (1.0 - reduction) / 8.0;
}
/* ----------------- legacy helpers (restored) ----------------- */
public static double getGrowthFactor(int speedCardCount) {
if (speedCardCount <= 0) return 1.0;
if (speedCardCount == 1) return 2.0;
if (speedCardCount <= 6) return Math.pow(2.0, 2.0 * speedCardCount);
return Math.pow(2.0, 3.0 * speedCardCount);
}
public static double getRawPower(int speedCardCount) {
double base = ModConfigs.ENTITY_TICKER_COST.getAsInt();
return base * getGrowthFactor(speedCardCount);
}
public static double getReductionPercent(int energyCardCount) {
if (energyCardCount <= 0) return 0.0;
if (energyCardCount == 1) return 0.1;
if (energyCardCount >= 8) return 0.5;
return 0.5 * (1.0 - Math.pow(0.7, energyCardCount));
}
public static double getFinalPower(int speedCardCount, int energyCardCount) {
double raw = getRawPower(speedCardCount);
double reduction = getReductionPercent(energyCardCount);
return raw * (1.0 - reduction) / 4.0;
}
/**
* 返回能源卡减免后剩余的功耗比率例如 1 张能源卡 -> 0.9
* @param energyCardCount 能源卡数量
* @return 剩余功耗比率
*/
public static double getRemainingRatio(int energyCardCount) {
return 1.0 - getReductionPercent(energyCardCount);
}
/**
* 将剩余功耗比率格式化为百分比字符串例如 0.9 -> "90%"
*/
public static String formatPercentage(double ratio) {
double pct = ratio * 100.0;
// 如果为整数则无小数
if (Math.abs(pct - Math.round(pct)) < 1e-9) {
return String.format("%d%%", Math.round(pct));
}
return String.format("%.2f%%", pct);
}
}

View File

@ -1,10 +1,13 @@
package com.extendedae_plus.util; package com.extendedae_plus.util.entitySpeed;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
/** /**
* 配置解析工具类用于解析黑名单与倍率配置的字符串 * 配置解析工具类用于解析黑名单与倍率配置的字符串
*/ */
@ -23,35 +26,32 @@ public final class ConfigParsingUtils {
/** /**
* 编译用户提供的匹配串支持简单的 glob 语法'*' '?'以及完整的正则表达式 * 编译用户提供的匹配串支持简单的 glob 语法'*' '?'以及完整的正则表达式
* 规则
* - 如果字符串包含 ".*" 或其他正则元字符优先尝试按正则编译若失败则回退到 glob 转换
* - 否则若包含 '*' '?'将按 glob 语法转换为正则
* - 否则按字面量匹配处理
*/ */
public static Pattern compilePattern(String raw) { public static Pattern compilePattern(String raw) {
if (raw == null) throw new IllegalArgumentException("pattern is null"); if (raw == null || raw.trim().isEmpty()) {
LOGGER.warn("Invalid pattern: {}", raw);
throw new IllegalArgumentException("Pattern is null or empty");
}
raw = raw.trim(); raw = raw.trim();
if (raw.isEmpty()) throw new IllegalArgumentException("pattern is empty");
// If it looks like regex (contains '.*' or regex metachar), try regex first // Try regex first if it contains regex metacharacters
if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) { if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) {
try { try {
return Pattern.compile("^" + raw + "$"); return Pattern.compile("^" + raw + "$");
} catch (PatternSyntaxException ignored) { } catch (PatternSyntaxException e) {
// fallback to glob below LOGGER.warn("Failed to compile regex pattern '{}': {}", raw, e.getMessage());
// Fallback to glob
} }
} }
// If contains glob chars, convert to regex // Convert glob to regex
if (raw.contains("*") || raw.contains("?")) { if (raw.contains("*") || raw.contains("?")) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder("^");
sb.append('^');
for (char c : raw.toCharArray()) { for (char c : raw.toCharArray()) {
switch (c) { switch (c) {
case '*': sb.append(".*"); break; case '*': sb.append(".*"); break;
case '?': sb.append('.'); break; case '?': sb.append('.'); break;
default: default:
// escape regex special chars
if (".\\+[]{}()^$|".indexOf(c) >= 0) { if (".\\+[]{}()^$|".indexOf(c) >= 0) {
sb.append('\\'); sb.append('\\');
} }
@ -62,41 +62,71 @@ public final class ConfigParsingUtils {
return Pattern.compile(sb.toString()); return Pattern.compile(sb.toString());
} }
// Otherwise treat as a literal (match exact) // Literal match
return Pattern.compile("^" + Pattern.quote(raw) + "$"); return Pattern.compile("^" + Pattern.quote(raw) + "$");
} }
/** /**
* Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects. * 解析倍率条目 'modid:block 2x'
* Accepts values with optional trailing 'x' (case-insensitive).
*/ */
public static MultiplierEntry parseMultiplierEntry(String entry) { public static MultiplierEntry parseMultiplierEntry(String entry) {
if (entry == null) return null; if (entry == null || entry.trim().isEmpty()) return null;
String[] parts = entry.trim().split("\\s+"); String[] parts = entry.trim().split("\\s+");
if (parts.length < 2) return null; if (parts.length < 2) {
LOGGER.warn("Invalid multiplier entry: {}", entry);
return null;
}
String key = parts[0]; String key = parts[0];
String val = parts[1].toLowerCase(); String val = parts[1].toLowerCase();
if (val.endsWith("x")) val = val.substring(0, val.length() - 1); if (val.endsWith("x")) val = val.substring(0, val.length() - 1);
double m; double multiplier;
try { try {
m = Double.parseDouble(val); multiplier = Double.parseDouble(val);
} catch (NumberFormatException ex) { } catch (NumberFormatException e) {
LOGGER.warn("Invalid multiplier value in '{}': {}", entry, val);
return null; return null;
} }
try { try {
Pattern p = compilePattern(key); Pattern pattern = compilePattern(key);
return new MultiplierEntry(p, m); return new MultiplierEntry(pattern, multiplier);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
LOGGER.warn("Failed to compile pattern in '{}': {}", entry, e.getMessage());
return null; return null;
} }
} }
/**
* 检查方块是否在黑名单中
*/
public static boolean isBlockBlacklisted(String blockId, List<? extends String> blacklist) {
if (blockId == null) return false;
return getCachedBlacklist(blacklist).stream().anyMatch(p -> p.matcher(blockId).matches());
}
/**
* 获取方块的倍率
*/
public static double getMultiplierForBlock(String blockId, List<? extends String> multipliers) {
if (blockId == null) return 1.0;
double maxMultiplier = 1.0;
for (MultiplierEntry me : getCachedMultiplierEntries(multipliers)) {
if (me.pattern.matcher(blockId).matches()) {
maxMultiplier = Math.max(maxMultiplier, me.multiplier);
}
}
return maxMultiplier;
}
public static List<Pattern> compilePatterns(List<? extends String> raw) { public static List<Pattern> compilePatterns(List<? extends String> raw) {
List<Pattern> out = new ArrayList<>(); List<Pattern> out = new ArrayList<>();
if (raw == null) return out; if (raw == null) return out;
for (String s : raw) { for (String s : raw) {
if (s == null || s.isBlank()) continue; if (s == null || s.trim().isEmpty()) continue;
try { out.add(compilePattern(s)); } catch (IllegalArgumentException ignored) {} try {
out.add(compilePattern(s));
} catch (IllegalArgumentException e) {
LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage());
}
} }
return out; return out;
} }
@ -121,14 +151,11 @@ public final class ConfigParsingUtils {
/** /**
* 获取已解析并缓存的黑名单线程安全懒加载 * 获取已解析并缓存的黑名单线程安全懒加载
*/ */
public static List<Pattern> getCachedBlacklist(java.util.List<? extends String> source) { public static List<Pattern> getCachedBlacklist(List<? extends String> source) {
List<String> normalized = normalizeSource(source); List<String> normalized = normalizeSource(source);
// fast path: identical snapshot reference or equal contents
if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) { if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) {
return Collections.unmodifiableList(cachedBlacklist); return Collections.unmodifiableList(cachedBlacklist);
} }
synchronized (CACHE_LOCK) { synchronized (CACHE_LOCK) {
if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) { if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) {
cachedBlacklist = compilePatterns(normalized); cachedBlacklist = compilePatterns(normalized);
@ -141,13 +168,11 @@ public final class ConfigParsingUtils {
/** /**
* 获取已解析并缓存的倍率列表线程安全懒加载 * 获取已解析并缓存的倍率列表线程安全懒加载
*/ */
public static List<MultiplierEntry> getCachedMultiplierEntries(java.util.List<? extends String> source) { public static List<MultiplierEntry> getCachedMultiplierEntries(List<? extends String> source) {
List<String> normalized = normalizeSource(source); List<String> normalized = normalizeSource(source);
if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) { if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) {
return Collections.unmodifiableList(cachedMultiplierEntries); return Collections.unmodifiableList(cachedMultiplierEntries);
} }
synchronized (CACHE_LOCK) { synchronized (CACHE_LOCK) {
if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) { if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) {
cachedMultiplierEntries = parseMultiplierList(normalized); cachedMultiplierEntries = parseMultiplierList(normalized);
@ -158,7 +183,7 @@ public final class ConfigParsingUtils {
} }
// Normalize the incoming source list: trim entries, drop blanks, keep stable ordering // Normalize the incoming source list: trim entries, drop blanks, keep stable ordering
private static List<String> normalizeSource(java.util.List<? extends String> source) { private static List<String> normalizeSource(List<? extends String> source) {
List<String> out = new ArrayList<>(); List<String> out = new ArrayList<>();
if (source == null) return out; if (source == null) return out;
for (String s : source) { for (String s : source) {
@ -188,8 +213,8 @@ public final class ConfigParsingUtils {
synchronized (CACHE_LOCK) { synchronized (CACHE_LOCK) {
cachedBlacklist = null; cachedBlacklist = null;
cachedMultiplierEntries = null; cachedMultiplierEntries = null;
cachedBlacklistSourceSnapshot = null;
cachedMultiplierSourceSnapshot = null;
} }
} }
} }

View File

@ -0,0 +1,98 @@
package com.extendedae_plus.util.entitySpeed;
import appeng.api.upgrades.IUpgradeInventory;
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
import com.extendedae_plus.config.ModConfigs;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.List;
/**
* 用于计算实体加速器的能耗与加速倍率的工具类
*/
public final class PowerUtils {
private PowerUtils() {}
/**
* 计算加速卡的乘积并应用上限
* @param upgrades 升级槽位
* @param maxCards 最大计入的卡数
* @return 被上限约束后的乘积
*/
public static long computeProductWithCap(IUpgradeInventory upgrades, int maxCards) {
List<Integer> multipliers = new ArrayList<>();
int considered = 0;
for (ItemStack stack : upgrades) {
if (considered >= maxCards) break;
if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) {
int multVal = EntitySpeedCardItem.readMultiplier(stack);
int count = Math.min(stack.getCount(), maxCards - considered);
for (int i = 0; i < count; i++) {
multipliers.add(multVal);
considered++;
}
}
}
long product = 1L;
int highest = 1;
for (Integer m : multipliers) {
if (m == null || m <= 0) continue;
product *= m;
highest = Math.max(highest, m);
}
return Math.min(product, capForHighestMultiplier(highest));
}
/**
* 根据最高单卡倍率返回上限值
*/
public static long capForHighestMultiplier(int highestMultiplier) {
if (highestMultiplier >= 16) return 1024L;
if (highestMultiplier >= 8) return 256L;
if (highestMultiplier >= 4) return 64L;
if (highestMultiplier >= 2) return 8L;
return 1L;
}
/**
* 计算最终能耗
* @param product 加速卡乘积
* @param energyCardCount 能量卡数量
* @return 最终能耗值
*/
public static double computeFinalPowerForProduct(long product, int energyCardCount) {
if (product <= 1L) return 0.0;
double base = ModConfigs.ENTITY_TICKER_COST.get();
double log2 = Math.log(product) / Math.log(2.0);
double raw;
if (product == 2L) {
raw = base * 4;
} else if (product <= 256L) {
raw = base * Math.pow(2.0, 1.5 * log2) * 2;
} else {
raw = base * Math.pow(2.0, 2.5 * log2);
}
return raw * getRemainingRatio(energyCardCount) / 8.0;
}
/**
* 计算能量卡减免后的剩余功耗比率
*/
public static double getRemainingRatio(int energyCardCount) {
if (energyCardCount <= 0) return 1.0;
if (energyCardCount == 1) return 0.9;
if (energyCardCount >= 8) return 0.5;
return 1.0 - 0.5 * (1.0 - Math.pow(0.7, energyCardCount));
}
/**
* 将剩余功耗比率格式化为百分比字符串
*/
public static String formatPercentage(double ratio) {
double pct = ratio * 100.0;
return Math.abs(pct - Math.round(pct)) < 1e-9 ?
String.format("%d%%", Math.round(pct)) :
String.format("%.2f%%", pct);
}
}

View File

@ -100,6 +100,7 @@
"extendedae_plus.configuration.entityTickerCost": "Entity Ticker Base Energy Cost", "extendedae_plus.configuration.entityTickerCost": "Entity Ticker Base Energy Cost",
"extendedae_plus.configuration.entityTickerBlackList": "Entity Ticker Blacklist", "extendedae_plus.configuration.entityTickerBlackList": "Entity Ticker Blacklist",
"extendedae_plus.configuration.entityTickerMultipliers": "Entity Ticker Extra Cost Multipliers", "extendedae_plus.configuration.entityTickerMultipliers": "Entity Ticker Extra Cost Multipliers",
"extendedae_plus.configuration.prioritizeDiskEnergy": "Prioritize FE energy from disk (requires Applied Flux)",
"extendedae_plus.configuration.state_on": "On", "extendedae_plus.configuration.state_on": "On",
"extendedae_plus.configuration.state_off": "Off" "extendedae_plus.configuration.state_off": "Off"

View File

@ -100,6 +100,7 @@
"extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值",
"extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单",
"extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率", "extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率",
"extendedae_plus.configuration.prioritizeDiskEnergy": "优先从磁盘提取FE能量仅当Applied Flux模组存在时生效",
"extendedae_plus.configuration.state_on": "开", "extendedae_plus.configuration.state_on": "开",
"extendedae_plus.configuration.state_off": "关" "extendedae_plus.configuration.state_off": "关"