feat: 实体加速器配置
This commit is contained in:
parent
0d9edb6773
commit
4a02c5bf42
|
|
@ -5,9 +5,13 @@ import appeng.menu.implementations.UpgradeableMenu;
|
|||
import appeng.menu.slot.OptionalFakeSlot;
|
||||
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
||||
import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
|
||||
import com.extendedae_plus.config.ModConfigs;
|
||||
import com.extendedae_plus.init.ModMenuTypes;
|
||||
import com.extendedae_plus.util.ConfigParsingUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
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> {
|
||||
|
|
@ -15,6 +19,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
|||
public int speedCardCount;
|
||||
// 已安装的能量卡数量
|
||||
public int energyCardCount;
|
||||
// 当前生效的倍率(从配置中读取并同步)
|
||||
public double multiplier = 1.0;
|
||||
|
||||
// 构造方法,初始化菜单并与部件绑定
|
||||
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.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 (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ import appeng.parts.PartModel;
|
|||
import appeng.parts.automation.UpgradeablePart;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
||||
import com.extendedae_plus.config.ModConfigs;
|
||||
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;
|
||||
|
|
@ -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.BlockEntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
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>
|
||||
|
|
@ -165,12 +172,19 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
return;
|
||||
}
|
||||
|
||||
// String id = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString();
|
||||
|
||||
// 获取方块实体的位置
|
||||
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(ModConfigs.EntitySpeedTickerBlackList.get());
|
||||
for (Pattern p : compiledBlacklist) {
|
||||
if (p.matcher(blockId).matches()) return;
|
||||
}
|
||||
|
||||
// 获取该方块实体的 Ticker
|
||||
@SuppressWarnings("unchecked")
|
||||
BlockEntityTicker<T> blockEntityTicker = this.getLevel()
|
||||
|
|
@ -191,6 +205,15 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
|||
// 使用工具类统一计算增长因子与原始功耗,并从网络中抽取对应能量
|
||||
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()
|
||||
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
|
|||
private void textData() {
|
||||
int speedCardCount = getMenu().speedCardCount;
|
||||
int energyCardCount = getMenu().energyCardCount;
|
||||
double multiplier = getMenu().multiplier;
|
||||
|
||||
double finalPower = PowerUtils.getFinalPower(speedCardCount, energyCardCount);
|
||||
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("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("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier)));
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,9 @@ public final class ModConfigs {
|
|||
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_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 {
|
||||
|
|
@ -81,10 +83,26 @@ public final class ModConfigs {
|
|||
.define("patternTerminalShowSlotsDefault", true);
|
||||
|
||||
|
||||
EntityTickerCost = builder
|
||||
builder.push("EntitySpeedTicker");
|
||||
EntitySpeedTickerCost = builder
|
||||
.comment("实体加速器的能量消耗基础值")
|
||||
.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();
|
||||
COMMON_SPEC = builder.build();
|
||||
}
|
||||
|
|
|
|||
158
src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
Normal file
158
src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ public final class PowerUtils {
|
|||
* @return 原始能耗
|
||||
*/
|
||||
public static double getRawPower(int speedCardCount) {
|
||||
double base = ModConfigs.EntityTickerCost.get();
|
||||
double base = ModConfigs.EntitySpeedTickerCost.get();
|
||||
return base * getGrowthFactor(speedCardCount);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@
|
|||
},
|
||||
"align": "CENTER"
|
||||
}
|
||||
,
|
||||
"multiplier": {
|
||||
"position": {
|
||||
"top": 90,
|
||||
"left": 88
|
||||
},
|
||||
"align": "CENTER"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"toolbox": {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"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.speed": "Current speed multiplier: %d",
|
||||
"screen.extendedae_plus.entity_speed_ticker.multiplier": "Extra consumption multiplier: %s",
|
||||
|
||||
"gui.expatternprovider.toggle_slots": "Toggle Slots",
|
||||
"gui.expatternprovider.hide_slots": "Hide Slots",
|
||||
|
|
@ -29,8 +30,7 @@
|
|||
"config.jade.plugin_extendedae_plus.wt_network_usable": "Show Network Online State",
|
||||
"extendedae_plus.tooltip.frequency": "Frequency: %d",
|
||||
"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",
|
||||
|
||||
"config.extendedae_plus.pageMultiplier": "Pattern Provider Page Multiplier",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
"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.speed": "当前加速倍率: %d",
|
||||
"screen.extendedae_plus.entity_speed_ticker.multiplier": "额外消耗倍率: %s",
|
||||
|
||||
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息",
|
||||
"config.jade.plugin_extendedae_plus.wt_frequency": "显示频率",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user