diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index fa57422..4253b20 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -2,14 +2,18 @@ package com.extendedae_plus; import appeng.api.parts.IPart; import appeng.api.parts.PartModels; +import appeng.api.storage.StorageCells; import appeng.block.AEBaseEntityBlock; import appeng.blockentity.crafting.CraftingBlockEntity; import appeng.items.parts.PartModelsHelper; +import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; +import com.extendedae_plus.util.storage.InfinityStorageManager; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.block.Blocks; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; @@ -18,7 +22,10 @@ import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; // The value here should match an entry in the META-INF/neoforge.mods.toml file @@ -52,7 +59,8 @@ public class ExtendedAEPlus { // Note that this is necessary if and only if we want *this* class (ExtendedAEPlus) to respond directly to events. // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. NeoForge.EVENT_BUS.register(this); - + NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStarted); + NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStopped); // 注册配置:接入自定义的 ModConfigs modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC, "extendedae_plus-common.toml"); modContainer.registerConfig(ModConfig.Type.CLIENT, ModConfigs.CLIENT_SPEC, "extendedae_plus-client.toml"); @@ -69,6 +77,7 @@ public class ExtendedAEPlus { LOGGER.info("HELLO FROM COMMON SETUP"); // 示例日志,避免引用不存在的模板 Config 字段 LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); + StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE); // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { @@ -125,7 +134,28 @@ public class ExtendedAEPlus { }); } - // 移除示例 addCreative 事件,避免将示例物品加入原版标签 + @Nullable + private static InfinityStorageManager storageManager; + + @Nullable + private static MinecraftServer storageManagerServer; + + private static void onServerStarted(ServerStartedEvent event) { + storageManagerServer = event.getServer(); + storageManager = InfinityStorageManager.getInstance(event.getServer()); + } + + private static void onServerStopped(ServerStoppedEvent event) { + if (storageManagerServer == event.getServer()) { + storageManagerServer = null; + storageManager = null; + } + } + + @Nullable + public static InfinityStorageManager currentStorageManager() { + return storageManager; + } // You can use SubscribeEvent and let the Event Bus discover methods to call @SubscribeEvent diff --git a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java new file mode 100644 index 0000000..3b7aa46 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java @@ -0,0 +1,22 @@ +package com.extendedae_plus.ae.api.storage; + +import appeng.api.storage.cells.ICellHandler; +import appeng.api.storage.cells.ISaveProvider; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; +import net.minecraft.world.item.ItemStack; + +public class InfinityBigIntegerCellHandler implements ICellHandler { + + public static final InfinityBigIntegerCellHandler INSTANCE = new InfinityBigIntegerCellHandler(); + + @Override + public boolean isCell(ItemStack is) { + return is.getItem() instanceof InfinityBigIntegerCellItem; + } + + @Override + public InfinityBigIntegerCellInventory getCellInventory(ItemStack is, ISaveProvider container) { + return InfinityBigIntegerCellInventory.createInventory(is, container, ExtendedAEPlus.currentStorageManager()); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java new file mode 100644 index 0000000..675c41e --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java @@ -0,0 +1,414 @@ +package com.extendedae_plus.ae.api.storage; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.KeyCounter; +import appeng.api.storage.cells.CellState; +import appeng.api.storage.cells.ISaveProvider; +import appeng.api.storage.cells.StorageCell; +import appeng.core.AELog; +import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; +import com.extendedae_plus.util.storage.InfinityConstants; +import com.extendedae_plus.util.storage.InfinityDataStorage; +import com.extendedae_plus.util.storage.InfinityStorageManager; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import org.jetbrains.annotations.Nullable; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Objects; +import java.util.UUID; + +/** + * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.

+ * Original copyright (c) Technici4n

+ */ +public class InfinityBigIntegerCellInventory implements StorageCell { + private final InfinityBigIntegerCellItem cell; + // 磁盘本身 + private final ItemStack self; + @Nullable + private final InfinityStorageManager storageManager; + // AE2 提供的保存提供者,用于在容器中批量保存时触发回调 + private final ISaveProvider container; + // 存储物品键和数量的映射 + private Object2ObjectMap AEKey2AmountsMap; + // 存储的物品种类数量 + private int totalAEKeyType; + // 存储的物品总数 + private BigInteger totalAEKey2Amounts = BigInteger.ZERO; + // 标记是否已持久化到 SavedData + private boolean isPersisted = true; + + + public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell, + ItemStack stack, + ISaveProvider saveProvider, + @Nullable InfinityStorageManager storageManager) { + // 保存存储单元类型(InfinityBigIntegerCellItem 实例),用于访问磁盘属性 + this.cell = cell; + // 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据 + this.self = stack; + // 保存提供者,用于触发数据保存 + this.container = saveProvider; + // 初始化 storedAmounts 为 null,延迟加载物品数据 + this.AEKey2AmountsMap = null; + this.storageManager = storageManager; + // 初始化磁盘数据 + initData(); + } + + // 将 BigInteger 格式化为带单位的字符串,保留两位小数 + public static String formatBigInteger(BigInteger number) { + // 使用方法局部的 DecimalFormat,避免静态共享的非线程安全问题 + java.text.DecimalFormat df = new java.text.DecimalFormat("#.##"); + BigDecimal bd = new BigDecimal(number); + BigDecimal thousand = new BigDecimal(1000); + String[] units = new String[]{"", "K", "M", "G", "T", "P", "E", "Z", "Y"}; + int idx = 0; + while (bd.compareTo(thousand) >= 0 && idx < units.length - 1) { + bd = bd.divide(thousand, 2, RoundingMode.HALF_UP); + idx++; + } + if (idx == 0) { + return bd.setScale(0, RoundingMode.DOWN).toPlainString(); + } + return df.format(bd.doubleValue()) + units[idx]; + } + + // 静态方法,创建存储单元库存 + public static InfinityBigIntegerCellInventory createInventory(ItemStack stack, + ISaveProvider saveProvider, + @Nullable InfinityStorageManager storageManager) { + // 检查物品堆栈是否为空 + Objects.requireNonNull(stack, "Cannot create cell inventory for null itemstack"); + // 检查物品是否为 IDISKCellItem 类型 + if (!(stack.getItem() instanceof InfinityBigIntegerCellItem cell)) { + return null; + } + // 创建并返回新的 DISKCellInventory 实例 + return new InfinityBigIntegerCellInventory(cell, stack, saveProvider, storageManager); + } + + // 获取磁盘的 InfinityDataStorage 数据 + private InfinityDataStorage getCellStorage() { + // 如果磁盘有 UUID,返回对应的 InfinityDataStorage + if (getUUID() != null && this.storageManager != null) { + return storageManager.getOrCreateCell(getUUID()); + } else { + // 否则返回空的 InfinityDataStorage + return InfinityDataStorage.EMPTY; + } + } + + // 初始化磁盘数据 + private void initData() { + // 如果磁盘有 UUID,加载存储的物品数据 + if (hasUUID()) { + this.totalAEKeyType = getCellStorage().amounts.size(); + this.totalAEKey2Amounts = getCellStorage().itemCount.equals(BigInteger.ZERO) ? + BigInteger.ZERO : + getCellStorage().itemCount; + + } else { + // 否则初始化为空 + this.totalAEKeyType = 0; + this.totalAEKey2Amounts = BigInteger.ZERO; + // 加载物品数据 + getCellStoredMap(); + } + } + + // 获取存储单元的状态(空、部分填充) + @Override + public CellState getStatus() { + // 如果没有存储任何物品,返回空状态 + if (this.getTotalAEKey2Amounts().equals(BigInteger.ZERO)) { + return CellState.EMPTY; + } + // 否则返回满状态 + return CellState.NOT_EMPTY; + } + + // 获取存储单元的待机能耗 + @Override + public double getIdleDrain() { + return 512; + } + + // 持久化存储单元数据到全局存储 + @Override + public void persist() { + if (this.isPersisted || this.storageManager == null) + return; + + if (this.totalAEKey2Amounts.equals(BigInteger.ZERO)) { + if (hasUUID()) { + this.storageManager.removeCell(getUUID()); + // 从 DataComponents.CUSTOM_DATA 里移除对应字段 + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + CompoundTag tag = data.copyTag(); + tag.remove(InfinityConstants.INFINITY_CELL_UUID); + tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL); + tag.remove(InfinityConstants.INFINITY_ITEM_TYPES); + // backward compat + tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT); + + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + + initData(); + } + return; + } + + // 创建物品键列表 + ListTag keys = new ListTag(); + // 创建物品数量列表 + ListTag amounts = new ListTag(); + // 初始化物品总数 + BigInteger itemCount = BigInteger.ZERO; + + for (var entry : this.AEKey2AmountsMap.object2ObjectEntrySet()) { + BigInteger amount = entry.getValue(); + // 如果数量大于 0,添加到键和数量列表 + if (amount.compareTo(BigInteger.ZERO) > 0) { + keys.add(entry.getKey().toTagGeneric(this.storageManager.getRegistries())); + CompoundTag amountTag = new CompoundTag(); + amountTag.putByteArray("value", amount.toByteArray()); + amounts.add(amountTag); + + itemCount = itemCount.add(amount); + } + } + + if (keys.isEmpty()) { + this.storageManager.updateCell(getUUID(), new InfinityDataStorage()); + } else { + this.storageManager.modifyDisk(getUUID(), keys, amounts, itemCount); + } + + // 更新存储的物品种类数量 + this.totalAEKeyType = this.AEKey2AmountsMap.size(); + // 更新存储的物品总数 + this.totalAEKey2Amounts = itemCount; + // 将物品总数与种类数量存入物品堆栈的 NBT(用于快捷查看/tooltip),同时保留旧字段以兼容历史版本 + + // 写回 DataComponents.CUSTOM_DATA(替代 getOrCreateTag) + CompoundTag tag = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCount.toByteArray()); + tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType); + tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray()); + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + // 标记数据已持久化 + this.isPersisted = true; + } + + // 获取存储单元的描述(此处返回null,可自定义) + @Override + public Component getDescription() { + return null; + } + + // 获取存储的物品总数 + public BigInteger getTotalAEKey2Amounts() { + return this.totalAEKey2Amounts; + } + + // 获取存储的物品种类数量 + public int getTotalAEKeyType() { + return this.totalAEKeyType; + } + + // 判断物品堆栈是否有UUID + public boolean hasUUID() { + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + return !data.isEmpty() && data.copyTag().contains(InfinityConstants.INFINITY_CELL_UUID); + } + + // 获取物品堆栈的UUID + public UUID getUUID() { + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + if (!data.isEmpty() && data.copyTag().contains(InfinityConstants.INFINITY_CELL_UUID)) { + return data.copyTag().getUUID(InfinityConstants.INFINITY_CELL_UUID); + } + return null; + } + + // 获取或初始化存储映射 + private Object2ObjectMap getCellStoredMap() { + if (AEKey2AmountsMap == null) { + AEKey2AmountsMap = new Object2ObjectOpenHashMap<>(); + this.loadCellStoredMap(); + } + return AEKey2AmountsMap; + } + + // 获取所有可用的物品堆栈及其数量 + @Override + public void getAvailableStacks(KeyCounter out) { + BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); + if (this.getCellStoredMap() == null) return; + for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) { + AEKey key = entry.getKey(); + BigInteger value = entry.getValue(); + + // 获取 KeyCounter 中已有的值 + long existing = out.get(key); + + // 计算总和并限制到 Long.MAX_VALUE + BigInteger sum = BigInteger.valueOf(existing).add(value); + long toSet = sum.compareTo(maxLong) > 0 ? Long.MAX_VALUE : sum.longValue(); + // 更新 KeyCounter + if (existing == Long.MAX_VALUE) { + continue; + } + long delta = toSet - existing; + if (delta != 0) { + out.add(key, delta); + } + } + } + + + // 从存储中加载物品映射 + private void loadCellStoredMap() { + if (this.storageManager == null) { + return; + } + + boolean dataCorruption = false; + if (self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).isEmpty()) return; + + var keys = getCellStorage().keys; + var amounts = getCellStorage().amounts; + // 数据损坏 + if (keys.size() != amounts.size()) { + AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", amounts.size(), keys.size()); + } + + var registries = this.storageManager.getRegistries(); + + // 遍历数量和键,加载到 AEKey2AmountsMap + for (int i = 0; i < amounts.size(); i++) { + AEKey key = AEKey.fromTagGeneric(registries,keys.getCompound(i)); + BigInteger amount = new BigInteger(amounts.getCompound(i).getByteArray("value")); + // 检查数据是否损坏 + if (amount.compareTo(BigInteger.ZERO) <= 0 || key == null) { + dataCorruption = true; + } else { + AEKey2AmountsMap.put(key, amount); + } + } + if (dataCorruption) { + this.saveChanges(); + } + } + + // 标记数据需要保存,并通知容器或直接持久化 + private void saveChanges() { + // 更新存储的物品种类数量 + this.totalAEKeyType = this.AEKey2AmountsMap.size(); + // 重置物品总数 + this.totalAEKey2Amounts = BigInteger.ZERO; + // 计算物品总数 + for (BigInteger AEKey2Amounts : this.AEKey2AmountsMap.values()) { + this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(AEKey2Amounts); + } + // 标记数据未持久化 + this.isPersisted = false; + // 如果有保存提供者,通知保存 + if (this.container != null) { + this.container.saveChanges(); + } else { + // 否则立即持久化 + this.persist(); + } + } + + // 插入物品到存储单元 + @Override + public long insert(AEKey what, long amount, Actionable mode, IActionSource source) { + // 数量为0或类型不匹配直接返回 + if (amount == 0) { + return 0; + } + // 不允许存储无限单元自身 + if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { + return 0; + } + + // 如果没有 UUID,且服务器端存储管理器已就绪,则生成 UUID 并初始化存储 + if (storageManager != null && !this.hasUUID()) { + // 取出自定义 NBT(如果没有就返回空) + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + CompoundTag tag = data.copyTag(); + + // 生成新的 UUID 并写入 + UUID newUUID = UUID.randomUUID(); + tag.putUUID(InfinityConstants.INFINITY_CELL_UUID, newUUID); + + // 回写到 ItemStack + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + + // 初始化存储 + this.storageManager.getOrCreateCell(newUUID); + + // 加载已存储的映射 + loadCellStoredMap(); + } + // 获取当前物品数量 + BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO); + + if (mode == Actionable.MODULATE) { + // 实际插入,更新数量并保存 + BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount)); + getCellStoredMap().put(what, newAmount); + this.saveChanges(); + } + return amount; + } + + // 从存储单元提取物品 + @Override + public long extract(AEKey what, long amount, Actionable mode, IActionSource source) { + BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO); + // 如果有物品可提取 + if (currentAmount.compareTo(BigInteger.ZERO) > 0) { + + BigInteger requested = BigInteger.valueOf(amount); + + // 如果提取数量大于等于当前数量 + if (requested.compareTo(currentAmount) >= 0) { + if (mode == Actionable.MODULATE) { + getCellStoredMap().remove(what); + this.saveChanges(); + } + return currentAmount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 ? Long.MAX_VALUE : currentAmount.longValue(); + } else { + // 提取部分数量 + if (mode == Actionable.MODULATE) { + getCellStoredMap().put(what, currentAmount.subtract(requested)); + this.saveChanges(); + } + return requested.longValue(); + } + } + return 0; + } + + // 获取存储单元内所有物品的总数量(格式化字符串) + public String getTotalStorage() { + // 使用缓存的 totalStored,避免每次全表扫描 + return formatBigInteger(totalAEKey2Amounts); + } +} diff --git a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java new file mode 100644 index 0000000..2ae9fb0 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java @@ -0,0 +1,88 @@ +package com.extendedae_plus.ae.items; + +import appeng.api.config.FuzzyMode; +import appeng.api.storage.cells.ICellWorkbenchItem; +import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory; +import com.extendedae_plus.util.storage.InfinityConstants; +import com.google.common.base.Preconditions; +import net.minecraft.ChatFormatting; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.CustomData; + +import java.math.BigInteger; +import java.util.List; + +public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchItem { + + public InfinityBigIntegerCellItem() { + super(new Properties().stacksTo(1).fireResistant()); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag tooltipFlag) { + tooltip.add(Component.translatable("tooltip.extendedae_plus.infinity_biginteger_cell.summon1")); + tooltip.add(Component.translatable("tooltip.extendedae_plus.infinity_biginteger_cell.summon2")); + + Preconditions.checkArgument(stack.getItem() == this); + // 仅在 ItemStack 自身存在 UUID 时显示 UUID,避免触发持久化或加载逻辑 + CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + + if (!customData.isEmpty()) { + CompoundTag tag = customData.copyTag(); + + if (tag.contains(InfinityConstants.INFINITY_CELL_UUID)) { + String uuidStr = tag.getUUID(InfinityConstants.INFINITY_CELL_UUID).toString(); + tooltip.add( + Component.literal("UUID: ").withStyle(ChatFormatting.GRAY) + .append(Component.literal(uuidStr).withStyle(ChatFormatting.YELLOW)) + ); + } + + if (tag.contains(InfinityConstants.INFINITY_ITEM_TYPES)) { + try { + int types = tag.getInt(InfinityConstants.INFINITY_ITEM_TYPES); + tooltip.add( + Component.literal("Types: ").withStyle(ChatFormatting.GRAY) + .append(Component.literal(String.valueOf(types)).withStyle(ChatFormatting.GREEN)) + ); + } catch (Exception ignored) {} + } + + if (tag.contains(InfinityConstants.INFINITY_ITEM_TOTAL)) { + try { + byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_ITEM_TOTAL); + BigInteger total = new BigInteger(bytes); + String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total); + tooltip.add( + Component.literal("Total: ").withStyle(ChatFormatting.GRAY) + .append(Component.literal(formatted).withStyle(ChatFormatting.AQUA)) + ); + } catch (Exception ignored) {} + } else if (tag.contains(InfinityConstants.INFINITY_CELL_ITEM_COUNT)) { + try { + byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT); + BigInteger total = new BigInteger(bytes); + String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total); + tooltip.add( + Component.literal("Total: ").withStyle(ChatFormatting.GRAY) + .append(Component.literal(formatted).withStyle(ChatFormatting.AQUA)) + ); + } catch (Exception ignored) {} + } + } + } + + @Override + public FuzzyMode getFuzzyMode(ItemStack itemStack) { + return null; + } + + @Override + public void setFuzzyMode(ItemStack itemStack, FuzzyMode fuzzyMode) { + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java index e72e8ce..40e1e28 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -36,6 +36,28 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu partItem) { @@ -81,18 +75,42 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka .setIdlePowerUsage(1) .addService(IGridTickable.class, this); } + // 当前打开的菜单实例(如果有) + public EntitySpeedTickerMenu menu; + // 控制是否启用加速(默认启用) + private boolean accelerateEnabled = true; + // 标记网络中能量是否充足(用于 GUI 提示,默认充足) + private boolean networkEnergySufficient = true; public boolean getAccelerateEnabled() { return this.accelerateEnabled; } + public boolean isNetworkEnergySufficient() { + return this.networkEnergySufficient; + } + public void setAccelerateEnabled(boolean accelerateEnabled) { this.accelerateEnabled = accelerateEnabled; } + /** + * 更新网络能量充足标记并在菜单存在且状态变化时触发同步 + * @param sufficient 是否能量充足 + */ + private void updateNetworkEnergySufficient(boolean sufficient) { + // 保持部件内部状态一致(部件为权威来源) + this.networkEnergySufficient = sufficient; + if (this.menu != null) { + try { + // 使用菜单的封装方法更新并广播,以保持封装性 + this.menu.setNetworkEnergySufficient(sufficient); + } catch (Exception ignored) {} + } + } + /** * 获取当前状态下的静态模型(用于渲染) - * * @return 当前状态的模型 */ public IPartModel getStaticModels() { @@ -105,8 +123,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } } - - /** * 当玩家激活部件(右键)时调用,打开自定义菜单 * @@ -239,14 +255,21 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka requiredPower *= multiplier; - // 先模拟提取以检查网络中是否有足够能量,再真正抽取 +// 先模拟提取以检查网络中是否有足够能量,再真正抽取 double simulated = getMainNode().getGrid().getEnergyService() .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); - if (simulated < requiredPower) return; + if (simulated < requiredPower) { + updateNetworkEnergySufficient(false); // 能量不足 + return; + } double extractedPower = getMainNode().getGrid().getEnergyService() .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extractedPower < requiredPower) return; + if (extractedPower < requiredPower) { + updateNetworkEnergySufficient(false); // 能量不足 + return; + } + updateNetworkEnergySufficient(true); // 能量充足 // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) // 已由 product 计算得到 speed;上面已在没有卡时提前返回 diff --git a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java index 32421e2..30605bb 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -128,6 +128,13 @@ public class EntitySpeedTickerScreen extends Up double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + // 如果网络能量不足,优先显示警告信息并在能量值处显示 0 + if (!getMenu().networkEnergySufficient) { + setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); + }else { + setTextContent("enable", null); + } + setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed)); setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false))); setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio))); diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 77d059a..bc623a7 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -31,6 +31,8 @@ public final class ModCreativeTabs { output.accept(ModItems.createEntitySpeedCardStack((byte) 4)); output.accept(ModItems.createEntitySpeedCardStack((byte) 8)); output.accept(ModItems.createEntitySpeedCardStack((byte) 16)); + + output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get()); }) .build()); diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 5189c54..9e1eaa5 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -4,6 +4,7 @@ import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -69,6 +70,11 @@ public final class ModItems { () -> new ChannelCardItem(new Item.Properties()) ); + public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( + "infinity_biginteger_cell", InfinityBigIntegerCellItem::new + ); + + /** * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) */ diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java index 2136f1a..229c50c 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java @@ -14,7 +14,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen; import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; -import org.checkerframework.checker.units.qual.C; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -43,8 +42,8 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen cachedBlacklist = null; private static volatile List cachedMultiplierEntries = null; + private static volatile List cachedBlacklistSourceSnapshot = null; + private static volatile List cachedMultiplierSourceSnapshot = null; private static final Object CACHE_LOCK = new Object(); /** * 获取已解析并缓存的黑名单(线程安全、懒加载)。 */ - public static List getCachedBlacklist(List source) { - if (cachedBlacklist == null) { - synchronized (CACHE_LOCK) { - if (cachedBlacklist == null) { - cachedBlacklist = compilePatterns(source); - } - } - } else { - synchronized (CACHE_LOCK) { - List newCachedBlackList = compilePatterns(source); - if (!cachedBlacklist.equals(newCachedBlackList)) { - cachedBlacklist = newCachedBlackList; - } - } + public static List getCachedBlacklist(java.util.List source) { + List 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); + cachedBlacklistSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized); + } + return Collections.unmodifiableList(cachedBlacklist); } - return Collections.unmodifiableList(cachedBlacklist); } /** * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 */ - public static List getCachedMultiplierEntries(List source) { - if (cachedMultiplierEntries == null) { - synchronized (CACHE_LOCK) { - if (cachedMultiplierEntries == null) { - cachedMultiplierEntries = parseMultiplierList(source); - } - } - }else { - synchronized (CACHE_LOCK) { - List newCachedMultiplierEntries = parseMultiplierList(source); - if (!cachedMultiplierEntries.equals(newCachedMultiplierEntries)) { - cachedMultiplierEntries = newCachedMultiplierEntries; - } - } + public static List getCachedMultiplierEntries(java.util.List source) { + List normalized = normalizeSource(source); + + if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) { + return Collections.unmodifiableList(cachedMultiplierEntries); } - return Collections.unmodifiableList(cachedMultiplierEntries); + + synchronized (CACHE_LOCK) { + if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) { + cachedMultiplierEntries = parseMultiplierList(normalized); + cachedMultiplierSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized); + } + return Collections.unmodifiableList(cachedMultiplierEntries); + } + } + + // Normalize the incoming source list: trim entries, drop blanks, keep stable ordering + private static List normalizeSource(java.util.List source) { + List out = new ArrayList<>(); + if (source == null) return out; + for (String s : source) { + if (s == null) continue; + String t = s.trim(); + if (t.isEmpty()) continue; + out.add(t); + } + return out; + } + + // Null-safe equality for two lists of strings. Uses size + element equals. + private static boolean listEquals(List a, List b) { + if (a == b) return true; + if (a == null || b == null) return false; + if (a.size() != b.size()) return false; + for (int i = 0; i < a.size(); i++) { + if (!a.get(i).equals(b.get(i))) return false; + } + return true; } /** diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java b/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java new file mode 100644 index 0000000..1fcb598 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java @@ -0,0 +1,30 @@ +package com.extendedae_plus.util.storage; + +public interface InfinityConstants { + // 当前磁盘格式版本号,增加字段用于向后/向前兼容 + int FORMAT_VERSION = 2; + // 存储磁盘数据的格式版本号 + String FORMAT_VERSION_FIELD = "infinity_format_version"; + + // savedData 文件名常量 + String SAVE_FILE_NAME = "infinity_biginteger_cells"; + + // 磁盘的唯一标识符键名,存储在 ItemStack 和 InfinityStorageManager 中 + String INFINITY_CELL_UUID = "infinity_cell_uuid"; + // 单个磁盘的 InfinityDataStorage 数据键名 + String INFINITY_CELL_DATA = "infinity_cell_data"; + // 所有磁盘数据的列表键名,存储在 InfinityStorageManager 的 NBT 中 + String INFINITY_CELL_LIST = "infinity_cell_list"; + + // 磁盘中所有物品键的键名(ListTag of CompoundTag) + String INFINITY_CELL_KEYS = "infinity_cell_keys"; + // 磁盘中每种物品数量的键名(ListTag of CompoundTag,包含 "value") + String INFINITY_CELL_AMOUNTS = "infinity_cell_amounts"; + // 磁盘中所有物品的总数键名(ListTag,包含一个 CompoundTag 的 "value") + String INFINITY_CELL_ITEM_COUNT = "infinity_cell_item_count"; + + // ItemStack 的 NBT 中存储总物品数量的键名 + String INFINITY_ITEM_TOTAL = "infinity_item_total"; + // ItemStack 的 NBT 中存储物品种类数量的键名 + String INFINITY_ITEM_TYPES = "infinity_item_types"; +} diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java new file mode 100644 index 0000000..6462128 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java @@ -0,0 +1,48 @@ +package com.extendedae_plus.util.storage; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import java.math.BigInteger; + +/** + * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.

+ * Original copyright (c) Technici4n

+ */ +public class InfinityDataStorage { + // 定义一个静态常量 EMPTY,表示一个空的 DataStorage 实例,用于默认或占位场景 + public static final InfinityDataStorage EMPTY = new InfinityDataStorage(); + + public ListTag keys; + public ListTag amounts; + // 存储磁盘中物品的总数,使用 BigInteger 支持大容量 + public BigInteger itemCount; + + public InfinityDataStorage() { + this(new ListTag(), new ListTag(), BigInteger.ZERO); + } + + private InfinityDataStorage(ListTag keys, ListTag amounts, BigInteger itemCount) { + this.keys = keys; + this.amounts = amounts; + this.itemCount = itemCount; + } + + // 将 DataStorage 数据序列化为 NBT 格式 + public CompoundTag serializeNBT() { + CompoundTag nbt = new CompoundTag(); + nbt.put(InfinityConstants.INFINITY_CELL_KEYS, keys); + nbt.put(InfinityConstants.INFINITY_CELL_AMOUNTS, amounts); + nbt.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray()); + return nbt; + } + + // 从 NBT 数据反序列化创建 DataStorage 实例 + public static InfinityDataStorage loadFromNBT(CompoundTag nbt) { + ListTag keys = nbt.getList(InfinityConstants.INFINITY_CELL_KEYS, ListTag.TAG_COMPOUND); + ListTag amounts = nbt.getList(InfinityConstants.INFINITY_CELL_AMOUNTS, ListTag.TAG_COMPOUND); + BigInteger itemCount = new BigInteger(nbt.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT)); + // 使用加载的数据创建新的 DataStorage 实例 + return new InfinityDataStorage(keys, amounts, itemCount); + } +} diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java new file mode 100644 index 0000000..fabcfff --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java @@ -0,0 +1,151 @@ +package com.extendedae_plus.util.storage; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.saveddata.SavedData; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.math.BigInteger; +import java.util.*; + +/** + * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.

+ * Original copyright (c) Technici4n

+ */ +public class InfinityStorageManager extends SavedData { + private static final Factory FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt); + // 存储所有磁盘的Map,键为UUID,值为DataStorage对象 + private final Map cells; + @Nullable + private WeakReference registries; + + + // 构造方法,初始化磁盘Map + public InfinityStorageManager() { + cells = new HashMap<>(); + // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘 + this.setDirty(); + } + + // 私有构造方法,用于从已有Map创建StorageManager + private InfinityStorageManager(Map cells) { + // 确保使用已加载的数据 + this.cells = cells; + // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘 + this.setDirty(); + } + + + // 静态方法,从 NBT 数据反序列化创建 StorageManager 实例 + public static InfinityStorageManager readNbt(CompoundTag nbt, HolderLookup.Provider registries) { + // 读取格式版本,缺省视为 1(兼容旧档) + int version = nbt.contains(InfinityConstants.FORMAT_VERSION_FIELD) ? + nbt.getInt(InfinityConstants.FORMAT_VERSION_FIELD) : + 1; + + Map cells = new HashMap<>(); + // 从 NBT 中获取磁盘数据列表,指定类型为 CompoundTag(TAG_COMPOUND) + ListTag cellList = nbt.getList(InfinityConstants.INFINITY_CELL_LIST, CompoundTag.TAG_COMPOUND); + // 遍历 cellList 中的每个 CompoundTag + for (int i = 0; i < cellList.size(); i++) { + // 获取当前索引的 CompoundTag,表示单个磁盘的数据 + CompoundTag cell = cellList.getCompound(i); + // 从 CompoundTag 中读取 UUID 和 DataStorage 数据,并存入 cells 映射 + cells.put(cell.getUUID(InfinityConstants.INFINITY_CELL_UUID), InfinityDataStorage.loadFromNBT(cell.getCompound(InfinityConstants.INFINITY_CELL_DATA))); + } + // 使用加载的 cells 数据创建新的 StorageManager 实例 + return new InfinityStorageManager(cells); + } + + + @Override + public CompoundTag save(CompoundTag nbt, HolderLookup.Provider provider) { + // 将内存中的所有 cell 序列化为一个 ListTag + ListTag cellList = new ListTag(); + for (Map.Entry entry : cells.entrySet()) { + CompoundTag cell = new CompoundTag(); + cell.putUUID(InfinityConstants.INFINITY_CELL_UUID, entry.getKey()); + cell.put(InfinityConstants.INFINITY_CELL_DATA, entry.getValue().serializeNBT()); + cellList.add(cell); + } + nbt.put(InfinityConstants.INFINITY_CELL_LIST, cellList); + // 写入当前格式版本号,便于未来迁移与兼容判断 + nbt.putInt(InfinityConstants.FORMAT_VERSION_FIELD, InfinityConstants.FORMAT_VERSION); + return nbt; + } + + // 返回当前已加载的所有 UUID 的不可变视图,用于命令或调试用途 + public Set getAllLoadedUUIDs() { + return Collections.unmodifiableSet(cells.keySet()); + } + + // 更新或添加某个 UUID 对应的数据并标记为脏(需要保存) + public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) { + cells.put(uuid, infinityDataStorage); + // 标记数据为“脏”,确保修改后的数据会在下次保存时写入磁盘 + setDirty(); + } + + // 删除某个 UUID 的持久化记录并标记为脏 + public void removeCell(UUID uuid) { + cells.remove(uuid); + // 标记数据为“脏”,确保移除操作会在下次保存时反映到磁盘 + setDirty(); + } + + // 检查指定 UUID 是否存在于 disks 映射中 + public boolean hasUUID(UUID uuid) { + // 返回 cells 映射是否包含指定 UUID + return cells.containsKey(uuid); + } + + // 获取或创建某个 UUID 对应的数据容器 + public InfinityDataStorage getOrCreateCell(UUID uuid) { + // 检查 cells 映射中是否不存在指定 UUID + if (!cells.containsKey(uuid)) { + updateCell(uuid, new InfinityDataStorage()); + } + // 返回指定 UUID 对应的 DataStorage 对象 + return cells.get(uuid); + } + + // 修改指定 UUID 的磁盘数据,包括堆栈键、数量和总项目数 + public void modifyDisk(UUID uuid, ListTag keys, ListTag amounts, BigInteger itemCount) { + // 获取或创建指定 UUID 的 DataStorage 对象 + InfinityDataStorage cellToModify = getOrCreateCell(uuid); + if (keys != null && amounts != null) { + cellToModify.keys = keys; + cellToModify.amounts = amounts; + } + // 更新 DataStorage 的 itemCount 字段 + cellToModify.itemCount = itemCount; + // 将修改后的 DataStorage 对象更新到 cells 映射 + updateCell(uuid, cellToModify); + } + + + public static InfinityStorageManager getInstance(MinecraftServer server) { + ServerLevel world = server.getLevel(ServerLevel.OVERWORLD); + var manager = world.getDataStorage().computeIfAbsent(FACTORY, InfinityConstants.SAVE_FILE_NAME); + manager.registries = new WeakReference<>(server.registryAccess()); + return manager; + } + + public HolderLookup.Provider getRegistries() { + var r = this.registries; + if (r == null) { + throw new IllegalStateException("StorageManager was not initialized properly."); + } + + var registries = r.get(); + if (registries == null) { + throw new IllegalStateException("Using a StorageManager whose server was already closed"); + } + + return registries; + } +} diff --git a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json index aaeab6c..352842a 100644 --- a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -51,8 +51,7 @@ "left": 88 }, "align": "CENTER" - } - , + }, "multiplier": { "position": { "top": 80, diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index 764c9ae..7e9e34e 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -27,6 +27,9 @@ "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", "item.extendedae_plus.entity_speed_card.x8": "Entity Speed Card (x8)", "item.extendedae_plus.entity_speed_card.x16": "Entity Speed Card (x16)", + "item.extendedae_plus.infinity_biginteger_cell": "§4De§cvou§6rer §eof §aCo§bsmic §dSilence", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6Through ninefold sacrifice, the Void echoes§r—§8Iava, Lord of the Void§r, bestows upon thee this artifact", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b—§4A §dUni§cverse §eWith§ain §6A §bSin§5gle §9Point", "tooltip.extendedae_plus.entity_speed_card.multiplier": "Multiplier: %s", "tooltip.extendedae_plus.entity_speed_card.max": "Maximum Effect: %s Times", @@ -56,6 +59,7 @@ "screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power Ratio: %s", "screen.extendedae_plus.entity_speed_ticker.speed": "Current Acceleration Multiplier: %d", "screen.extendedae_plus.entity_speed_ticker.multiplier": "Additional Consumption Multiplier: %s", + "screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient": "§c§lInsufficient network energy", "item.extendedae_plus.entity_speed_ticker.tip.requirement": "Requires Entity Speed Card to Enable Acceleration", "item.extendedae_plus.entity_speed_ticker.tip.max": "Up to 1024x Acceleration", diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index e7cb381..f530b72 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -27,6 +27,9 @@ "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", + "item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰", "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", @@ -56,6 +59,7 @@ "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", + "screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient": "§c§l网络能量不足", "item.extendedae_plus.entity_speed_ticker.tip.requirement": "需要放入实体加速卡以启用加速", "item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速", diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png index adfcc54..fc3db82 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png index 043aa02..fdb99c9 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png index ab8775f..fe4451d 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png index dbfdae5..c73e9ac 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png and b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png differ diff --git a/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png b/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png index 5c47ca4..75e2465 100644 Binary files a/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png and b/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png differ