feat: 实体加速器配置

This commit is contained in:
C-H716 2025-09-12 00:33:48 +08:00
parent 0d9edb6773
commit 4a02c5bf42
9 changed files with 239 additions and 7 deletions

View File

@ -5,9 +5,13 @@ import appeng.menu.implementations.UpgradeableMenu;
import appeng.menu.slot.OptionalFakeSlot; import appeng.menu.slot.OptionalFakeSlot;
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.util.ConfigParsingUtils;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.registries.ForgeRegistries;
// 实体加速器菜单负责与客户端界面同步数据 // 实体加速器菜单负责与客户端界面同步数据
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> { public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
@ -15,6 +19,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
public int speedCardCount; public int speedCardCount;
// 已安装的能量卡数量 // 已安装的能量卡数量
public int energyCardCount; public int energyCardCount;
// 当前生效的倍率从配置中读取并同步
public double multiplier = 1.0;
// 构造方法初始化菜单并与部件绑定 // 构造方法初始化菜单并与部件绑定
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
@ -30,6 +36,22 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
// 重新统计速度卡和能量卡数量 // 重新统计速度卡和能量卡数量
this.speedCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.SPEED_CARD); this.speedCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.SPEED_CARD);
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); 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(ModConfigs.EntitySpeedTickerMultipliers.get())) {
if (me.pattern.matcher(blockId).matches()) {
mult = Math.max(mult, me.multiplier);
}
}
}
} catch (Exception ignored) {}
this.multiplier = mult;
// 如果在客户端刷新界面 // 如果在客户端刷新界面
if (isClientSide()) { if (isClientSide()) {
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {

View File

@ -19,7 +19,9 @@ import appeng.parts.PartModel;
import appeng.parts.automation.UpgradeablePart; import appeng.parts.automation.UpgradeablePart;
import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.util.ConfigParsingUtils;
import com.extendedae_plus.util.PowerUtils; import com.extendedae_plus.util.PowerUtils;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
@ -33,9 +35,14 @@ 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.minecraftforge.registries.ForgeRegistries;
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.util.Objects;
import java.util.regex.Pattern;
/** /**
* EntitySpeedTickerPart 是一个可升级的 AE2 部件<p> * EntitySpeedTickerPart 是一个可升级的 AE2 部件<p>
* 该部件可以加速目标方块实体的 tick 速率消耗 AE 网络能量并支持加速卡升级<p> * 该部件可以加速目标方块实体的 tick 速率消耗 AE 网络能量并支持加速卡升级<p>
@ -165,12 +172,19 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
return; return;
} }
// String id = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString();
// 获取方块实体的位置 // 获取方块实体的位置
BlockPos pos = blockEntity.getBlockPos(); BlockPos pos = blockEntity.getBlockPos();
if (blockEntity.getLevel() == null) return; if (blockEntity.getLevel() == null) return;
// 检查黑名单支持通配符/正则
String blockId = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString();
// 使用工具类的缓存接口工具类内部负责懒加载/线程安全
List<Pattern> compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.EntitySpeedTickerBlackList.get());
for (Pattern p : compiledBlacklist) {
if (p.matcher(blockId).matches()) return;
}
// 获取该方块实体的 Ticker // 获取该方块实体的 Ticker
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
BlockEntityTicker<T> blockEntityTicker = this.getLevel() BlockEntityTicker<T> blockEntityTicker = this.getLevel()
@ -191,6 +205,15 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
// 使用工具类统一计算增长因子与原始功耗并从网络中抽取对应能量 // 使用工具类统一计算增长因子与原始功耗并从网络中抽取对应能量
double requiredPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount); double requiredPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount);
double multiplier = 1.0;
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.EntitySpeedTickerMultipliers.get())) {
if (me.pattern.matcher(blockId).matches()) {
multiplier = Math.max(multiplier, me.multiplier);
}
}
requiredPower *= multiplier;
// 先模拟提取以检查网络中是否有足够能量再真正抽取 // 先模拟提取以检查网络中是否有足够能量再真正抽取
double simulated = getMainNode().getGrid().getEnergyService() double simulated = getMainNode().getGrid().getEnergyService()
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);

View File

@ -27,6 +27,7 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
private void textData() { private void textData() {
int speedCardCount = getMenu().speedCardCount; int speedCardCount = getMenu().speedCardCount;
int energyCardCount = getMenu().energyCardCount; int energyCardCount = getMenu().energyCardCount;
double multiplier = getMenu().multiplier;
double finalPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount); double finalPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount);
int speed = PowerUtils.getSpeedMultiplier(speedCardCount); int speed = PowerUtils.getSpeedMultiplier(speedCardCount);
@ -35,5 +36,6 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", speed)); setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", speed));
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", PowerUtils.formatPower(finalPower))); setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", PowerUtils.formatPower(finalPower)));
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio))); 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

@ -12,7 +12,9 @@ public final class ModConfigs {
public static final ForgeConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ForgeConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT;
public static final ForgeConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; public static final ForgeConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER;
public static final ForgeConfigSpec.IntValue SMART_SCALING_MIN_BENEFIT_FACTOR; public static final ForgeConfigSpec.IntValue SMART_SCALING_MIN_BENEFIT_FACTOR;
public static final ForgeConfigSpec.IntValue EntityTickerCost; public static final ForgeConfigSpec.IntValue EntitySpeedTickerCost;
public static final ForgeConfigSpec.ConfigValue<java.util.List<? extends String>> EntitySpeedTickerBlackList;
public static final ForgeConfigSpec.ConfigValue<java.util.List<? extends String>> EntitySpeedTickerMultipliers;
static { static {
@ -81,10 +83,26 @@ public final class ModConfigs {
.define("patternTerminalShowSlotsDefault", true); .define("patternTerminalShowSlotsDefault", true);
EntityTickerCost = builder builder.push("EntitySpeedTicker");
EntitySpeedTickerCost = builder
.comment("实体加速器的能量消耗基础值") .comment("实体加速器的能量消耗基础值")
.defineInRange("EntityTickerCost", 512, 0, Integer.MAX_VALUE); .defineInRange("EntityTickerCost", 512, 0, Integer.MAX_VALUE);
EntitySpeedTickerBlackList = builder
.comment(
"实体加速器黑名单:匹配的方块将不会被加速。支持通配符/正则例如minecraft:*",
"格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'"
)
.defineList("EntityTickerBlackList", java.util.List.of(), o -> o instanceof String);
EntitySpeedTickerMultipliers = builder
.comment(
"额外消耗倍率配置:为某些方块设置额外能量倍率,格式 'modid:blockid multiplier',例如 'minecraft:chest 2x'",
"支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。"
)
.defineList("EntityTickerMultipliers", java.util.List.of(), o -> o instanceof String);
builder.pop();
builder.pop(); builder.pop();
COMMON_SPEC = builder.build(); COMMON_SPEC = builder.build();
} }

View File

@ -0,0 +1,158 @@
package com.extendedae_plus.util;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* 配置解析工具类用于解析黑名单与倍率配置的字符串
*/
public final class ConfigParsingUtils {
private ConfigParsingUtils() {}
public static final class MultiplierEntry {
public final Pattern pattern;
public final double multiplier;
public MultiplierEntry(Pattern pattern, double multiplier) {
this.pattern = pattern;
this.multiplier = multiplier;
}
}
/**
* 编译用户提供的匹配串支持简单的 glob 语法'*' '?'以及完整的正则表达式
* 规则
* - 如果字符串包含 ".*" 或其他正则元字符优先尝试按正则编译若失败则回退到 glob 转换
* - 否则若包含 '*' '?'将按 glob 语法转换为正则
* - 否则按字面量匹配处理
*/
public static Pattern compilePattern(String raw) {
if (raw == null) throw new IllegalArgumentException("pattern is null");
raw = raw.trim();
if (raw.isEmpty()) throw new IllegalArgumentException("pattern is empty");
// If it looks like regex (contains '.*' or regex metachar), try regex first
if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) {
try {
return Pattern.compile("^" + raw + "$");
} catch (PatternSyntaxException ignored) {
// fallback to glob below
}
}
// If contains glob chars, convert to regex
if (raw.contains("*") || raw.contains("?")) {
StringBuilder sb = new StringBuilder();
sb.append('^');
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('\\');
}
sb.append(c);
}
}
sb.append('$');
return Pattern.compile(sb.toString());
}
// Otherwise treat as a literal (match exact)
return Pattern.compile("^" + Pattern.quote(raw) + "$");
}
/**
* Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects.
* Accepts values with optional trailing 'x' (case-insensitive).
*/
public static MultiplierEntry parseMultiplierEntry(String entry) {
if (entry == null) return null;
String[] parts = entry.trim().split("\\s+");
if (parts.length < 2) return null;
String key = parts[0];
String val = parts[1].toLowerCase();
if (val.endsWith("x")) val = val.substring(0, val.length() - 1);
double m;
try {
m = Double.parseDouble(val);
} catch (NumberFormatException ex) {
return null;
}
try {
Pattern p = compilePattern(key);
return new MultiplierEntry(p, m);
} catch (IllegalArgumentException e) {
return null;
}
}
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) {}
}
return out;
}
public static List<MultiplierEntry> parseMultiplierList(List<? extends String> raw) {
List<MultiplierEntry> out = new ArrayList<>();
if (raw == null) return out;
for (String s : raw) {
MultiplierEntry me = parseMultiplierEntry(s);
if (me != null) out.add(me);
}
return out;
}
// ------------------ 全局缓存与接口 ------------------
private static volatile List<Pattern> cachedBlacklist = null;
private static volatile List<MultiplierEntry> cachedMultiplierEntries = null;
private static final Object CACHE_LOCK = new Object();
/**
* 获取已解析并缓存的黑名单线程安全懒加载
*/
public static List<Pattern> getCachedBlacklist(java.util.List<? extends String> source) {
if (cachedBlacklist == null) {
synchronized (CACHE_LOCK) {
if (cachedBlacklist == null) {
cachedBlacklist = compilePatterns(source);
}
}
}
return java.util.List.copyOf(cachedBlacklist);
}
/**
* 获取已解析并缓存的倍率列表线程安全懒加载
*/
public static List<MultiplierEntry> getCachedMultiplierEntries(java.util.List<? extends String> source) {
if (cachedMultiplierEntries == null) {
synchronized (CACHE_LOCK) {
if (cachedMultiplierEntries == null) {
cachedMultiplierEntries = parseMultiplierList(source);
}
}
}
return java.util.List.copyOf(cachedMultiplierEntries);
}
/**
* 清空缓存下一次获取时将重新从提供的源解析或调用方可以重新调用 getter
*/
public static void reload() {
synchronized (CACHE_LOCK) {
cachedBlacklist = null;
cachedMultiplierEntries = null;
}
}
}

View File

@ -37,7 +37,7 @@ public final class PowerUtils {
* @return 原始能耗 * @return 原始能耗
*/ */
public static double getRawPower(int speedCardCount) { public static double getRawPower(int speedCardCount) {
double base = ModConfigs.EntityTickerCost.get(); double base = ModConfigs.EntitySpeedTickerCost.get();
return base * getGrowthFactor(speedCardCount); return base * getGrowthFactor(speedCardCount);
} }

View File

@ -44,6 +44,14 @@
}, },
"align": "CENTER" "align": "CENTER"
} }
,
"multiplier": {
"position": {
"top": 90,
"left": 88
},
"align": "CENTER"
}
}, },
"widgets": { "widgets": {
"toolbox": { "toolbox": {

View File

@ -3,6 +3,7 @@
"screen.extendedae_plus.entity_speed_ticker.energy": "Energy Usage: %s FE/t", "screen.extendedae_plus.entity_speed_ticker.energy": "Energy Usage: %s FE/t",
"screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power ratio: %s", "screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power ratio: %s",
"screen.extendedae_plus.entity_speed_ticker.speed": "Current speed multiplier: %d", "screen.extendedae_plus.entity_speed_ticker.speed": "Current speed multiplier: %d",
"screen.extendedae_plus.entity_speed_ticker.multiplier": "Extra consumption multiplier: %s",
"gui.expatternprovider.toggle_slots": "Toggle Slots", "gui.expatternprovider.toggle_slots": "Toggle Slots",
"gui.expatternprovider.hide_slots": "Hide Slots", "gui.expatternprovider.hide_slots": "Hide Slots",
@ -29,8 +30,7 @@
"config.jade.plugin_extendedae_plus.wt_network_usable": "Show Network Online State", "config.jade.plugin_extendedae_plus.wt_network_usable": "Show Network Online State",
"extendedae_plus.tooltip.frequency": "Frequency: %d", "extendedae_plus.tooltip.frequency": "Frequency: %d",
"extendedae_plus.tooltip.master_mode": "Mode: %s", "extendedae_plus.tooltip.master_mode": "Mode: %s",
"extendedae_plus.tooltip.locked": "Locked: %s" "extendedae_plus.tooltip.locked": "Locked: %s",
,
"screen.extendedae_plus.title": "ExtendedAE Plus Config", "screen.extendedae_plus.title": "ExtendedAE Plus Config",
"config.extendedae_plus.pageMultiplier": "Pattern Provider Page Multiplier", "config.extendedae_plus.pageMultiplier": "Pattern Provider Page Multiplier",

View File

@ -33,6 +33,7 @@
"screen.extendedae_plus.entity_speed_ticker.energy": "能耗: %s FE/t", "screen.extendedae_plus.entity_speed_ticker.energy": "能耗: %s FE/t",
"screen.extendedae_plus.entity_speed_ticker.power_ratio": "功耗比例: %s", "screen.extendedae_plus.entity_speed_ticker.power_ratio": "功耗比例: %s",
"screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d", "screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d",
"screen.extendedae_plus.entity_speed_ticker.multiplier": "额外消耗倍率: %s",
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息", "config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息",
"config.jade.plugin_extendedae_plus.wt_frequency": "显示频率", "config.jade.plugin_extendedae_plus.wt_frequency": "显示频率",