feat: 移植无限盘

This commit is contained in:
C-H716 2025-09-26 15:52:51 +08:00
parent 0db71d6cc7
commit cc9dd480b4
11 changed files with 799 additions and 2 deletions

View File

@ -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(() -> {
@ -116,7 +125,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

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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) {
}
}

View File

@ -29,6 +29,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());

View File

@ -3,6 +3,7 @@ package com.extendedae_plus.init;
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.InfinityBigIntegerCellItem;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@ -62,6 +63,11 @@ public final class ModItems {
() -> new EntitySpeedCardItem(new Item.Properties())
);
public static final DeferredItem<Item> INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register(
"infinity_biginteger_cell", InfinityBigIntegerCellItem::new
);
/**
* 工厂创建带 multiplier 的实体加速卡 ItemStack2/4/8/16
*/

View File

@ -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";
}

View File

@ -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);
}
}

View File

@ -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 中获取磁盘数据列表指定类型为 CompoundTagTAG_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;
}
}

View File

@ -22,6 +22,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",

View File

@ -22,6 +22,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 倍",