Merge remote-tracking branch 'ae/develop/neoforge_iava' into 1.21.1
# Conflicts: # src/main/java/com/extendedae_plus/init/ModItems.java
|
|
@ -2,14 +2,18 @@ package com.extendedae_plus;
|
||||||
|
|
||||||
import appeng.api.parts.IPart;
|
import appeng.api.parts.IPart;
|
||||||
import appeng.api.parts.PartModels;
|
import appeng.api.parts.PartModels;
|
||||||
|
import appeng.api.storage.StorageCells;
|
||||||
import appeng.block.AEBaseEntityBlock;
|
import appeng.block.AEBaseEntityBlock;
|
||||||
import appeng.blockentity.crafting.CraftingBlockEntity;
|
import appeng.blockentity.crafting.CraftingBlockEntity;
|
||||||
import appeng.items.parts.PartModelsHelper;
|
import appeng.items.parts.PartModelsHelper;
|
||||||
|
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
|
||||||
import com.extendedae_plus.config.ModConfigs;
|
import com.extendedae_plus.config.ModConfigs;
|
||||||
import com.extendedae_plus.init.*;
|
import com.extendedae_plus.init.*;
|
||||||
|
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.neoforged.bus.api.IEventBus;
|
import net.neoforged.bus.api.IEventBus;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
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.config.ModConfig;
|
||||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||||
import net.neoforged.neoforge.common.NeoForge;
|
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.ServerStartingEvent;
|
||||||
|
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
// The value here should match an entry in the META-INF/neoforge.mods.toml file
|
// 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.
|
// 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.
|
// 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.register(this);
|
||||||
|
NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStarted);
|
||||||
|
NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStopped);
|
||||||
// 注册配置:接入自定义的 ModConfigs
|
// 注册配置:接入自定义的 ModConfigs
|
||||||
modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC, "extendedae_plus-common.toml");
|
modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC, "extendedae_plus-common.toml");
|
||||||
modContainer.registerConfig(ModConfig.Type.CLIENT, ModConfigs.CLIENT_SPEC, "extendedae_plus-client.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");
|
LOGGER.info("HELLO FROM COMMON SETUP");
|
||||||
// 示例日志,避免引用不存在的模板 Config 字段
|
// 示例日志,避免引用不存在的模板 Config 字段
|
||||||
LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT));
|
LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT));
|
||||||
|
StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE);
|
||||||
|
|
||||||
// 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空
|
// 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空
|
||||||
event.enqueueWork(() -> {
|
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
|
// You can use SubscribeEvent and let the Event Bus discover methods to call
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.<p>
|
||||||
|
* Original copyright (c) Technici4n<p>
|
||||||
|
*/
|
||||||
|
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<AEKey, BigInteger> 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<AEKey, BigInteger> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Component> 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,28 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
||||||
public double multiplier = 1.0;
|
public double multiplier = 1.0;
|
||||||
@GuiSync(721)
|
@GuiSync(721)
|
||||||
public boolean targetBlacklisted = false;
|
public boolean targetBlacklisted = false;
|
||||||
|
// 来自部件的网络能量充足提示(服务端设置,客户端用于显示警告)
|
||||||
|
@GuiSync(722) public boolean networkEnergySufficient = true;
|
||||||
|
|
||||||
|
|
||||||
|
public boolean getAccelerateEnabled() {
|
||||||
|
return this.accelerateEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccelerateEnabled(boolean enabled) {
|
||||||
|
this.accelerateEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Part 更新 networkEnergySufficient 的封装方法(由服务器调用)
|
||||||
|
* 该方法会更新 @GuiSync 字段并广播变化到客户端
|
||||||
|
*/
|
||||||
|
public void setNetworkEnergySufficient(boolean sufficient) {
|
||||||
|
this.networkEnergySufficient = sufficient;
|
||||||
|
// 触发一次数据广播,使客户端立即接收到最新状态
|
||||||
|
this.broadcastChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 构造方法,初始化菜单并与部件绑定
|
// 构造方法,初始化菜单并与部件绑定
|
||||||
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
|
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
|
||||||
|
|
@ -45,16 +67,7 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
||||||
// 初始同步部件上的开关状态到菜单(服务器端构造时保证一致)
|
// 初始同步部件上的开关状态到菜单(服务器端构造时保证一致)
|
||||||
try {
|
try {
|
||||||
this.accelerateEnabled = getHost().getAccelerateEnabled();
|
this.accelerateEnabled = getHost().getAccelerateEnabled();
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getAccelerateEnabled() {
|
|
||||||
return this.accelerateEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAccelerateEnabled(boolean enabled) {
|
|
||||||
this.accelerateEnabled = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -79,7 +92,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
this.multiplier = mult;
|
this.multiplier = mult;
|
||||||
|
|
||||||
// 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算)
|
// 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算)
|
||||||
|
|
@ -111,6 +125,14 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
|
||||||
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
|
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从部件同步网络能量充足状态:仅在服务器端从部件读取,客户端应使用由 @GuiSync 同步过来的值
|
||||||
|
try {
|
||||||
|
EntitySpeedTickerPart host = getHost();
|
||||||
|
if (host != null && !isClientSide()) {
|
||||||
|
this.networkEnergySufficient = host.isNetworkEnergySufficient();
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
// 如果在客户端,刷新界面
|
// 如果在客户端,刷新界面
|
||||||
if (isClientSide()) {
|
if (isClientSide()) {
|
||||||
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
||||||
|
|
|
||||||
|
|
@ -63,14 +63,8 @@ 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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前打开的菜单实例(如果有)
|
|
||||||
public EntitySpeedTickerMenu menu;
|
|
||||||
// 控制是否启用加速(默认启用)
|
|
||||||
private boolean accelerateEnabled = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数,初始化部件并设置网络节点属性
|
* 构造函数,初始化部件并设置网络节点属性
|
||||||
*
|
|
||||||
* @param partItem 部件物品
|
* @param partItem 部件物品
|
||||||
*/
|
*/
|
||||||
public EntitySpeedTickerPart(IPartItem<?> partItem) {
|
public EntitySpeedTickerPart(IPartItem<?> partItem) {
|
||||||
|
|
@ -81,18 +75,42 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
||||||
.setIdlePowerUsage(1)
|
.setIdlePowerUsage(1)
|
||||||
.addService(IGridTickable.class, this);
|
.addService(IGridTickable.class, this);
|
||||||
}
|
}
|
||||||
|
// 当前打开的菜单实例(如果有)
|
||||||
|
public EntitySpeedTickerMenu menu;
|
||||||
|
// 控制是否启用加速(默认启用)
|
||||||
|
private boolean accelerateEnabled = true;
|
||||||
|
// 标记网络中能量是否充足(用于 GUI 提示,默认充足)
|
||||||
|
private boolean networkEnergySufficient = true;
|
||||||
|
|
||||||
public boolean getAccelerateEnabled() {
|
public boolean getAccelerateEnabled() {
|
||||||
return this.accelerateEnabled;
|
return this.accelerateEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNetworkEnergySufficient() {
|
||||||
|
return this.networkEnergySufficient;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAccelerateEnabled(boolean accelerateEnabled) {
|
public void setAccelerateEnabled(boolean accelerateEnabled) {
|
||||||
this.accelerateEnabled = 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 当前状态的模型
|
* @return 当前状态的模型
|
||||||
*/
|
*/
|
||||||
public IPartModel getStaticModels() {
|
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;
|
requiredPower *= multiplier;
|
||||||
|
|
||||||
// 先模拟提取以检查网络中是否有足够能量,再真正抽取
|
// 先模拟提取以检查网络中是否有足够能量,再真正抽取
|
||||||
double simulated = getMainNode().getGrid().getEnergyService()
|
double simulated = getMainNode().getGrid().getEnergyService()
|
||||||
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
||||||
if (simulated < requiredPower) return;
|
if (simulated < requiredPower) {
|
||||||
|
updateNetworkEnergySufficient(false); // 能量不足
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
double extractedPower = getMainNode().getGrid().getEnergyService()
|
double extractedPower = getMainNode().getGrid().getEnergyService()
|
||||||
.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
||||||
if (extractedPower < requiredPower) return;
|
if (extractedPower < requiredPower) {
|
||||||
|
updateNetworkEnergySufficient(false); // 能量不足
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateNetworkEnergySufficient(true); // 能量充足
|
||||||
|
|
||||||
// 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10)
|
// 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10)
|
||||||
// 已由 product 计算得到 speed;上面已在没有卡时提前返回
|
// 已由 product 计算得到 speed;上面已在没有卡时提前返回
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,13 @@ public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends Up
|
||||||
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
|
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
|
||||||
double remainingRatio = PowerUtils.getRemainingRatio(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("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("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
|
||||||
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
|
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ public final class ModCreativeTabs {
|
||||||
output.accept(ModItems.createEntitySpeedCardStack((byte) 4));
|
output.accept(ModItems.createEntitySpeedCardStack((byte) 4));
|
||||||
output.accept(ModItems.createEntitySpeedCardStack((byte) 8));
|
output.accept(ModItems.createEntitySpeedCardStack((byte) 8));
|
||||||
output.accept(ModItems.createEntitySpeedCardStack((byte) 16));
|
output.accept(ModItems.createEntitySpeedCardStack((byte) 16));
|
||||||
|
|
||||||
|
output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get());
|
||||||
})
|
})
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.extendedae_plus.ExtendedAEPlus;
|
||||||
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
|
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
|
||||||
import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem;
|
import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem;
|
||||||
import com.extendedae_plus.ae.items.ChannelCardItem;
|
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.BlockItem;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
@ -69,6 +70,11 @@ public final class ModItems {
|
||||||
() -> new ChannelCardItem(new Item.Properties())
|
() -> new ChannelCardItem(new Item.Properties())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static final DeferredItem<Item> INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register(
|
||||||
|
"infinity_biginteger_cell", InfinityBigIntegerCellItem::new
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16)
|
* 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen;
|
import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen;
|
||||||
import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu;
|
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.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -43,8 +42,8 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen<AdvPatt
|
||||||
@Unique
|
@Unique
|
||||||
private boolean eap$SmartDoublingEnabled = false;
|
private boolean eap$SmartDoublingEnabled = false;
|
||||||
|
|
||||||
public AdvPatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public AdvPatternProviderScreenMixin(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super((AdvPatternProviderMenu) menu, playerInventory, title, style);
|
super(menu, playerInventory, title, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.pedroksl.advanced_ae.client.gui.SmallAdvPatternProviderScreen;
|
import net.pedroksl.advanced_ae.client.gui.SmallAdvPatternProviderScreen;
|
||||||
import net.pedroksl.advanced_ae.gui.advpatternprovider.SmallAdvPatternProviderMenu;
|
import net.pedroksl.advanced_ae.gui.advpatternprovider.SmallAdvPatternProviderMenu;
|
||||||
import org.checkerframework.checker.units.qual.C;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -43,8 +42,8 @@ public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen<Sm
|
||||||
@Unique
|
@Unique
|
||||||
private boolean eap$SmartDoublingEnabled = false;
|
private boolean eap$SmartDoublingEnabled = false;
|
||||||
|
|
||||||
public SmallAdvPatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public SmallAdvPatternProviderScreenMixin(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super((SmallAdvPatternProviderMenu) menu, playerInventory, title, style);
|
super(menu, playerInventory, title, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
|
|
||||||
|
|
@ -114,48 +114,71 @@ public final class ConfigParsingUtils {
|
||||||
// ------------------ 全局缓存与接口 ------------------
|
// ------------------ 全局缓存与接口 ------------------
|
||||||
private static volatile List<Pattern> cachedBlacklist = null;
|
private static volatile List<Pattern> cachedBlacklist = null;
|
||||||
private static volatile List<MultiplierEntry> cachedMultiplierEntries = null;
|
private static volatile List<MultiplierEntry> cachedMultiplierEntries = null;
|
||||||
|
private static volatile List<String> cachedBlacklistSourceSnapshot = null;
|
||||||
|
private static volatile List<String> cachedMultiplierSourceSnapshot = null;
|
||||||
private static final Object CACHE_LOCK = new Object();
|
private static final Object CACHE_LOCK = new Object();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已解析并缓存的黑名单(线程安全、懒加载)。
|
* 获取已解析并缓存的黑名单(线程安全、懒加载)。
|
||||||
*/
|
*/
|
||||||
public static List<Pattern> getCachedBlacklist(List<? extends String> source) {
|
public static List<Pattern> getCachedBlacklist(java.util.List<? extends String> source) {
|
||||||
if (cachedBlacklist == null) {
|
List<String> normalized = normalizeSource(source);
|
||||||
synchronized (CACHE_LOCK) {
|
|
||||||
if (cachedBlacklist == null) {
|
// fast path: identical snapshot reference or equal contents
|
||||||
cachedBlacklist = compilePatterns(source);
|
if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) {
|
||||||
}
|
return Collections.unmodifiableList(cachedBlacklist);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
synchronized (CACHE_LOCK) {
|
synchronized (CACHE_LOCK) {
|
||||||
List<Pattern> newCachedBlackList = compilePatterns(source);
|
if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) {
|
||||||
if (!cachedBlacklist.equals(newCachedBlackList)) {
|
cachedBlacklist = compilePatterns(normalized);
|
||||||
cachedBlacklist = newCachedBlackList;
|
cachedBlacklistSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized);
|
||||||
}
|
}
|
||||||
}
|
return Collections.unmodifiableList(cachedBlacklist);
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(cachedBlacklist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已解析并缓存的倍率列表(线程安全、懒加载)。
|
* 获取已解析并缓存的倍率列表(线程安全、懒加载)。
|
||||||
*/
|
*/
|
||||||
public static List<MultiplierEntry> getCachedMultiplierEntries(List<? extends String> source) {
|
public static List<MultiplierEntry> getCachedMultiplierEntries(java.util.List<? extends String> source) {
|
||||||
if (cachedMultiplierEntries == null) {
|
List<String> normalized = normalizeSource(source);
|
||||||
synchronized (CACHE_LOCK) {
|
|
||||||
if (cachedMultiplierEntries == null) {
|
if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) {
|
||||||
cachedMultiplierEntries = parseMultiplierList(source);
|
return Collections.unmodifiableList(cachedMultiplierEntries);
|
||||||
}
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
synchronized (CACHE_LOCK) {
|
|
||||||
List<MultiplierEntry> newCachedMultiplierEntries = parseMultiplierList(source);
|
|
||||||
if (!cachedMultiplierEntries.equals(newCachedMultiplierEntries)) {
|
|
||||||
cachedMultiplierEntries = newCachedMultiplierEntries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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<String> normalizeSource(java.util.List<? extends String> source) {
|
||||||
|
List<String> 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<String> a, List<String> 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
@ -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.<p>
|
||||||
|
* Original copyright (c) Technici4n<p>
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.<p>
|
||||||
|
* Original copyright (c) Technici4n<p>
|
||||||
|
*/
|
||||||
|
public class InfinityStorageManager extends SavedData {
|
||||||
|
private static final Factory<InfinityStorageManager> FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt);
|
||||||
|
// 存储所有磁盘的Map,键为UUID,值为DataStorage对象
|
||||||
|
private final Map<UUID, InfinityDataStorage> cells;
|
||||||
|
@Nullable
|
||||||
|
private WeakReference<HolderLookup.Provider> registries;
|
||||||
|
|
||||||
|
|
||||||
|
// 构造方法,初始化磁盘Map
|
||||||
|
public InfinityStorageManager() {
|
||||||
|
cells = new HashMap<>();
|
||||||
|
// 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘
|
||||||
|
this.setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 私有构造方法,用于从已有Map创建StorageManager
|
||||||
|
private InfinityStorageManager(Map<UUID, InfinityDataStorage> 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<UUID, InfinityDataStorage> 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<UUID, InfinityDataStorage> 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<UUID> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -51,8 +51,7 @@
|
||||||
"left": 88
|
"left": 88
|
||||||
},
|
},
|
||||||
"align": "CENTER"
|
"align": "CENTER"
|
||||||
}
|
},
|
||||||
,
|
|
||||||
"multiplier": {
|
"multiplier": {
|
||||||
"position": {
|
"position": {
|
||||||
"top": 80,
|
"top": 80,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
"item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)",
|
"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.x8": "Entity Speed Card (x8)",
|
||||||
"item.extendedae_plus.entity_speed_card.x16": "Entity Speed Card (x16)",
|
"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.multiplier": "Multiplier: %s",
|
||||||
"tooltip.extendedae_plus.entity_speed_card.max": "Maximum Effect: %s Times",
|
"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.power_ratio": "Power Ratio: %s",
|
||||||
"screen.extendedae_plus.entity_speed_ticker.speed": "Current Acceleration Multiplier: %d",
|
"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.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.requirement": "Requires Entity Speed Card to Enable Acceleration",
|
||||||
"item.extendedae_plus.entity_speed_ticker.tip.max": "Up to 1024x Acceleration",
|
"item.extendedae_plus.entity_speed_ticker.tip.max": "Up to 1024x Acceleration",
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
"item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)",
|
"item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)",
|
||||||
"item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)",
|
"item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)",
|
||||||
"item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)",
|
"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.multiplier": "乘数: %s",
|
||||||
"tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %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.power_ratio": "功耗比例: %s",
|
||||||
"screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d",
|
"screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d",
|
||||||
"screen.extendedae_plus.entity_speed_ticker.multiplier": "额外消耗倍率: %s",
|
"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.requirement": "需要放入实体加速卡以启用加速",
|
||||||
"item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速",
|
"item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速",
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 421 B |