重新调整实体加速器代码结构,提升可读性
This commit is contained in:
parent
848cf86624
commit
47c4b2cab9
|
|
@ -18,140 +18,159 @@ import net.minecraftforge.registries.ForgeRegistries;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
// 实体加速器菜单,负责与客户端界面同步数据
|
||||
/**
|
||||
* 实体加速器菜单,负责管理客户端与服务端的数据同步,处理加速卡、能量卡和目标方块的状态。
|
||||
*/
|
||||
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
|
||||
@GuiSync(716) public boolean accelerateEnabled = true;
|
||||
// 已安装的实体加速卡数量(用于能耗计算)
|
||||
@GuiSync(717) public int entitySpeedCardCount;
|
||||
// 已安装的能量卡数量
|
||||
@GuiSync(718) 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;
|
||||
@GuiSync(716) public boolean accelerateEnabled = true; // 是否启用加速
|
||||
@GuiSync(717) public int entitySpeedCardCount; // 已安装的实体加速卡数量
|
||||
@GuiSync(718) public int energyCardCount; // 已安装的能量卡数量
|
||||
@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() {
|
||||
return this.accelerateEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加速开关状态,并同步到部件。
|
||||
* @param enabled 是否启用加速
|
||||
*/
|
||||
public void setAccelerateEnabled(boolean enabled) {
|
||||
this.accelerateEnabled = enabled;
|
||||
if (getHost() != null) {
|
||||
getHost().setAccelerateEnabled(enabled); // 同步到部件
|
||||
}
|
||||
broadcastChanges(); // 广播状态变化
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Part 更新 networkEnergySufficient 的封装方法(由服务器调用)
|
||||
* 该方法会更新 @GuiSync 字段并广播变化到客户端
|
||||
* 更新网络能量充足状态并广播到客户端。
|
||||
* @param sufficient 是否能量充足
|
||||
*/
|
||||
public void setNetworkEnergySufficient(boolean sufficient) {
|
||||
this.networkEnergySufficient = sufficient;
|
||||
// 触发一次数据广播,使客户端立即接收到最新状态
|
||||
this.broadcastChanges();
|
||||
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
|
||||
public void onServerDataSync() {
|
||||
super.onServerDataSync();
|
||||
// 重新统计实体加速卡和能量卡数量
|
||||
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
||||
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||
|
||||
// 计算当前面向方块的倍率(服务器端),并同步给客户端
|
||||
double mult = 1.0;
|
||||
try {
|
||||
BlockEntity target = getHost().getLevel().getBlockEntity(getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()));
|
||||
if (target != null) {
|
||||
String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
||||
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) {
|
||||
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) {
|
||||
String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
||||
for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList))) {
|
||||
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) {}
|
||||
|
||||
// 如果在客户端,刷新界面
|
||||
updateCardCounts(); // 更新卡数量
|
||||
updateTargetStatus(); // 更新目标方块的黑名单和倍率
|
||||
updateEffectiveSpeed(); // 计算生效速度
|
||||
updateNetworkEnergyStatus(); // 同步能量状态
|
||||
if (isClientSide()) {
|
||||
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
||||
screen.refreshGui();
|
||||
}
|
||||
refreshClientGui(); // 客户端刷新界面
|
||||
}
|
||||
}
|
||||
|
||||
// 当任意槽位发生变化时调用
|
||||
/**
|
||||
* 当槽位内容变化时调用,客户端更新卡数量和生效速度。
|
||||
* @param slot 发生变化的槽位
|
||||
*/
|
||||
@Override
|
||||
public void onSlotChange(net.minecraft.world.inventory.Slot slot) {
|
||||
super.onSlotChange(slot);
|
||||
// 客户端重新统计卡数量并刷新界面
|
||||
if (isClientSide()) {
|
||||
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
||||
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||
// 立即在客户端计算生效速度以便界面即时反馈(使用与服务端相同的工具方法,最多 8 张卡)
|
||||
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
|
||||
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
||||
screen.refreshGui();
|
||||
}
|
||||
updateCardCounts();
|
||||
updateEffectiveSpeed();
|
||||
refreshClientGui();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播数据变化,清理未启用槽位的显示堆栈。
|
||||
*/
|
||||
@Override
|
||||
public void broadcastChanges(){
|
||||
// 遍历所有槽位,清理未启用但有物品显示的 OptionalFakeSlot
|
||||
public void broadcastChanges() {
|
||||
for (Object o : this.slots) {
|
||||
if (o instanceof OptionalFakeSlot fs) {
|
||||
if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) {
|
||||
fs.clearStack();
|
||||
}
|
||||
if (o instanceof OptionalFakeSlot fs && !fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) {
|
||||
fs.clearStack(); // 清理未启用槽位的显示
|
||||
}
|
||||
}
|
||||
// 调用标准的同步方法,通知监听者数据已更新
|
||||
this.standardDetectAndSendChanges();
|
||||
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 = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
||||
this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, List.of(ModConfig.INSTANCE.entityTickerMultipliers));
|
||||
this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, List.of(ModConfig.INSTANCE.entityTickerBlackList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算生效速度(考虑黑名单和卡数量)。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import com.extendedae_plus.init.ModItems;
|
|||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.util.ConfigParsingUtils;
|
||||
import com.extendedae_plus.util.PowerUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
|
|
@ -41,52 +40,38 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* EntitySpeedTickerPart 是一个可升级的 AE2 部件<p>
|
||||
* 该部件可以加速目标方块实体的 tick 速率,消耗 AE 网络能量,并支持加速卡升级<p>
|
||||
* 功能受<a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>启发
|
||||
* 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。
|
||||
* 灵感来源于 <a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>。
|
||||
*/
|
||||
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject {
|
||||
// 当前打开的菜单实例(如果有)
|
||||
public EntitySpeedTickerMenu menu;
|
||||
|
||||
public static final ResourceLocation MODEL_BASE = new ResourceLocation(
|
||||
ExtendedAEPlus.MODID, "part/entity_speed_ticker_part");
|
||||
|
||||
@PartModels
|
||||
public static final PartModel MODELS_OFF;
|
||||
public static final PartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off"));
|
||||
@PartModels
|
||||
public static final PartModel MODELS_ON;
|
||||
public static final PartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on"));
|
||||
@PartModels
|
||||
public static final PartModel MODELS_HAS_CHANNEL;
|
||||
public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
|
||||
|
||||
static {
|
||||
MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off"));
|
||||
MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on"));
|
||||
MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
|
||||
}
|
||||
public EntitySpeedTickerMenu menu; // 当前打开的菜单实例
|
||||
private boolean accelerateEnabled = true; // 是否启用加速
|
||||
private boolean networkEnergySufficient = true; // 网络能量是否充足
|
||||
|
||||
/**
|
||||
* 构造函数,初始化部件并设置网络节点属性
|
||||
* 构造函数,初始化部件并设置网络节点属性。
|
||||
* @param partItem 部件物品
|
||||
*/
|
||||
public EntitySpeedTickerPart(IPartItem<?> partItem) {
|
||||
super(partItem);
|
||||
// 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务
|
||||
this.getMainNode()
|
||||
.setFlags(GridFlags.REQUIRE_CHANNEL)
|
||||
.setIdlePowerUsage(1)
|
||||
.addService(IGridTickable.class, this);
|
||||
}
|
||||
|
||||
// 控制是否启用加速(默认启用)
|
||||
private boolean accelerateEnabled = true;
|
||||
// 标记网络中能量是否充足(用于 GUI 提示,默认充足)
|
||||
private boolean networkEnergySufficient = true;
|
||||
|
||||
public boolean getAccelerateEnabled() {
|
||||
return this.accelerateEnabled;
|
||||
}
|
||||
|
|
@ -95,29 +80,33 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
return this.networkEnergySufficient;
|
||||
}
|
||||
|
||||
public void setAccelerateEnabled(boolean accelerateEnabled) {
|
||||
this.accelerateEnabled = accelerateEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网络能量充足标记并在菜单存在且状态变化时触发同步
|
||||
* @param sufficient 是否能量充足
|
||||
* 设置加速开关状态并通知菜单。
|
||||
* @param enabled 是否启用加速
|
||||
*/
|
||||
private void updateNetworkEnergySufficient(boolean sufficient) {
|
||||
// 保持部件内部状态一致(部件为权威来源)
|
||||
this.networkEnergySufficient = sufficient;
|
||||
if (this.menu != null) {
|
||||
try {
|
||||
// 使用菜单的封装方法更新并广播,以保持封装性
|
||||
this.menu.setNetworkEnergySufficient(sufficient);
|
||||
} catch (Exception ignored) {}
|
||||
public void setAccelerateEnabled(boolean enabled) {
|
||||
this.accelerateEnabled = enabled;
|
||||
if (menu != null) {
|
||||
menu.setAccelerateEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前状态下的静态模型(用于渲染)
|
||||
* 更新网络能量充足状态并通知菜单。
|
||||
* @param sufficient 是否能量充足
|
||||
*/
|
||||
private void updateNetworkEnergySufficient(boolean sufficient) {
|
||||
this.networkEnergySufficient = sufficient;
|
||||
if (menu != null) {
|
||||
menu.setNetworkEnergySufficient(sufficient);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前状态的渲染模型。
|
||||
* @return 当前状态的模型
|
||||
*/
|
||||
@Override
|
||||
public IPartModel getStaticModels() {
|
||||
if (this.isActive() && this.isPowered()) {
|
||||
return MODELS_HAS_CHANNEL;
|
||||
|
|
@ -129,15 +118,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
|
||||
/**
|
||||
* 当玩家激活部件(右键)时调用,打开自定义菜单
|
||||
* 处理玩家右键激活部件,打开菜单。
|
||||
* @param player 玩家
|
||||
* @param hand 手
|
||||
* @param pos 点击位置
|
||||
* @return 总是返回 true,表示激活成功
|
||||
* @return 总是返回 true
|
||||
*/
|
||||
@Override
|
||||
public boolean onPartActivate(Player player, InteractionHand hand, Vec3 pos) {
|
||||
// 仅在服务端打开菜单
|
||||
if (!player.getCommandSenderWorld().isClientSide()) {
|
||||
MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this));
|
||||
}
|
||||
|
|
@ -145,7 +133,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
|
||||
/**
|
||||
* 定义部件的碰撞箱(用于物理碰撞和渲染)
|
||||
* 定义部件的碰撞箱。
|
||||
* @param bch 碰撞辅助器
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -155,43 +143,37 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取定时请求,决定本部件多久 tick 一次
|
||||
* 获取定时请求,指定 tick 频率。
|
||||
* @param iGridNode 网络节点
|
||||
* @return TickingRequest 对象
|
||||
*/
|
||||
@Override
|
||||
public TickingRequest getTickingRequest(IGridNode iGridNode) {
|
||||
// 每 1 tick 执行一次
|
||||
return new TickingRequest(1, 1, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当升级卡数量发生变化时调用,通知菜单更新
|
||||
* 当升级卡变化时通知菜单更新。
|
||||
*/
|
||||
@Override
|
||||
public void upgradesChanged() {
|
||||
if (this.menu != null) {
|
||||
// 使用 AE2 风格:当升级发生变化时让菜单广播变化(槽/数据会被同步),客户端会基于槽内容重新计算并刷新界面
|
||||
this.menu.broadcastChanges();
|
||||
if (menu != null) {
|
||||
menu.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络定时回调,每次 tick 时调用
|
||||
* 网络定时回调,处理目标方块实体的加速。
|
||||
* @param iGridNode 网络节点
|
||||
* @param ticksSinceLastCall 距离上次调用经过的 tick 数
|
||||
* @return TickRateModulation.IDLE 表示继续保持当前 tick 速率
|
||||
* @param ticksSinceLastCall 距离上次调用的 tick 数
|
||||
* @return TickRateModulation.IDLE
|
||||
*/
|
||||
@Override
|
||||
public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) {
|
||||
// 如果部件的加速开关被关闭,则不进行加速(提前返回)
|
||||
if (!this.getAccelerateEnabled()) {
|
||||
return TickRateModulation.IDLE;
|
||||
}
|
||||
|
||||
// 获取目标方块实体(本部件朝向的方块)
|
||||
BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide()));
|
||||
// 仅在目标存在且部件处于激活状态时执行加速
|
||||
if (target != null && isActive()) {
|
||||
ticker(target);
|
||||
}
|
||||
|
|
@ -199,85 +181,108 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
|
||||
/**
|
||||
* 以指定速度对目标方块实体进行 tick 操作
|
||||
* @param blockEntity 需要被 tick 的方块实体
|
||||
* 对目标方块实体执行加速 tick 操作。
|
||||
* @param blockEntity 目标方块实体
|
||||
* @param <T> 方块实体类型
|
||||
*/
|
||||
private <T extends BlockEntity> void ticker(@NotNull T blockEntity) {
|
||||
if (this.getGridNode() == null
|
||||
|| this.getMainNode() == null
|
||||
|| this.getMainNode().getGrid() == null) {
|
||||
if (!isValidForTicking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取方块实体的位置
|
||||
BlockPos pos = blockEntity.getBlockPos();
|
||||
if (blockEntity.getLevel() == null) return;
|
||||
|
||||
// 检查黑名单(支持通配符/正则)
|
||||
String blockId = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString();
|
||||
|
||||
// 使用工具类的缓存接口(工具类内部负责懒加载/线程安全)
|
||||
List<Pattern> compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList));
|
||||
for (Pattern p : compiledBlacklist) {
|
||||
if (p.matcher(blockId).matches()) return;
|
||||
String blockId = ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock()).toString();
|
||||
if (ConfigParsingUtils.isBlockBlacklisted(blockId, List.of(ModConfig.INSTANCE.entityTickerBlackList))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取该方块实体的 Ticker
|
||||
@SuppressWarnings("unchecked")
|
||||
BlockEntityTicker<T> blockEntityTicker = this.getLevel()
|
||||
.getBlockState(pos)
|
||||
.getTicker(this.getLevel(), (BlockEntityType<T>) blockEntity.getType());
|
||||
if (blockEntityTicker == null) return;
|
||||
BlockEntityTicker<T> ticker = getTicker(blockEntity);
|
||||
if (ticker == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用集中定义的 CardDef 列表,支持以后添加等级或改倍率而无需修改此逻辑
|
||||
int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||
int speed = calculateSpeed();
|
||||
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());
|
||||
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, List.of(ModConfig.INSTANCE.entityTickerMultipliers));
|
||||
return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier;
|
||||
}
|
||||
|
||||
// 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗)
|
||||
if (entitySpeedCardCount <= 0) return;
|
||||
|
||||
// 计算本次 tick 所需能量:使用工具类根据 product 计算最终能耗
|
||||
double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount);
|
||||
|
||||
int speed = (int) product;
|
||||
|
||||
double multiplier = 1.0;
|
||||
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) {
|
||||
if (me.pattern.matcher(blockId).matches()) {
|
||||
multiplier = Math.max(multiplier, me.multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
requiredPower *= multiplier;
|
||||
|
||||
// 先模拟提取以检查网络中是否有足够能量,再真正抽取
|
||||
double simulated = getMainNode().getGrid().getEnergyService()
|
||||
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
||||
/**
|
||||
* 提取网络能量并更新状态。
|
||||
* @param requiredPower 所需能量
|
||||
* @return 是否成功提取
|
||||
*/
|
||||
private boolean extractPower(double requiredPower) {
|
||||
var energyService = getMainNode().getGrid().getEnergyService();
|
||||
double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
||||
if (simulated < requiredPower) {
|
||||
updateNetworkEnergySufficient(false); // 能量不足
|
||||
return;
|
||||
updateNetworkEnergySufficient(false);
|
||||
return false;
|
||||
}
|
||||
double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
||||
boolean sufficient = extracted >= requiredPower;
|
||||
updateNetworkEnergySufficient(sufficient);
|
||||
return sufficient;
|
||||
}
|
||||
|
||||
double extractedPower = getMainNode().getGrid().getEnergyService()
|
||||
.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
||||
if (extractedPower < requiredPower) {
|
||||
updateNetworkEnergySufficient(false); // 能量不足
|
||||
return;
|
||||
}
|
||||
updateNetworkEnergySufficient(true); // 能量充足
|
||||
|
||||
// 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10)
|
||||
// 已由 product 计算得到 speed;上面已在没有卡时提前返回
|
||||
|
||||
// 执行 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++) {
|
||||
blockEntityTicker.tick(
|
||||
ticker.tick(
|
||||
blockEntity.getLevel(),
|
||||
blockEntity.getBlockPos(),
|
||||
blockEntity.getBlockState(),
|
||||
|
|
@ -286,26 +291,18 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断部件是否有自定义名称
|
||||
* @return 是否有自定义名称
|
||||
*/
|
||||
@Override
|
||||
public boolean hasCustomName() {
|
||||
return super.hasCustomName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部件的显示名称
|
||||
* @return 显示名称
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Component getDisplayName() {
|
||||
return super.getDisplayName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义菜单(GUI)
|
||||
* 创建菜单实例。
|
||||
* @param containerId 容器ID
|
||||
* @param playerInventory 玩家背包
|
||||
* @param player 玩家
|
||||
|
|
@ -319,7 +316,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取可用的升级卡槽数量
|
||||
* 获取升级卡槽数量。
|
||||
* @return 升级卡槽数量
|
||||
*/
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -15,125 +15,111 @@ import com.extendedae_plus.util.PowerUtils;
|
|||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 实体加速器界面,显示加速状态、卡数量、能耗和倍率信息。
|
||||
*/
|
||||
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
|
||||
private boolean eap$entitySpeedTickerEnabled = false;
|
||||
private SettingToggleButton<YesNo> eap$entitySpeedTickerToggle;
|
||||
private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
|
||||
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);
|
||||
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<>(
|
||||
Settings.BLOCKING_MODE,
|
||||
this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) -> {
|
||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket());
|
||||
}
|
||||
(btn, backwards) ->
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket())
|
||||
) {
|
||||
@Override
|
||||
public List<Component> getTooltipMessage() {
|
||||
// 如果目标在黑名单中,直接显示已禁用的提示
|
||||
try {
|
||||
if (menu != null && menu.targetBlacklisted) {
|
||||
var title = Component.literal("实体加速");
|
||||
var stateLine = Component.literal("已禁用(目标在黑名单)");
|
||||
return List.of(title, stateLine);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (menu.targetBlacklisted) {
|
||||
return List.of(
|
||||
Component.literal("实体加速"),
|
||||
Component.literal("已禁用(目标在黑名单)")
|
||||
);
|
||||
}
|
||||
boolean enabled = eap$entitySpeedTickerEnabled;
|
||||
var title = Component.literal("实体加速");
|
||||
var stateLine = enabled
|
||||
? Component.literal("已启用: 将加速目标方块实体的tick")
|
||||
: Component.literal("已关闭: 不会对目标方块实体进行加速");
|
||||
return List.of(title, stateLine);
|
||||
return List.of(
|
||||
Component.literal("实体加速"),
|
||||
enabled ? Component.literal("已启用: 将加速目标方块实体的tick") :
|
||||
Component.literal("已关闭: 不会对目标方块实体进行加速")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Icon getIcon() {
|
||||
try {
|
||||
if (menu != null && menu.targetBlacklisted) {
|
||||
// 黑名单时显示禁用图标
|
||||
return Icon.INVALID;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
// 根据当前值显示不同图标(可按需替换 Icon 常量)
|
||||
if (this.getCurrentValue() == YesNo.YES) {
|
||||
return Icon.VALID;
|
||||
} else {
|
||||
return Icon.INVALID;
|
||||
}
|
||||
if (menu.targetBlacklisted) return Icon.INVALID;
|
||||
return this.getCurrentValue() == YesNo.YES ? Icon.VALID : Icon.INVALID;
|
||||
}
|
||||
};
|
||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
||||
eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO);
|
||||
|
||||
this.addToLeftToolbar(eap$entitySpeedTickerToggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在渲染前更新界面状态。
|
||||
*/
|
||||
@Override
|
||||
protected void updateBeforeRender() {
|
||||
super.updateBeforeRender();
|
||||
|
||||
if (this.eap$entitySpeedTickerToggle != null) {
|
||||
boolean desired = this.eap$entitySpeedTickerEnabled;
|
||||
if (this.menu != null) {
|
||||
desired = this.menu.getAccelerateEnabled();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (eap$entitySpeedTickerToggle != null && menu != null) {
|
||||
eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
|
||||
// 如果目标在黑名单,禁用按钮并显示关闭状态
|
||||
eap$entitySpeedTickerToggle.set(menu.targetBlacklisted ? YesNo.NO : (eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO));
|
||||
eap$entitySpeedTickerToggle.active = !menu.targetBlacklisted;
|
||||
}
|
||||
textData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新界面显示。
|
||||
*/
|
||||
public void refreshGui() {
|
||||
textData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新界面文本内容,包括加速状态、速度、能耗和倍率。
|
||||
*/
|
||||
private void textData() {
|
||||
// 如果目标被黑名单禁止,则显示禁用状态并把数值显示为 0
|
||||
Map<String, Component> textContents = new HashMap<>();
|
||||
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));
|
||||
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false)));
|
||||
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0)));
|
||||
setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0)));
|
||||
return;
|
||||
}
|
||||
// 黑名单禁用时的默认显示
|
||||
textContents.put("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable"));
|
||||
textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0));
|
||||
textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false)));
|
||||
textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0)));
|
||||
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);
|
||||
|
||||
int energyCardCount = getMenu().energyCardCount;
|
||||
double multiplier = getMenu().multiplier;
|
||||
int effectiveSpeed = getMenu().effectiveSpeed;
|
||||
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
|
||||
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount);
|
||||
|
||||
if (!getMenu().networkEnergySufficient) {
|
||||
setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
|
||||
}else {
|
||||
setTextContent("enable", null);
|
||||
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)));
|
||||
}
|
||||
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)));
|
||||
textContents.forEach(this::setTextContent);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import java.util.List;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
|
||||
/**
|
||||
* 配置解析工具类:用于解析黑名单与倍率配置的字符串
|
||||
*/
|
||||
|
|
@ -24,35 +26,32 @@ public final class ConfigParsingUtils {
|
|||
|
||||
/**
|
||||
* 编译用户提供的匹配串。支持简单的 glob 语法('*' 和 '?')以及完整的正则表达式。
|
||||
* 规则:
|
||||
* - 如果字符串包含 ".*" 或其他正则元字符,优先尝试按正则编译(若失败则回退到 glob 转换)。
|
||||
* - 否则若包含 '*' 或 '?',将按 glob 语法转换为正则。
|
||||
* - 否则按字面量匹配处理。
|
||||
*/
|
||||
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();
|
||||
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(".*[\\[\\(\\+\\{\\\\].*")) {
|
||||
try {
|
||||
return Pattern.compile("^" + raw + "$");
|
||||
} catch (PatternSyntaxException ignored) {
|
||||
// fallback to glob below
|
||||
} catch (PatternSyntaxException e) {
|
||||
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("?")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('^');
|
||||
StringBuilder sb = new StringBuilder("^");
|
||||
for (char c : raw.toCharArray()) {
|
||||
switch (c) {
|
||||
case '*': sb.append(".*"); break;
|
||||
case '?': sb.append('.'); break;
|
||||
default:
|
||||
// escape regex special chars
|
||||
if (".\\+[]{}()^$|".indexOf(c) >= 0) {
|
||||
sb.append('\\');
|
||||
}
|
||||
|
|
@ -63,41 +62,71 @@ public final class ConfigParsingUtils {
|
|||
return Pattern.compile(sb.toString());
|
||||
}
|
||||
|
||||
// Otherwise treat as a literal (match exact)
|
||||
// Literal match
|
||||
return Pattern.compile("^" + Pattern.quote(raw) + "$");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects.
|
||||
* Accepts values with optional trailing 'x' (case-insensitive).
|
||||
* 解析倍率条目,如 'modid:block 2x'。
|
||||
*/
|
||||
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+");
|
||||
if (parts.length < 2) return null;
|
||||
if (parts.length < 2) {
|
||||
LOGGER.warn("Invalid multiplier entry: {}", entry);
|
||||
return null;
|
||||
}
|
||||
String key = parts[0];
|
||||
String val = parts[1].toLowerCase();
|
||||
if (val.endsWith("x")) val = val.substring(0, val.length() - 1);
|
||||
double m;
|
||||
double multiplier;
|
||||
try {
|
||||
m = Double.parseDouble(val);
|
||||
} catch (NumberFormatException ex) {
|
||||
multiplier = Double.parseDouble(val);
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.warn("Invalid multiplier value in '{}': {}", entry, val);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Pattern p = compilePattern(key);
|
||||
return new MultiplierEntry(p, m);
|
||||
Pattern pattern = compilePattern(key);
|
||||
return new MultiplierEntry(pattern, multiplier);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.warn("Failed to compile pattern in '{}': {}", entry, e.getMessage());
|
||||
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) {
|
||||
List<Pattern> out = new ArrayList<>();
|
||||
if (raw == null) return out;
|
||||
for (String s : raw) {
|
||||
if (s == null || s.isBlank()) continue;
|
||||
try { out.add(compilePattern(s)); } catch (IllegalArgumentException ignored) {}
|
||||
if (s == null || s.trim().isEmpty()) continue;
|
||||
try {
|
||||
out.add(compilePattern(s));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage());
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -122,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);
|
||||
|
||||
// fast path: identical snapshot reference or equal contents
|
||||
if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) {
|
||||
return Collections.unmodifiableList(cachedBlacklist);
|
||||
}
|
||||
|
||||
synchronized (CACHE_LOCK) {
|
||||
if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) {
|
||||
cachedBlacklist = compilePatterns(normalized);
|
||||
|
|
@ -142,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);
|
||||
|
||||
if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) {
|
||||
return Collections.unmodifiableList(cachedMultiplierEntries);
|
||||
}
|
||||
|
||||
synchronized (CACHE_LOCK) {
|
||||
if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) {
|
||||
cachedMultiplierEntries = parseMultiplierList(normalized);
|
||||
|
|
@ -189,8 +213,8 @@ public final class ConfigParsingUtils {
|
|||
synchronized (CACHE_LOCK) {
|
||||
cachedBlacklist = null;
|
||||
cachedMultiplierEntries = null;
|
||||
cachedBlacklistSourceSnapshot = null;
|
||||
cachedMultiplierSourceSnapshot = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,39 +1,51 @@
|
|||
package com.extendedae_plus.util;
|
||||
|
||||
import appeng.api.upgrades.IUpgradeInventory;
|
||||
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用于计算实体加速器的能耗与加速倍率的工具类
|
||||
*/
|
||||
public final class PowerUtils {
|
||||
private PowerUtils() {}
|
||||
// ---- 重构后的 API ----
|
||||
|
||||
/**
|
||||
* 将 card multipliers(按插槽序)计算乘积并应用 cap 规则(见 capForHighestMultiplier)
|
||||
* @param multipliers iterable of per-card multipliers
|
||||
* @param maxCards 最多计入的卡数
|
||||
* @return 被 cap 约束后的乘积
|
||||
* 计算加速卡的乘积并应用上限。
|
||||
* @param upgrades 升级槽位
|
||||
* @param maxCards 最大计入的卡数
|
||||
* @return 被上限约束后的乘积
|
||||
*/
|
||||
public static long computeProductWithCap(Iterable<Integer> multipliers, int maxCards) {
|
||||
long product = 1L;
|
||||
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) continue;
|
||||
if (considered >= maxCards) break;
|
||||
int mult = m.intValue();
|
||||
if (mult <= 0) mult = 1;
|
||||
product *= mult;
|
||||
highest = Math.max(highest, mult);
|
||||
considered++;
|
||||
if (m == null || m <= 0) continue;
|
||||
product *= m;
|
||||
highest = Math.max(highest, m);
|
||||
}
|
||||
long cap = capForHighestMultiplier(highest);
|
||||
return Math.min(product, cap);
|
||||
return Math.min(product, capForHighestMultiplier(highest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据最高单卡 multiplier 返回 cap 值
|
||||
* 根据最高单卡倍率返回上限值。
|
||||
*/
|
||||
public static long capForHighestMultiplier(int highestMultiplier) {
|
||||
if (highestMultiplier >= 16) return 1024L;
|
||||
|
|
@ -44,118 +56,43 @@ public final class PowerUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* 从菜单对象读取前 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
|
||||
* 计算最终能耗。
|
||||
* @param product 加速卡乘积
|
||||
* @param energyCardCount 能量卡数量
|
||||
* @return 最终能耗值
|
||||
*/
|
||||
public static double computeFinalPowerForProduct(long product, int energyCardCount) {
|
||||
if (product <= 1L) return 0.0;
|
||||
double base = ModConfig.INSTANCE.entityTickerCost;
|
||||
|
||||
// 计算以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 = ModConfig.INSTANCE.entityTickerCost;
|
||||
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;
|
||||
return raw * getRemainingRatio(energyCardCount) / 8.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9)
|
||||
* @param energyCardCount 能源卡数量
|
||||
* @return 剩余功耗比率
|
||||
* 计算能量卡减免后的剩余功耗比率。
|
||||
*/
|
||||
public static double getRemainingRatio(int energyCardCount) {
|
||||
return 1.0 - getReductionPercent(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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将剩余功耗比率格式化为百分比字符串(例如 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);
|
||||
return Math.abs(pct - Math.round(pct)) < 1e-9 ?
|
||||
String.format("%d%%", Math.round(pct)) :
|
||||
String.format("%.2f%%", pct);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user