重构实体加速器界面代码;

补全mod信息;
更新实体加速器GUI;
更新实体加速器模型
This commit is contained in:
C-H716 2025-11-24 17:59:08 +08:00
parent 444f682ca0
commit 2a7331b1f3
9 changed files with 662 additions and 264 deletions

View File

@ -30,7 +30,7 @@ mod_id=extendedae_plus
# The human-readable display name for the mod. # The human-readable display name for the mod.
mod_name=ExtendedAE-Plus mod_name=ExtendedAE-Plus
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=All Rights Reserved mod_license=LGPL-3.0-or-later
# The mod version. See https://semver.org/ # The mod version. See https://semver.org/
mod_version=1.21.1-1.4.3 mod_version=1.21.1-1.4.3
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
@ -38,9 +38,9 @@ mod_version=1.21.1-1.4.3
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # See https://maven.apache.org/guides/mini/guide-naming-conventions.html
mod_group_id=com.extendedae_plus mod_group_id=com.extendedae_plus
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list. # The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
mod_authors=YourNameHere, OtherNameHere mod_authors=GaLi, C-H716
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
mod_description=Example mod description.\nNewline characters can be used and will be replaced properly. mod_description=Add more practical features and auxiliary operations to the Applied Energistics 2 mod
## UI item explorer selection (emi | rei | jei) ## UI item explorer selection (emi | rei | jei)
# Default to 'emi' per request; you can override by running with -Puse_Xei=rei or -Puse_Xei=jei # Default to 'emi' per request; you can override by running with -Puse_Xei=rei or -Puse_Xei=jei

View File

@ -9,12 +9,12 @@ 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.api.config.EAPSettings; import com.extendedae_plus.api.config.EAPSettings;
import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
import com.extendedae_plus.util.entitySpeed.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.inventory.Slot; import net.minecraft.world.inventory.Slot;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -23,25 +23,20 @@ import net.minecraft.world.level.block.entity.BlockEntity;
* 实体加速器菜单负责管理客户端与服务端的数据同步处理加速卡能量卡和目标方块的状态 * 实体加速器菜单负责管理客户端与服务端的数据同步处理加速卡能量卡和目标方块的状态
*/ */
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> { public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
@GuiSync(716) public YesNo accelerate; // 是否启用加速 private final EntitySpeedTickerPart logic;
@GuiSync(717) public YesNo redstoneControl; // 是否启用红石控制 @GuiSync(716) public int energyCardCount; // 已安装的能量卡数量
private int entitySpeedCardCount; // 已安装的实体加速卡数量 @GuiSync(717) public int effectiveSpeed = 1; // 当前生效的加速倍率
@GuiSync(719) public int energyCardCount; // 已安装的能量卡数量 @GuiSync(718) public double multiplier = 1.0; // 目标方块的配置倍率
@GuiSync(720) public int effectiveSpeed = 1; // 当前生效的加速倍率 @GuiSync(719) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中
@GuiSync(721) public double multiplier = 1.0; // 目标方块的配置倍率 @GuiSync(720) public YesNo networkEnergySufficient; // 网络能量是否充足
@GuiSync(722) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中 @GuiSync(721) private YesNo accelerate; // 是否启用加速
@GuiSync(723) public YesNo networkEnergySufficient; // 网络能量是否充足 @GuiSync(722) private YesNo redstoneControl; // 是否启用红石控制
protected final EntitySpeedTickerPart logic;
@Override
protected void loadSettingsFromHost(IConfigManager cm) {
// 不需要模糊模式
}
/** /**
* 构造函数初始化菜单并绑定部件 * 构造函数初始化菜单并绑定部件
* @param id 菜单ID *
* @param ip 玩家背包 * @param id 菜单ID
* @param ip 玩家背包
* @param host 关联的实体加速器部件 * @param host 关联的实体加速器部件
*/ */
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
@ -49,32 +44,34 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
this.logic = host; this.logic = host;
} }
/** // 服务端 客户端同步时触发包括第一次打开 GUI
* 服务端数据同步到客户端时调用更新卡数量目标状态和生效速度
*/
@Override @Override
public void onServerDataSync(ShortSet updatedFields) { public void onServerDataSync(ShortSet updatedFields) {
super.onServerDataSync(updatedFields); super.onServerDataSync(updatedFields);
updateCardCounts(); // 更新卡数量
updateTargetStatus(); // 更新目标方块的黑名单和倍率 // 客户端只更新可能变化的显示相关字段
updateEffectiveSpeed(); // 计算生效速度 // 顺序必须和 broadcastChanges 里一致
// updateNetworkEnergyStatus(); // 同步能量状态 this.updateTargetStatus(); // multiplier + targetBlacklisted
if (isClientSide()) { this.updateEffectiveSpeed(); // 依赖上面两个字段
refreshClientGui(); // 客户端刷新界面
if (this.isClientSide()) {
this.refreshClientGui();
} }
} }
/** /**
* 当槽位内容变化时调用客户端更新卡数量和生效速度 * 当槽位内容变化时调用客户端更新卡数量和生效速度
*
* @param slot 发生变化的槽位 * @param slot 发生变化的槽位
*/ */
@Override @Override
public void onSlotChange(Slot slot) { public void onSlotChange(Slot slot) {
super.onSlotChange(slot); super.onSlotChange(slot);
if (isClientSide()) { if (this.isClientSide()) {
updateCardCounts(); // 升级卡变化时energyCardCount 已经由 AE2 自动同步
updateEffectiveSpeed(); // 重新计算生效速度并刷新 UI
refreshClientGui(); this.updateEffectiveSpeed();
this.refreshClientGui();
} }
} }
@ -83,14 +80,27 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
*/ */
@Override @Override
public void broadcastChanges() { public void broadcastChanges() {
if (isServerSide()) { if (this.isServerSide()) {
this.accelerate = logic.getConfigManager().getSetting(EAPSettings.ACCELERATE); // 1. 先更新配置类开关红石能量充足
this.redstoneControl = logic.getConfigManager().getSetting(EAPSettings.REDSTONE_CONTROL); this.accelerate = this.logic.getConfigManager().getSetting(EAPSettings.ACCELERATE);
this.networkEnergySufficient = logic.isNetworkEnergySufficient() ? YesNo.YES : YesNo.NO; this.redstoneControl = this.logic.getConfigManager().getSetting(EAPSettings.REDSTONE_CONTROL);
this.networkEnergySufficient = this.logic.isNetworkEnergySufficient() ? YesNo.YES : YesNo.NO;
// 2. 再更新升级卡数量
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
// 3. 最后更新目标状态 + 生效速度
this.updateTargetStatus();
this.updateEffectiveSpeed();
} }
super.broadcastChanges(); super.broadcastChanges();
} }
@Override
protected void loadSettingsFromHost(IConfigManager cm) {
// 不需要模糊模式
}
public YesNo getAccelerate() { public YesNo getAccelerate() {
return this.accelerate; return this.accelerate;
} }
@ -98,25 +108,18 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
public YesNo getRedstoneControl() { public YesNo getRedstoneControl() {
return this.redstoneControl; return this.redstoneControl;
} }
/**
* 更新加速卡和能量卡的数量
*/
private void updateCardCounts() {
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
}
/** /**
* 更新目标方块的黑名单状态和倍率 * 更新目标方块的黑名单状态和倍率
*/ */
private void updateTargetStatus() { private void updateTargetStatus() {
BlockEntity target = getTargetBlockEntity(); BlockEntity target = this.getTargetBlockEntity();
if (target == null) { if (target == null) {
this.multiplier = 1.0; this.multiplier = 1.0;
this.targetBlacklisted = false; this.targetBlacklisted = false;
return; return;
} }
String blockId = net.minecraft.core.registries.BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString(); String blockId = BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString();
this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get()); this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get());
this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get()); this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get());
} }
@ -125,7 +128,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
* 计算生效速度考虑黑名单和卡数量 * 计算生效速度考虑黑名单和卡数量
*/ */
private void updateEffectiveSpeed() { private void updateEffectiveSpeed() {
this.effectiveSpeed = targetBlacklisted ? 0 : (int) PowerUtils.computeProductWithCap(getUpgrades(), 8); this.effectiveSpeed =
this.targetBlacklisted ? 0 : (int) PowerUtils.computeProductWithCap(this.getUpgrades(), 8);
} }
/** /**
@ -139,12 +143,13 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
/** /**
* 获取目标方块实体 * 获取目标方块实体
*
* @return 目标方块实体或 null * @return 目标方块实体或 null
*/ */
private BlockEntity getTargetBlockEntity() { private BlockEntity getTargetBlockEntity() {
return getHost() != null ? return this.getHost() != null ?
getHost().getLevel().getBlockEntity( this.getHost().getLevel().getBlockEntity(
getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) this.getHost().getBlockEntity().getBlockPos().relative(this.getHost().getSide())
) : null; ) : null;
} }
} }

View File

@ -18,6 +18,7 @@ import appeng.api.parts.IPartModel;
import appeng.api.storage.MEStorage; import appeng.api.storage.MEStorage;
import appeng.api.upgrades.IUpgradeableObject; import appeng.api.upgrades.IUpgradeableObject;
import appeng.api.util.IConfigManager; import appeng.api.util.IConfigManager;
import appeng.api.util.IConfigManagerBuilder;
import appeng.core.definitions.AEItems; import appeng.core.definitions.AEItems;
import appeng.items.parts.PartModels; import appeng.items.parts.PartModels;
import appeng.menu.MenuOpener; import appeng.menu.MenuOpener;
@ -30,8 +31,10 @@ import com.extendedae_plus.api.config.EAPSettings;
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.ExtendedAELogger;
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
import com.extendedae_plus.util.entitySpeed.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;
@ -39,6 +42,7 @@ import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.entity.BlockEntity; 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;
@ -55,14 +59,14 @@ import java.lang.reflect.Method;
* 功能受<a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>启发 * 功能受<a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>启发
*/ */
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject {
public static final ResourceLocation MODEL_BASE = ResourceLocation.fromNamespaceAndPath( private static final ResourceLocation MODEL_BASE = ResourceLocation.fromNamespaceAndPath(
ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); ExtendedAEPlus.MODID, "part/entity_speed_ticker_part");
@PartModels @PartModels
public static final PartModel MODELS_OFF; private static final PartModel MODELS_OFF;
@PartModels @PartModels
public static final PartModel MODELS_ON; private static final PartModel MODELS_ON;
@PartModels @PartModels
public static final PartModel MODELS_HAS_CHANNEL; private static final PartModel MODELS_HAS_CHANNEL;
static { static {
MODELS_OFF = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off")); MODELS_OFF = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off"));
@ -70,10 +74,9 @@ 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"));
} }
private final IConfigManager configManager;
public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 public EntitySpeedTickerMenu menu; // 当前打开的菜单实例
private YesNo networkEnergySufficient; // 网络能量是否充足 private YesNo networkEnergySufficient = YesNo.YES; // 网络能量是否充足
private YesNo redstoneState = YesNo.UNDECIDED;
/** /**
* 构造函数初始化部件并设置网络节点属性 * 构造函数初始化部件并设置网络节点属性
* *
@ -85,40 +88,66 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
.setFlags(GridFlags.REQUIRE_CHANNEL) .setFlags(GridFlags.REQUIRE_CHANNEL)
.setIdlePowerUsage(1) .setIdlePowerUsage(1)
.addService(IGridTickable.class, this); .addService(IGridTickable.class, this);
configManager = IConfigManager.builder(this::configChanged)
.registerSetting(EAPSettings.ACCELERATE, YesNo.NO)
.registerSetting(EAPSettings.REDSTONE_CONTROL, YesNo.YES)
.build();
} }
private void configChanged(IConfigManager manager, Setting<?> setting) { @Override
this.saveChanges(); protected void registerSettings(IConfigManagerBuilder builder) {
} super.registerSettings(builder);
builder.registerSetting(EAPSettings.ACCELERATE, YesNo.YES); // 默认开启加速
public void saveChanges() { builder.registerSetting(EAPSettings.REDSTONE_CONTROL, YesNo.NO); // 默认忽略红石信号
getHost().markForSave();
}
public boolean isAccelerate() {
return this.configManager.getSetting(EAPSettings.ACCELERATE) == YesNo.YES;
}
public boolean isRedstoneControl() {
return this.configManager.getSetting(EAPSettings.REDSTONE_CONTROL) == YesNo.YES;
}
public boolean isNetworkEnergySufficient() {
return this.networkEnergySufficient == YesNo.YES;
} }
/** /**
* 更新网络能量充足状态并通知菜单 * 获取可用的升级卡槽数量
* *
* @param sufficient 是否能量充足 * @return 升级卡槽数量
*/ */
private void setNetworkEnergySufficient(boolean sufficient) { @Override
this.networkEnergySufficient = sufficient ? YesNo.YES : YesNo.NO; protected int getUpgradeSlots() {
saveChanges(); return 8;
}
// 判断当前是否应该休眠
@Override
protected boolean isSleeping() {
// 主开关没开 休眠
if (this.getConfigManager().getSetting(EAPSettings.ACCELERATE) != YesNo.YES) {
return true;
}
// 没开红石控制 一直工作
if (this.getConfigManager().getSetting(EAPSettings.REDSTONE_CONTROL) != YesNo.YES) {
return false;
}
// 开启了红石控制 必须有红石信号才工作
return !this.getHost().hasRedstone(); // 没信号 休眠
}
@Override
protected void onSettingChanged(IConfigManager manager, Setting<?> setting) {
// 每次玩家在 GUI 里点任何按钮包括加速开关红石控制开关都会进来这里
this.getMainNode().ifPresent((grid, node) -> {
if (this.isSleeping()) {
grid.getTickManager().sleepDevice(node);
} else {
grid.getTickManager().wakeDevice(node);
}
});
}
@Override
public void onNeighborChanged(BlockGetter level, BlockPos pos, BlockPos neighbor) {
// 只关心红石控制开启的情况下
if (this.getConfigManager().getSetting(EAPSettings.REDSTONE_CONTROL) == YesNo.YES) {
this.getMainNode().ifPresent((grid, node) -> {
if (this.getHost().hasRedstone()) {
grid.getTickManager().wakeDevice(node); // 有信号 立刻唤醒
} else {
grid.getTickManager().sleepDevice(node); // 没信号 立刻休眠
}
});
}
} }
/** /**
@ -130,7 +159,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
*/ */
@Override @Override
public final boolean onUseWithoutItem(Player player, Vec3 pos) { public final boolean onUseWithoutItem(Player player, Vec3 pos) {
if (!isClientSide()) { if (!this.isClientSide()) {
MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this)); MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this));
} }
return true; return true;
@ -159,8 +188,30 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
*/ */
@Override @Override
public void getBoxes(IPartCollisionHelper bch) { public void getBoxes(IPartCollisionHelper bch) {
bch.addBox(2, 2, 14, 14, 14, 16); bch.addBox(3, 3, 14, 13, 13, 16);
bch.addBox(5, 5, 12, 11, 11, 14); bch.addBox(5, 5, 11, 11, 11, 14);
}
public void saveChanges() {
this.getHost().markForSave();
}
private boolean isAccelerate() {
return this.getConfigManager().getSetting(EAPSettings.ACCELERATE) == YesNo.YES;
}
public boolean isNetworkEnergySufficient() {
return this.networkEnergySufficient == YesNo.YES;
}
/**
* 更新网络能量充足状态并通知菜单
*
* @param sufficient 是否能量充足
*/
private void setNetworkEnergySufficient(boolean sufficient) {
this.networkEnergySufficient = sufficient ? YesNo.YES : YesNo.NO;
this.saveChanges();
} }
/** /**
@ -172,7 +223,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
@Override @Override
public TickingRequest getTickingRequest(IGridNode iGridNode) { public TickingRequest getTickingRequest(IGridNode iGridNode) {
// 1 tick 执行一次 // 1 tick 执行一次
return new TickingRequest(1, 1, false); return new TickingRequest(1, 1, this.isSleeping());
} }
/** /**
@ -184,23 +235,24 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
*/ */
@Override @Override
public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) { public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) {
// 如果部件的加速开关被关闭则不进行加速提前返回 if (!this.isAccelerate()) {
if (!isAccelerate()) {
return TickRateModulation.IDLE; return TickRateModulation.IDLE;
} }
// 检查红石控制 if (this.isSleeping()) {
if (isRedstoneControl() && !getRedstoneState()) { return TickRateModulation.SLEEP;
// 如果启用了红石控制且没有红石信号则不执行加速
return TickRateModulation.IDLE;
} }
// 获取目标方块实体本部件朝向的方块 // 获取目标方块实体本部件朝向的方块
BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide())); BlockEntity target = this.getLevel().getBlockEntity(
this.getBlockEntity().getBlockPos().relative(this.getSide())
);
// 仅在目标存在且部件处于激活状态时执行加速 // 仅在目标存在且部件处于激活状态时执行加速
if (target != null && isActive()) { if (target == null || !this.isActive()) {
ticker(target); return TickRateModulation.IDLE;
} }
this.ticker(target);
return TickRateModulation.IDLE; return TickRateModulation.IDLE;
} }
@ -211,7 +263,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @param <T> 方块实体类型 * @param <T> 方块实体类型
*/ */
private <T extends BlockEntity> void ticker(@NotNull T blockEntity) { private <T extends BlockEntity> void ticker(@NotNull T blockEntity) {
if (!isValidForTicking()) { if (!this.isValidForTicking()) {
return; return;
} }
@ -220,22 +272,22 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
return; return;
} }
BlockEntityTicker<T> ticker = getTicker(blockEntity); BlockEntityTicker<T> ticker = this.getTicker(blockEntity);
if (ticker == null) { if (ticker == null) {
return; return;
} }
int speed = calculateSpeed(); int speed = this.calculateSpeed();
if (speed <= 0) { if (speed <= 0) {
return; return;
} }
double requiredPower = calculateRequiredPower(speed, blockId); double requiredPower = this.calculateRequiredPower(speed, blockId);
if (!extractPower(requiredPower)) { if (!this.extractPower(requiredPower)) {
return; return;
} }
performTicks(blockEntity, ticker, speed); this.performTicks(blockEntity, ticker, speed);
} }
/** /**
@ -244,7 +296,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @return 是否可以执行 tick * @return 是否可以执行 tick
*/ */
private boolean isValidForTicking() { private boolean isValidForTicking() {
return getGridNode() != null && getMainNode() != null && getMainNode().getGrid() != null; return this.getGridNode() != null && this.getMainNode() != null && this.getMainNode().getGrid() != null;
} }
/** /**
@ -254,8 +306,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @return ticker null * @return ticker null
*/ */
private <T extends BlockEntity> BlockEntityTicker<T> getTicker(T blockEntity) { private <T extends BlockEntity> BlockEntityTicker<T> getTicker(T blockEntity) {
return getLevel().getBlockState(blockEntity.getBlockPos()) return this.getLevel().getBlockState(blockEntity.getBlockPos())
.getTicker(getLevel(), (BlockEntityType<T>) blockEntity.getType()); .getTicker(this.getLevel(), (BlockEntityType<T>) blockEntity.getType());
} }
/** /**
@ -264,9 +316,9 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @return 生效的加速倍率 * @return 生效的加速倍率
*/ */
private int calculateSpeed() { private int calculateSpeed() {
int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); int entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
if (entitySpeedCardCount <= 0) return 0; if (entitySpeedCardCount <= 0) return 0;
return (int) PowerUtils.computeProductWithCap(getUpgrades(), 8); return (int) PowerUtils.computeProductWithCap(this.getUpgrades(), 8);
} }
/** /**
@ -277,7 +329,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @return 所需能量 * @return 所需能量
*/ */
private double calculateRequiredPower(int speed, String blockId) { private double calculateRequiredPower(int speed, String blockId) {
int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); int energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
double multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get()); double multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get());
return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier; return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier;
} }
@ -289,15 +341,15 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @return 是否成功提取足够能量 * @return 是否成功提取足够能量
*/ */
private boolean extractPower(double requiredPower) { private boolean extractPower(double requiredPower) {
IEnergyService energyService = getMainNode().getGrid().getEnergyService(); IEnergyService energyService = this.getMainNode().getGrid().getEnergyService();
MEStorage storage = getMainNode().getGrid().getStorageService().getInventory(); MEStorage storage = this.getMainNode().getGrid().getStorageService().getInventory();
IActionSource source = IActionSource.ofMachine(this); IActionSource source = IActionSource.ofMachine(this);
boolean appFluxLoaded = ModList.get().isLoaded("appflux"); boolean appFluxLoaded = ModList.get().isLoaded("appflux");
boolean preferDiskEnergy = appFluxLoaded && ModConfigs.PRIORITIZE_DISK_ENERGY.get(); boolean preferDiskEnergy = appFluxLoaded && ModConfigs.PRIORITIZE_DISK_ENERGY.get();
// 如果 appflux 存在且优先磁盘能量尝试提取 FE 能量 // 如果 appflux 存在且优先磁盘能量尝试提取 FE 能量
if (appFluxLoaded && preferDiskEnergy) { if (appFluxLoaded && preferDiskEnergy) {
if (tryExtractFE(energyService, storage, requiredPower, source)) { if (this.tryExtractFE(energyService, storage, requiredPower, source)) {
return true; return true;
} }
} }
@ -307,14 +359,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
if (simulated >= requiredPower) { if (simulated >= requiredPower) {
double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
boolean sufficient = extracted >= requiredPower; boolean sufficient = extracted >= requiredPower;
setNetworkEnergySufficient(sufficient); this.setNetworkEnergySufficient(sufficient);
return sufficient; return sufficient;
} }
setNetworkEnergySufficient(false); this.setNetworkEnergySufficient(false);
// 如果 appflux 存在且优先 AE 能量尝试提取 FE 能量作为备用 // 如果 appflux 存在且优先 AE 能量尝试提取 FE 能量作为备用
if (appFluxLoaded && !preferDiskEnergy) { if (appFluxLoaded && !preferDiskEnergy) {
return tryExtractFE(energyService, storage, requiredPower, source); return this.tryExtractFE(energyService, storage, requiredPower, source);
} }
return false; return false;
} }
@ -332,13 +384,13 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE
long feExtracted = (long) extractMethod.invoke(null, energyService, storage, feRequired, source); long feExtracted = (long) extractMethod.invoke(null, energyService, storage, feRequired, source);
if (feExtracted >= feRequired) { if (feExtracted >= feRequired) {
setNetworkEnergySufficient(true); this.setNetworkEnergySufficient(true);
return true; return true;
} }
} catch (Exception e) { } catch (Exception e) {
// 如果反射失败视为 FE 不可用 // 如果反射失败视为 FE 不可用
} }
setNetworkEnergySufficient(false); this.setNetworkEnergySufficient(false);
return false; return false;
} }
@ -354,12 +406,41 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
int speed) { int speed) {
// 执行 speed-1 次额外 tick原生 tick 已包含 1 // 执行 speed-1 次额外 tick原生 tick 已包含 1
for (int i = 0; i < speed - 1; i++) { for (int i = 0; i < speed - 1; i++) {
ticker.tick( try {
blockEntity.getLevel(), ticker.tick(
blockEntity.getBlockPos(), blockEntity.getLevel(),
blockEntity.getBlockState(), blockEntity.getBlockPos(),
blockEntity blockEntity.getBlockState(),
); blockEntity
);
} catch (IllegalStateException e) {
// 捕获随机数生成器的多线程访问异常
// 这通常发生在某些模组 Thermal的机器使用随机数时
// 由于加速导致在同一tick内多次访问随机数生成器而触发 ThreadingDetector
if (e.getMessage() != null && e.getMessage().contains("LegacyRandomSource")) {
// 记录警告并停止当前加速循环避免崩溃
ExtendedAELogger.LOGGER.warn(
"检测到方块实体 {} 在位置 {} 的随机数访问冲突,已停止本次加速以避免崩溃。" +
"建议将此方块类型添加到配置黑名单中。",
blockEntity.getType().toString(),
blockEntity.getBlockPos()
);
break; // 停止后续的加速 tick
} else {
// 如果是其他类型的 IllegalStateException继续抛出
throw e;
}
} catch (Exception e) {
// 捕获其他可能的异常防止崩溃
ExtendedAELogger.LOGGER.error(
"在加速方块实体 {} 位置 {} 时发生错误: {}",
blockEntity.getType().toString(),
blockEntity.getBlockPos(),
e.getMessage(),
e
);
break; // 停止后续的加速 tick
}
} }
} }
@ -397,46 +478,4 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
@NotNull Player player) { @NotNull Player player) {
return new EntitySpeedTickerMenu(containerId, playerInventory, this); return new EntitySpeedTickerMenu(containerId, playerInventory, this);
} }
/**
* 获取可用的升级卡槽数量
*
* @return 升级卡槽数量
*/
@Override
protected int getUpgradeSlots() {
return 8;
}
/**
* 当升级卡数量发生变化时调用通知菜单更新
*/
@Override
public void upgradesChanged() {
if (this.menu != null) {
// 使用 AE2 风格当升级发生变化时让菜单广播变化/数据会被同步客户端会基于槽内容重新计算并刷新界面
this.menu.broadcastChanges();
}
}
public IConfigManager getConfigManager() {
return this.configManager;
}
// 获取红石信号状态
private boolean getRedstoneState() {
// 每次调用都更新红石状态确保及时性
updateRedstoneState();
return redstoneState == YesNo.YES;
}
// 更新红石信号状态
private void updateRedstoneState() {
var be = this.getHost().getBlockEntity();
if (be != null && be.getLevel() != null) {
redstoneState = be.getLevel().hasNeighborSignal(be.getBlockPos())
? YesNo.YES
: YesNo.NO;
}
}
} }

View File

@ -11,16 +11,13 @@ import com.extendedae_plus.api.config.EAPSettings;
import com.extendedae_plus.client.gui.widgets.EAPServerSettingToggleButton; import com.extendedae_plus.client.gui.widgets.EAPServerSettingToggleButton;
import com.extendedae_plus.client.gui.widgets.EAPSettingToggleButton; import com.extendedae_plus.client.gui.widgets.EAPSettingToggleButton;
import com.extendedae_plus.util.entitySpeed.PowerUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils;
import net.minecraft.client.gui.GuiGraphics;
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.Map;
public class EntitySpeedTickerScreen extends UpgradeableScreen<EntitySpeedTickerMenu> { public class EntitySpeedTickerScreen extends UpgradeableScreen<EntitySpeedTickerMenu> {
private final EAPSettingToggleButton<YesNo> accelerateButton; // 加速开关按钮 private final EAPSettingToggleButton<YesNo> accelerateButton; // 加速开关按钮
private final EAPSettingToggleButton<YesNo> redstoneControlButton; // 加速开关按钮 private final EAPSettingToggleButton<YesNo> redstoneControlButton; // 加速开关按钮
private final TextUpdater textUpdater = new TextUpdater();
/** /**
* 构造函数初始化界面和控件 * 构造函数初始化界面和控件
@ -37,7 +34,7 @@ public class EntitySpeedTickerScreen extends UpgradeableScreen<EntitySpeedTicker
super(menu, playerInventory, title, style); super(menu, playerInventory, title, style);
this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮 this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮
this.accelerateButton = new EAPServerSettingToggleButton<>(EAPSettings.ACCELERATE, YesNo.NO); this.accelerateButton = new EAPServerSettingToggleButton<>(EAPSettings.ACCELERATE, YesNo.YES);
this.addToLeftToolbar(this.accelerateButton); this.addToLeftToolbar(this.accelerateButton);
this.redstoneControlButton = new EAPServerSettingToggleButton<>(EAPSettings.REDSTONE_CONTROL, YesNo.NO); this.redstoneControlButton = new EAPServerSettingToggleButton<>(EAPSettings.REDSTONE_CONTROL, YesNo.NO);
@ -54,47 +51,55 @@ public class EntitySpeedTickerScreen extends UpgradeableScreen<EntitySpeedTicker
} else { } else {
this.accelerateButton.set(this.menu.getAccelerate()); this.accelerateButton.set(this.menu.getAccelerate());
} }
this.redstoneControlButton.set(this.menu.getRedstoneControl()); this.redstoneControlButton.set(this.menu.getRedstoneControl());
this.textData(); // 文本更新统一处理
} this.textUpdater.update();
@Override
public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, float partialTicks) {
super.drawBG(guiGraphics, offsetX, offsetY, mouseX, mouseY, partialTicks);
} }
public void refreshGui() { public void refreshGui() {
this.textData(); this.textUpdater.update();
} }
/** private class TextUpdater {
* 更新界面文本内容包括加速状态速度能耗和倍率 void update() {
*/ if (EntitySpeedTickerScreen.this.menu.targetBlacklisted) {
private void textData() { this.updateBlacklist();
Map<String, Component> textContents = new HashMap<>(); } else {
if (this.getMenu().targetBlacklisted) { this.updateNormal();
// 黑名单禁用时的默认显示 }
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))); private void updateBlacklist() {
textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); this.set("enable", this.translatable("enable"));
textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); this.set("speed", this.translatable("speed", 0));
} else { this.set("energy", this.translatable("energy", Platform.formatPower(0, false)));
// 正常状态下显示实际数据 this.set("power_ratio", this.translatable("power_ratio", PowerUtils.formatPercentage(0.0)));
int energyCardCount = this.getMenu().energyCardCount; this.set("multiplier", this.translatable("multiplier", "0.00x"));
double multiplier = this.getMenu().multiplier; }
int effectiveSpeed = this.getMenu().effectiveSpeed;
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); private void updateNormal() {
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); int energyCardCount = EntitySpeedTickerScreen.this.menu.energyCardCount;
double multiplier = EntitySpeedTickerScreen.this.menu.multiplier;
textContents.put("enable", this.getMenu().networkEnergySufficient == YesNo.YES ? null : int effectiveSpeed = EntitySpeedTickerScreen.this.menu.effectiveSpeed;
Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed)); double powerRatio = PowerUtils.getRemainingRatio(energyCardCount);
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))); this.set("enable", EntitySpeedTickerScreen.this.menu.networkEnergySufficient == YesNo.YES
textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier))); ? null
: this.translatable("warning_network_energy_insufficient"));
this.set("speed", this.translatable("speed", effectiveSpeed));
this.set("energy", this.translatable("energy", Platform.formatPower(finalPower, false)));
this.set("power_ratio", this.translatable("power_ratio", PowerUtils.formatPercentage(powerRatio)));
this.set("multiplier", this.translatable("multiplier", String.format("%.2fx", multiplier)));
}
private Component translatable(String key, Object... args) {
return Component.translatable("screen.extendedae_plus.entity_speed_ticker." + key, args);
}
private void set(String id, Component c) {
EntitySpeedTickerScreen.this.setTextContent(id, c);
} }
textContents.forEach(this::setTextContent);
} }
} }

View File

@ -11,7 +11,7 @@
0, 0,
0, 0,
176, 176,
205 220
] ]
}, },
"text": { "text": {
@ -62,14 +62,14 @@
}, },
"widgets": { "widgets": {
"toolbox": { "toolbox": {
"right": 1, "right": 2,
"bottom": 55 "bottom": 66
} }
}, },
"slots": { "slots": {
"TOOLBOX": { "TOOLBOX": {
"right": 0, "right": 1,
"bottom": 50, "bottom": 60,
"grid": "BREAK_AFTER_3COLS" "grid": "BREAK_AFTER_3COLS"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,7 +1,190 @@
{ {
"parent": "ae2:item/cable_interface", "parent": "ae2:item/cable_interface",
"textures": { "textures": {
"sides": "extendedae_plus:part/entity_speed_ticker_sides",
"front": "extendedae_plus:part/entity_speed_ticker_font", "front": "extendedae_plus:part/entity_speed_ticker_font",
"back": "extendedae_plus:part/entity_speed_ticker_back" "back": "extendedae_plus:part/entity_speed_ticker_back",
"particle": "ae2:part/monitor_sides_status"
},
"elements": [
{
"from": [4, 4, 7],
"to": [12, 12, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"north": {"uv": [3, 3, 13, 13], "texture": "#front"},
"south": {"uv": [3, 3, 13, 13], "texture": "#back"}
}
},
{
"from": [5, 5, 10],
"to": [11, 11, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [12, 5, 13, 11], "texture": "#sides"},
"south": {"uv": [5, 5, 11, 11], "texture": "#back"},
"west": {"uv": [3, 5, 4, 11], "texture": "#sides"},
"up": {"uv": [5, 3, 11, 4], "texture": "#sides"},
"down": {"uv": [5, 12, 11, 13], "texture": "#sides"}
}
},
{
"from": [5, 5, 9],
"to": [11, 11, 10],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [13, 5, 14, 11], "texture": "#particle"},
"south": {"uv": [11, 5, 5, 11], "texture": "#back"},
"west": {"uv": [2, 5, 3, 11], "texture": "#particle"},
"up": {"uv": [5, 2, 11, 3], "texture": "#particle"},
"down": {"uv": [5, 13, 11, 14], "texture": "#particle"}
}
},
{
"from": [5, 5, 11],
"to": [6, 7, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"west": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [6, 10, 11],
"to": [7, 11, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [9, 5, 11],
"to": [10, 6, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"south": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"west": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"up": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"down": {"uv": [14, 10, 15, 11], "texture": "#particle"}
}
},
{
"from": [10, 5, 11],
"to": [11, 7, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"south": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"west": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [10, 9, 11],
"to": [11, 11, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"south": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"west": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [9, 10, 11],
"to": [10, 11, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"south": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"west": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [5, 9, 11],
"to": [6, 11, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"west": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [6, 5, 11],
"to": [7, 6, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"east": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [3, 4, 7],
"to": [4, 12, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"north": {"uv": [2, 4, 3, 12], "texture": "#front"},
"south": {"uv": [2, 4, 3, 12], "texture": "#back"},
"west": {"uv": [0, 4, 2, 12], "texture": "#sides"},
"up": {"uv": [2, 0, 3, 2], "texture": "#sides"},
"down": {"uv": [13, 14, 14, 16], "texture": "#sides"}
}
},
{
"from": [4, 12, 7],
"to": [12, 13, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"north": {"uv": [4, 2, 12, 3], "texture": "#front"},
"east": {"uv": [14, 2, 16, 3], "texture": "#sides"},
"south": {"uv": [4, 2, 12, 3], "texture": "#back"},
"west": {"uv": [0, 2, 2, 3], "texture": "#sides"},
"up": {"uv": [4, 0, 12, 2], "texture": "#sides"}
}
},
{
"from": [4, 3, 7],
"to": [12, 4, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"north": {"uv": [4, 13, 12, 14], "texture": "#front"},
"east": {"uv": [14, 2, 16, 3], "texture": "#sides"},
"south": {"uv": [4, 13, 12, 14], "texture": "#back"},
"west": {"uv": [0, 13, 2, 14], "texture": "#sides"},
"down": {"uv": [4, 14, 12, 16], "texture": "#sides"}
}
},
{
"from": [12, 4, 7],
"to": [13, 12, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 7]},
"faces": {
"north": {"uv": [13, 4, 14, 12], "texture": "#front"},
"east": {"uv": [14, 4, 16, 12], "texture": "#sides"},
"south": {"uv": [13, 4, 14, 12], "texture": "#back"},
"up": {"uv": [2, 0, 3, 2], "texture": "#sides"},
"down": {"uv": [2, 14, 3, 16], "texture": "#sides"}
}
}
],
"display": {
"gui": {
"rotation": [20, -130, 0],
"scale": [0.95, 0.95, 0.95]
}
} }
} }

View File

@ -1,9 +1,181 @@
{ {
"parent": "ae2:part/interface_base", "parent": "ae2:item/cable_interface",
"textures": { "textures": {
"front": "extendedae_plus:part/entity_speed_ticker_font",
"sides": "extendedae_plus:part/entity_speed_ticker_sides", "sides": "extendedae_plus:part/entity_speed_ticker_sides",
"front": "extendedae_plus:part/entity_speed_ticker_font",
"back": "extendedae_plus:part/entity_speed_ticker_back", "back": "extendedae_plus:part/entity_speed_ticker_back",
"particle": "extendedae_plus:part/entity_speed_ticker_back" "particle": "ae2:part/monitor_sides_status"
} },
"elements": [
{
"from": [4, 4, 0],
"to": [12, 12, 2],
"faces": {
"north": {"uv": [3, 3, 13, 13], "texture": "#front"},
"south": {"uv": [3, 3, 13, 13], "texture": "#back"}
}
},
{
"from": [5, 5, 3],
"to": [11, 11, 4],
"faces": {
"east": {"uv": [12, 5, 13, 11], "texture": "#sides"},
"south": {"uv": [5, 5, 11, 11], "texture": "#back"},
"west": {"uv": [3, 5, 4, 11], "texture": "#sides"},
"up": {"uv": [5, 3, 11, 4], "texture": "#sides"},
"down": {"uv": [5, 12, 11, 13], "texture": "#sides"}
}
},
{
"from": [5, 5, 2],
"to": [11, 11, 3],
"faces": {
"east": {"uv": [13, 5, 14, 11], "texture": "#particle"},
"south": {"uv": [11, 5, 5, 11], "texture": "#back"},
"west": {"uv": [2, 5, 3, 11], "texture": "#particle"},
"up": {"uv": [5, 2, 11, 3], "texture": "#particle"},
"down": {"uv": [5, 13, 11, 14], "texture": "#particle"}
}
},
{
"from": [5, 5, 4],
"to": [6, 7, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 5, 3]},
"faces": {
"east": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"west": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [6, 10, 4],
"to": [7, 11, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [6, 10, 3]},
"faces": {
"east": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [9, 5, 4],
"to": [10, 6, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 5, 3]},
"faces": {
"south": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"west": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"up": {"uv": [14, 9, 15, 10], "texture": "#particle"},
"down": {"uv": [14, 10, 15, 11], "texture": "#particle"}
}
},
{
"from": [10, 5, 4],
"to": [11, 7, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 5, 3]},
"faces": {
"east": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"south": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"west": {"uv": [14, 9, 15, 11], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [10, 9, 4],
"to": [11, 11, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 9, 3]},
"faces": {
"east": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"south": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"west": {"uv": [14, 5, 15, 7], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [9, 10, 4],
"to": [10, 11, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [9, 10, 3]},
"faces": {
"south": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"west": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"up": {"uv": [14, 5, 15, 6], "texture": "#particle"},
"down": {"uv": [14, 6, 15, 7], "texture": "#particle"}
}
},
{
"from": [5, 9, 4],
"to": [6, 11, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 9, 3]},
"faces": {
"east": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"west": {"uv": [1, 5, 2, 7], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [6, 5, 4],
"to": [7, 6, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [6, 5, 3]},
"faces": {
"east": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"south": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"up": {"uv": [1, 5, 2, 6], "texture": "#particle"},
"down": {"uv": [1, 5, 2, 6], "texture": "#particle"}
}
},
{
"from": [3, 4, 0],
"to": [4, 12, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [2, 5, 0]},
"faces": {
"north": {"uv": [2, 4, 3, 12], "texture": "#front"},
"south": {"uv": [2, 4, 3, 12], "texture": "#back"},
"west": {"uv": [0, 4, 2, 12], "texture": "#sides"},
"up": {"uv": [2, 0, 3, 2], "texture": "#sides"},
"down": {"uv": [13, 14, 14, 16], "texture": "#sides"}
}
},
{
"from": [4, 12, 0],
"to": [12, 13, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 12, 0]},
"faces": {
"north": {"uv": [4, 2, 12, 3], "texture": "#front"},
"east": {"uv": [14, 2, 16, 3], "texture": "#sides"},
"south": {"uv": [4, 2, 12, 3], "texture": "#back"},
"west": {"uv": [0, 2, 2, 3], "texture": "#sides"},
"up": {"uv": [4, 0, 12, 2], "texture": "#sides"}
}
},
{
"from": [4, 3, 0],
"to": [12, 4, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [4, 2, 0]},
"faces": {
"north": {"uv": [4, 13, 12, 14], "texture": "#front"},
"east": {"uv": [14, 2, 16, 3], "texture": "#sides"},
"south": {"uv": [4, 13, 12, 14], "texture": "#back"},
"west": {"uv": [0, 13, 2, 14], "texture": "#sides"},
"down": {"uv": [4, 14, 12, 16], "texture": "#sides"}
}
},
{
"from": [12, 4, 0],
"to": [13, 12, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [12, 4, 0]},
"faces": {
"north": {"uv": [13, 4, 14, 12], "texture": "#front"},
"east": {"uv": [14, 4, 16, 12], "texture": "#sides"},
"south": {"uv": [13, 4, 14, 12], "texture": "#back"},
"up": {"uv": [2, 0, 3, 2], "texture": "#sides"},
"down": {"uv": [2, 14, 3, 16], "texture": "#sides"}
}
}
]
} }

View File

@ -5,30 +5,24 @@
# Find more information on toml format here: https://github.com/toml-lang/toml # Find more information on toml format here: https://github.com/toml-lang/toml
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
modLoader="javafml" #mandatory modLoader = "javafml" #mandatory
# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. # A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2.
loaderVersion="${loader_version_range}" #mandatory loaderVersion = "${loader_version_range}" #mandatory
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
license="${mod_license}" license = "${mod_license}"
# A URL to refer people to when problems occur with this mod # A URL to refer people to when problems occur with this mod
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
# A list of mods - how many allowed here is determined by the individual mod loader # A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory [[mods]]
modId = "${mod_id}"
# The modid of the mod version = "${mod_version}"
modId="${mod_id}" #mandatory displayName = "${mod_name}"
issueTrackerURL = "https://github.com/GaLicn/ExtendedAE_Plus/issues"
# The version number of the mod displayURL = "https://github.com/GaLicn/ExtendedAE_Plus"
version="${mod_version}" #mandatory
# A display name for the mod
displayName="${mod_name}" #mandatory
# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ # A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional #updateJSONURL="https://change.me.example.invalid/updates.json" #optional
@ -42,14 +36,14 @@ displayName="${mod_name}" #mandatory
#credits="" #optional #credits="" #optional
# A text field displayed in the mod UI # A text field displayed in the mod UI
authors="${mod_authors}" #optional authors = "${mod_authors}" #optional
# The description text for the mod (multi line!) (#mandatory) # The description text for the mod (multi line!) (#mandatory)
description='''${mod_description}''' description = '''${mod_description}'''
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. # The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
[[mixins]] [[mixins]]
config="${mod_id}.mixins.json" config = "${mod_id}.mixins.json"
# The [[accessTransformers]] block allows you to declare where your AT file is. # The [[accessTransformers]] block allows you to declare where your AT file is.
# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg # If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg
@ -59,41 +53,41 @@ config="${mod_id}.mixins.json"
# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json # The coremods config file path is not configurable and is always loaded from META-INF/coremods.json
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
[[dependencies.${mod_id}]] #optional [[dependencies.${ mod_id }]] #optional
# the modid of the dependency # the modid of the dependency
modId="neoforge" #mandatory modId = "neoforge" #mandatory
# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive).
# 'required' requires the mod to exist, 'optional' does not # 'required' requires the mod to exist, 'optional' does not
# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning # 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning
type="required" #mandatory type = "required" #mandatory
# Optional field describing why the dependency is required or why it is incompatible # Optional field describing why the dependency is required or why it is incompatible
# reason="..." # reason="..."
# The version range of the dependency # The version range of the dependency
versionRange="[21.1.1,)" #mandatory versionRange = "[21.1.1,)" #mandatory
# An ordering relationship for the dependency. # An ordering relationship for the dependency.
# BEFORE - This mod is loaded BEFORE the dependency # BEFORE - This mod is loaded BEFORE the dependency
# AFTER - This mod is loaded AFTER the dependency # AFTER - This mod is loaded AFTER the dependency
ordering="NONE" ordering = "NONE"
# Side this dependency is applied on - BOTH, CLIENT, or SERVER # Side this dependency is applied on - BOTH, CLIENT, or SERVER
side="BOTH" side = "BOTH"
# Here's another dependency # Here's another dependency
[[dependencies.${mod_id}]] [[dependencies.${ mod_id }]]
modId="minecraft" modId = "minecraft"
type="required" type = "required"
# This version range declares a minimum of the current minecraft version up to but not including the next major version # This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange="${minecraft_version_range}" versionRange = "${minecraft_version_range}"
ordering="NONE" ordering = "NONE"
side="BOTH" side = "BOTH"
# Require ExtendedAE (ExtendedAE-1.21-2.2.21-neoforge) to be present # Require ExtendedAE (ExtendedAE-1.21-2.2.21-neoforge) to be present
[[dependencies.${mod_id}]] [[dependencies.${ mod_id }]]
modId="extendedae" modId = "extendedae"
type="required" type = "required"
# Use a permissive range to tolerate upstream version string variations (e.g. 1.21-2.2.21-neoforge) # Use a permissive range to tolerate upstream version string variations (e.g. 1.21-2.2.21-neoforge)
versionRange="*" versionRange = "*"
ordering="AFTER" ordering = "AFTER"
side="BOTH" side = "BOTH"
# Features are specific properties of the game environment, that you may want to declare you require. This example declares # Features are specific properties of the game environment, that you may want to declare you require. This example declares
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't # that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't