完成无限元件重写
This commit is contained in:
parent
f910885190
commit
d9ec230e91
|
|
@ -9,11 +9,9 @@ import appeng.items.parts.PartModelsHelper;
|
||||||
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
|
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;
|
||||||
|
|
@ -22,10 +20,7 @@ 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
|
||||||
|
|
@ -59,8 +54,6 @@ 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");
|
||||||
|
|
@ -125,29 +118,6 @@ public class ExtendedAEPlus {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
public void onServerStarting(ServerStartingEvent event) {
|
public void onServerStarting(ServerStartingEvent event) {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package com.extendedae_plus.ae.api.storage;
|
||||||
|
|
||||||
import appeng.api.storage.cells.ICellHandler;
|
import appeng.api.storage.cells.ICellHandler;
|
||||||
import appeng.api.storage.cells.ISaveProvider;
|
import appeng.api.storage.cells.ISaveProvider;
|
||||||
import com.extendedae_plus.ExtendedAEPlus;
|
|
||||||
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
|
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
|
||||||
|
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class InfinityBigIntegerCellHandler implements ICellHandler {
|
public class InfinityBigIntegerCellHandler implements ICellHandler {
|
||||||
|
|
||||||
|
|
@ -15,8 +17,12 @@ public class InfinityBigIntegerCellHandler implements ICellHandler {
|
||||||
return is.getItem() instanceof InfinityBigIntegerCellItem;
|
return is.getItem() instanceof InfinityBigIntegerCellItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** AE的这个方法是Nullable的,这里直接把NULL拦在外面即可 */
|
||||||
@Override
|
@Override
|
||||||
public InfinityBigIntegerCellInventory getCellInventory(ItemStack is, ISaveProvider container) {
|
public @Nullable InfinityBigIntegerCellInventory getCellInventory(ItemStack is, @Nullable ISaveProvider container) {
|
||||||
return InfinityBigIntegerCellInventory.createInventory(is, container, ExtendedAEPlus.currentStorageManager());
|
if(ServerLifecycleHooks.getCurrentServer() == null) return null;
|
||||||
|
InfinityStorageManager manager = InfinityStorageManager.getInstance();
|
||||||
|
if(manager == null || is == null) return null;
|
||||||
|
return new InfinityBigIntegerCellInventory(is, container, manager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,70 +2,91 @@ package com.extendedae_plus.ae.api.storage;
|
||||||
|
|
||||||
import appeng.api.config.Actionable;
|
import appeng.api.config.Actionable;
|
||||||
import appeng.api.networking.security.IActionSource;
|
import appeng.api.networking.security.IActionSource;
|
||||||
import appeng.api.stacks.AEItemKey;
|
|
||||||
import appeng.api.stacks.AEKey;
|
import appeng.api.stacks.AEKey;
|
||||||
import appeng.api.stacks.KeyCounter;
|
import appeng.api.stacks.KeyCounter;
|
||||||
import appeng.api.storage.cells.CellState;
|
import appeng.api.storage.cells.CellState;
|
||||||
import appeng.api.storage.cells.ISaveProvider;
|
import appeng.api.storage.cells.ISaveProvider;
|
||||||
import appeng.api.storage.cells.StorageCell;
|
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.InfinityConstants;
|
||||||
import com.extendedae_plus.util.storage.InfinityDataStorage;
|
|
||||||
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.<p>
|
* MIT License
|
||||||
* Original copyright (c) Technici4n<p>
|
*
|
||||||
|
* Copyright (c) 2025 Frostbite-time
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于无限元件的存储,这个类永远只会在服务端被构造
|
||||||
|
* @author Frostbite
|
||||||
*/
|
*/
|
||||||
public class InfinityBigIntegerCellInventory implements StorageCell {
|
public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
private final InfinityBigIntegerCellItem cell;
|
|
||||||
// 磁盘本身
|
// 磁盘本身
|
||||||
|
@NotNull
|
||||||
private final ItemStack self;
|
private final ItemStack self;
|
||||||
@Nullable
|
@NotNull
|
||||||
private final InfinityStorageManager storageManager;
|
private final InfinityStorageManager storageManager;
|
||||||
// AE2 提供的保存提供者,用于在容器中批量保存时触发回调
|
// AE2 提供的保存提供者,用于在容器中批量保存时触发回调
|
||||||
|
@Nullable
|
||||||
private final ISaveProvider container;
|
private final ISaveProvider container;
|
||||||
// 存储物品键和数量的映射
|
// 存储物品键和数量的映射
|
||||||
private Object2ObjectMap<AEKey, BigInteger> AEKey2AmountsMap;
|
@NotNull
|
||||||
// 存储的物品种类数量
|
private final Object2ObjectMap<AEKey, BigInteger> AEKey2AmountsMap;
|
||||||
private int totalAEKeyType;
|
|
||||||
// 存储的物品总数
|
// 存储的物品总数
|
||||||
|
@NotNull
|
||||||
private BigInteger totalAEKey2Amounts = BigInteger.ZERO;
|
private BigInteger totalAEKey2Amounts = BigInteger.ZERO;
|
||||||
// 标记是否已持久化到 SavedData
|
// 标记表示是否通知了需要保存
|
||||||
private boolean isPersisted = true;
|
private boolean isPersisted = false;
|
||||||
|
|
||||||
|
|
||||||
public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell,
|
public InfinityBigIntegerCellInventory(@NotNull ItemStack stack,
|
||||||
ItemStack stack,
|
@Nullable ISaveProvider saveProvider,
|
||||||
ISaveProvider saveProvider,
|
@NotNull InfinityStorageManager storageManager) {
|
||||||
@Nullable InfinityStorageManager storageManager) {
|
|
||||||
// 保存存储单元类型(InfinityBigIntegerCellItem 实例),用于访问磁盘属性
|
|
||||||
this.cell = cell;
|
|
||||||
// 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据
|
// 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据
|
||||||
this.self = stack;
|
this.self = stack;
|
||||||
// 保存提供者,用于触发数据保存
|
// 保存提供者,用于触发数据保存
|
||||||
this.container = saveProvider;
|
this.container = saveProvider;
|
||||||
// 初始化 storedAmounts 为 null,延迟加载物品数据
|
|
||||||
this.AEKey2AmountsMap = null;
|
|
||||||
this.storageManager = storageManager;
|
this.storageManager = storageManager;
|
||||||
// 初始化磁盘数据
|
// 走到这一步之后,我们不再管stack到底是什么物品,只要它能突破InfinityBigIntegerCellHandler的限制塞进来,那我们都接收
|
||||||
initData();
|
// 这里立刻构建map,确保后续操作简洁,这里如果存在则取引用,如果不存在则建立空map,均不会造成性能问题
|
||||||
|
this.AEKey2AmountsMap = storageManager.getOrCreateCell(getOrCreateUUID(stack, storageManager)).getAEKey2Amounts();
|
||||||
|
|
||||||
|
// 第一次构建我们全量遍历数量,后续增量更新
|
||||||
|
for(BigInteger amount : AEKey2AmountsMap.values())
|
||||||
|
{
|
||||||
|
totalAEKey2Amounts = totalAEKey2Amounts.add(amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将 BigInteger 格式化为带单位的字符串,保留两位小数
|
// 将 BigInteger 格式化为带单位的字符串,保留两位小数
|
||||||
|
|
@ -86,49 +107,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
return df.format(bd.doubleValue()) + units[idx];
|
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
|
@Override
|
||||||
public CellState getStatus() {
|
public CellState getStatus() {
|
||||||
|
|
@ -149,107 +127,51 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
// 持久化存储单元数据到全局存储
|
// 持久化存储单元数据到全局存储
|
||||||
@Override
|
@Override
|
||||||
public void persist() {
|
public void persist() {
|
||||||
if (this.isPersisted || this.storageManager == null)
|
if (this.isPersisted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// 更新tooltip
|
||||||
|
|
||||||
if (this.totalAEKey2Amounts.equals(BigInteger.ZERO)) {
|
if (this.totalAEKey2Amounts.equals(BigInteger.ZERO)) {
|
||||||
if (hasUUID()) {
|
// 移除磁盘这一步有极少量优化效果,但是一个空map在内存中只占用极少的空间,持久化到磁盘中也只有极低的占用
|
||||||
this.storageManager.removeCell(getUUID());
|
// 且SavedData并不限制存储的nbt的长度大小,因此,我们不再处理磁盘移除,简化数据管理逻辑
|
||||||
// 从 DataComponents.CUSTOM_DATA 里移除对应字段
|
|
||||||
CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
||||||
CompoundTag tag = data.copyTag();
|
CompoundTag tag = data.copyTag();
|
||||||
tag.remove(InfinityConstants.INFINITY_CELL_UUID);
|
|
||||||
tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
|
tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
|
||||||
tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
|
tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
|
||||||
// backward compat
|
// backward compat
|
||||||
tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
||||||
|
|
||||||
self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||||
|
|
||||||
initData();
|
|
||||||
}
|
|
||||||
return;
|
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();
|
CompoundTag tag = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||||
tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCount.toByteArray());
|
tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, this.totalAEKey2Amounts.toByteArray());
|
||||||
tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType);
|
tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.AEKey2AmountsMap.size());
|
||||||
tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray());
|
tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, this.totalAEKey2Amounts.toByteArray());
|
||||||
self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||||
// 标记数据已持久化
|
|
||||||
this.isPersisted = true;
|
this.isPersisted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取存储单元的描述(此处返回null,可自定义)
|
// 获取存储单元的描述
|
||||||
@Override
|
@Override
|
||||||
public Component getDescription() {
|
public Component getDescription() {
|
||||||
return null;
|
return Component.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取存储的物品总数
|
// 获取存储的物品总数
|
||||||
public BigInteger getTotalAEKey2Amounts() {
|
public @NotNull BigInteger getTotalAEKey2Amounts() {
|
||||||
return this.totalAEKey2Amounts;
|
return this.totalAEKey2Amounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取存储的物品种类数量
|
// 获取存储的物品种类数量
|
||||||
public int getTotalAEKeyType() {
|
public int getTotalAEKeyType() {
|
||||||
return this.totalAEKeyType;
|
return this.AEKey2AmountsMap.size();
|
||||||
}
|
|
||||||
|
|
||||||
// 判断物品堆栈是否有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() {
|
private Object2ObjectMap<AEKey, BigInteger> getCellStoredMap() {
|
||||||
if (AEKey2AmountsMap == null) {
|
|
||||||
AEKey2AmountsMap = new Object2ObjectOpenHashMap<>();
|
|
||||||
this.loadCellStoredMap();
|
|
||||||
}
|
|
||||||
return AEKey2AmountsMap;
|
return AEKey2AmountsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,15 +179,13 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
@Override
|
@Override
|
||||||
public void getAvailableStacks(KeyCounter out) {
|
public void getAvailableStacks(KeyCounter out) {
|
||||||
BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
|
BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
|
||||||
if (this.getCellStoredMap() == null) return;
|
|
||||||
for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) {
|
for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) {
|
||||||
AEKey key = entry.getKey();
|
AEKey key = entry.getKey();
|
||||||
BigInteger value = entry.getValue();
|
BigInteger value = entry.getValue();
|
||||||
|
if(value.equals(BigInteger.ZERO)) continue;
|
||||||
// 获取 KeyCounter 中已有的值
|
|
||||||
long existing = out.get(key);
|
|
||||||
|
|
||||||
// 计算总和并限制到 Long.MAX_VALUE
|
// 计算总和并限制到 Long.MAX_VALUE
|
||||||
|
long existing = out.get(key);
|
||||||
BigInteger sum = BigInteger.valueOf(existing).add(value);
|
BigInteger sum = BigInteger.valueOf(existing).add(value);
|
||||||
long toSet = sum.compareTo(maxLong) > 0 ? Long.MAX_VALUE : sum.longValue();
|
long toSet = sum.compareTo(maxLong) > 0 ? Long.MAX_VALUE : sum.longValue();
|
||||||
// 更新 KeyCounter
|
// 更新 KeyCounter
|
||||||
|
|
@ -279,60 +199,16 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 从存储中加载物品映射
|
|
||||||
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() {
|
private void saveChanges() {
|
||||||
// 更新存储的物品种类数量
|
// 这里只需要这么多,container的统一持久化最终也会走persist
|
||||||
this.totalAEKeyType = this.AEKey2AmountsMap.size();
|
|
||||||
// 重置物品总数
|
|
||||||
this.totalAEKey2Amounts = BigInteger.ZERO;
|
|
||||||
// 计算物品总数
|
|
||||||
for (BigInteger AEKey2Amounts : this.AEKey2AmountsMap.values()) {
|
|
||||||
this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(AEKey2Amounts);
|
|
||||||
}
|
|
||||||
// 标记数据未持久化
|
|
||||||
this.isPersisted = false;
|
this.isPersisted = false;
|
||||||
// 如果有保存提供者,通知保存
|
|
||||||
if (this.container != null) {
|
if (this.container != null) {
|
||||||
this.container.saveChanges();
|
this.container.saveChanges();
|
||||||
} else {
|
} else {
|
||||||
// 否则立即持久化
|
|
||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
|
storageManager.setDirty(); // 数据设脏,这里只是通知,不会导致立刻保存,后续磁盘会随着保存事件一起保存
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入物品到存储单元
|
// 插入物品到存储单元
|
||||||
|
|
@ -342,40 +218,25 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// 不允许存储无限单元自身
|
|
||||||
if (what instanceof AEItemKey itemKey &&
|
|
||||||
itemKey.getItem() instanceof InfinityBigIntegerCellItem &&
|
|
||||||
itemKey.get(DataComponents.CUSTOM_DATA) != null
|
|
||||||
) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有 UUID,且服务器端存储管理器已就绪,则生成 UUID 并初始化存储
|
// 这里存储自身不会造成实际上的问题,因为无限磁盘本身存储的nbt极为有限
|
||||||
if (storageManager != null && !this.hasUUID()) {
|
// 如果后续需要判断那些不允许被存储的磁盘,自行调用StroageCells中获取磁盘仓库的方法来判空 + canFitInsideCell
|
||||||
// 取出自定义 NBT(如果没有就返回空)
|
// 这里不做过多处理
|
||||||
CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
// if (what instanceof AEItemKey itemKey &&
|
||||||
CompoundTag tag = data.copyTag();
|
// itemKey.getItem() instanceof InfinityBigIntegerCellItem &&
|
||||||
|
// itemKey.get(DataComponents.CUSTOM_DATA) != null
|
||||||
|
// ) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
|
||||||
// 生成新的 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);
|
BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO);
|
||||||
|
|
||||||
if (mode == Actionable.MODULATE) {
|
if (mode == Actionable.MODULATE) {
|
||||||
// 实际插入,更新数量并保存
|
// 实际插入,更新数量并保存
|
||||||
BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount));
|
BigInteger toAdd = BigInteger.valueOf(amount);
|
||||||
getCellStoredMap().put(what, newAmount);
|
this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(toAdd);
|
||||||
|
getCellStoredMap().put(what, currentAmount.add(toAdd));
|
||||||
this.saveChanges();
|
this.saveChanges();
|
||||||
}
|
}
|
||||||
return amount;
|
return amount;
|
||||||
|
|
@ -393,6 +254,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
// 如果提取数量大于等于当前数量
|
// 如果提取数量大于等于当前数量
|
||||||
if (requested.compareTo(currentAmount) >= 0) {
|
if (requested.compareTo(currentAmount) >= 0) {
|
||||||
if (mode == Actionable.MODULATE) {
|
if (mode == Actionable.MODULATE) {
|
||||||
|
this.totalAEKey2Amounts = this.totalAEKey2Amounts.subtract(currentAmount);
|
||||||
getCellStoredMap().remove(what);
|
getCellStoredMap().remove(what);
|
||||||
this.saveChanges();
|
this.saveChanges();
|
||||||
}
|
}
|
||||||
|
|
@ -400,6 +262,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
} else {
|
} else {
|
||||||
// 提取部分数量
|
// 提取部分数量
|
||||||
if (mode == Actionable.MODULATE) {
|
if (mode == Actionable.MODULATE) {
|
||||||
|
this.totalAEKey2Amounts = this.totalAEKey2Amounts.subtract(requested);
|
||||||
getCellStoredMap().put(what, currentAmount.subtract(requested));
|
getCellStoredMap().put(what, currentAmount.subtract(requested));
|
||||||
this.saveChanges();
|
this.saveChanges();
|
||||||
}
|
}
|
||||||
|
|
@ -414,4 +277,22 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
// 使用缓存的 totalStored,避免每次全表扫描
|
// 使用缓存的 totalStored,避免每次全表扫描
|
||||||
return formatBigInteger(totalAEKey2Amounts);
|
return formatBigInteger(totalAEKey2Amounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 能用来构建这个inv的物品必须要有一个UUID
|
||||||
|
public static UUID getOrCreateUUID(ItemStack stack, InfinityStorageManager storageManager)
|
||||||
|
{
|
||||||
|
CustomData data = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
|
||||||
|
if(!data.isEmpty() && data.copyTag().contains(InfinityConstants.INFINITY_CELL_UUID)) {
|
||||||
|
return data.copyTag().getUUID(InfinityConstants.INFINITY_CELL_UUID);
|
||||||
|
} else {
|
||||||
|
UUID uuid;
|
||||||
|
do {
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
|
} while (storageManager.hasUUID(uuid)); // 防御一下
|
||||||
|
CompoundTag tag = data.copyTag();
|
||||||
|
tag.putUUID(InfinityConstants.INFINITY_CELL_UUID, uuid);
|
||||||
|
stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag));
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchIt
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FuzzyMode getFuzzyMode(ItemStack itemStack) {
|
public FuzzyMode getFuzzyMode(ItemStack itemStack) {
|
||||||
return null;
|
return FuzzyMode.IGNORE_ALL; // 这里返回什么都无所谓,cell inv最终被我们自行操作,但是出于安全考虑,还是不返回null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,111 @@
|
||||||
package com.extendedae_plus.util.storage;
|
package com.extendedae_plus.util.storage;
|
||||||
|
|
||||||
|
import appeng.api.stacks.AEKey;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.<p>
|
* MIT License
|
||||||
* Original copyright (c) Technici4n<p>
|
*
|
||||||
|
* Copyright (c) 2025 Frostbite-time
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无尽元件的数据实际读写
|
||||||
|
* @author Frostbite
|
||||||
*/
|
*/
|
||||||
public class InfinityDataStorage {
|
public class InfinityDataStorage {
|
||||||
// 定义一个静态常量 EMPTY,表示一个空的 DataStorage 实例,用于默认或占位场景
|
// EMPTY也不需要,因为ae的硬盘获取实际上是允许返回null的,详见InfinityBigIntegerCellHandler的修改
|
||||||
public static final InfinityDataStorage EMPTY = new InfinityDataStorage();
|
|
||||||
|
|
||||||
public ListTag keys;
|
/** 同一个磁盘,在内存中总是只维护一份map,也就是这个。其他人永远只使用其引用 */
|
||||||
public ListTag amounts;
|
private final Object2ObjectMap<AEKey, BigInteger> AEKey2Amounts;
|
||||||
// 存储磁盘中物品的总数,使用 BigInteger 支持大容量
|
|
||||||
public BigInteger itemCount;
|
|
||||||
|
|
||||||
public InfinityDataStorage() {
|
public InfinityDataStorage() {
|
||||||
this(new ListTag(), new ListTag(), BigInteger.ZERO);
|
this(new Object2ObjectOpenHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private InfinityDataStorage(ListTag keys, ListTag amounts, BigInteger itemCount) {
|
public InfinityDataStorage(Object2ObjectMap<AEKey, BigInteger> maps) {
|
||||||
this.keys = keys;
|
AEKey2Amounts = maps;
|
||||||
this.amounts = amounts;
|
}
|
||||||
this.itemCount = itemCount;
|
|
||||||
|
public Object2ObjectMap<AEKey, BigInteger> getAEKey2Amounts()
|
||||||
|
{
|
||||||
|
return AEKey2Amounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将 DataStorage 数据序列化为 NBT 格式
|
// 将 DataStorage 数据序列化为 NBT 格式
|
||||||
public CompoundTag serializeNBT() {
|
public CompoundTag serializeNBT(@NotNull HolderLookup.Provider registry) {
|
||||||
CompoundTag nbt = new CompoundTag();
|
// 创建物品键列表
|
||||||
nbt.put(InfinityConstants.INFINITY_CELL_KEYS, keys);
|
ListTag keys = new ListTag();
|
||||||
nbt.put(InfinityConstants.INFINITY_CELL_AMOUNTS, amounts);
|
// 创建物品数量列表
|
||||||
nbt.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray());
|
ListTag amounts = new ListTag();
|
||||||
return nbt;
|
|
||||||
|
for (var entry : this.AEKey2Amounts.object2ObjectEntrySet()) {
|
||||||
|
BigInteger amount = entry.getValue();
|
||||||
|
// 如果数量大于 0,添加到键和数量列表
|
||||||
|
if (amount.compareTo(BigInteger.ZERO) > 0) {
|
||||||
|
try {
|
||||||
|
keys.add(entry.getKey().toTagGeneric(registry));
|
||||||
|
CompoundTag amountTag = new CompoundTag();
|
||||||
|
amountTag.putByteArray("value", amount.toByteArray());
|
||||||
|
amounts.add(amountTag);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// 静默所有错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag KVTags = new CompoundTag();
|
||||||
|
KVTags.put(InfinityConstants.INFINITY_CELL_KEYS, keys);
|
||||||
|
KVTags.put(InfinityConstants.INFINITY_CELL_AMOUNTS, amounts);
|
||||||
|
return KVTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 NBT 数据反序列化创建 DataStorage 实例
|
// 从 NBT 数据反序列化创建 DataStorage 实例
|
||||||
public static InfinityDataStorage loadFromNBT(CompoundTag nbt) {
|
public static InfinityDataStorage loadFromNBT(CompoundTag nbt, @NotNull HolderLookup.Provider registry) {
|
||||||
ListTag keys = nbt.getList(InfinityConstants.INFINITY_CELL_KEYS, ListTag.TAG_COMPOUND);
|
ListTag keys = nbt.getList(InfinityConstants.INFINITY_CELL_KEYS, ListTag.TAG_COMPOUND);
|
||||||
ListTag amounts = nbt.getList(InfinityConstants.INFINITY_CELL_AMOUNTS, 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 实例
|
Object2ObjectMap<AEKey, BigInteger> maps = new Object2ObjectOpenHashMap<>();
|
||||||
return new InfinityDataStorage(keys, amounts, itemCount);
|
maps.defaultReturnValue(BigInteger.ZERO);
|
||||||
|
for (int i = 0; i < keys.size(); i++) {
|
||||||
|
try {
|
||||||
|
AEKey key = AEKey.fromTagGeneric(registry,keys.getCompound(i));
|
||||||
|
BigInteger amount = new BigInteger(amounts.getCompound(i).getByteArray("value"));
|
||||||
|
if (amount.compareTo(BigInteger.ZERO) <= 0 || key == null) continue;
|
||||||
|
maps.put(AEKey.fromTagGeneric(registry, keys.getCompound(i)), new BigInteger(amounts.getCompound(i).getByteArray("value")));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// 忽略错误,保证读取时不崩溃
|
||||||
|
// 这里我静默处理所有错误,后续你可以自己做其他处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InfinityDataStorage(maps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,39 +4,55 @@ import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.saveddata.SavedData;
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
|
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.<p>
|
* MIT License
|
||||||
* Original copyright (c) Technici4n<p>
|
*
|
||||||
|
* Copyright (c) 2025 Frostbite-time
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
* */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无尽元件的SavedData管理类
|
||||||
|
* @author Frostbite
|
||||||
*/
|
*/
|
||||||
public class InfinityStorageManager extends SavedData {
|
public class InfinityStorageManager extends SavedData {
|
||||||
private static final Factory<InfinityStorageManager> FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt);
|
private static final Factory<InfinityStorageManager> FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt);
|
||||||
// 存储所有磁盘的Map,键为UUID,值为DataStorage对象
|
// 存储所有磁盘的Map,键为UUID,值为DataStorage对象
|
||||||
private final Map<UUID, InfinityDataStorage> cells;
|
private final Map<UUID, InfinityDataStorage> cells;
|
||||||
@Nullable
|
|
||||||
private WeakReference<HolderLookup.Provider> registries;
|
|
||||||
|
|
||||||
|
|
||||||
// 构造方法,初始化磁盘Map
|
// 构造方法,初始化磁盘Map
|
||||||
public InfinityStorageManager() {
|
public InfinityStorageManager() {
|
||||||
cells = new HashMap<>();
|
cells = new HashMap<>();
|
||||||
// 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘
|
|
||||||
this.setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 私有构造方法,用于从已有Map创建StorageManager
|
// 私有构造方法,用于从已有Map创建StorageManager
|
||||||
private InfinityStorageManager(Map<UUID, InfinityDataStorage> cells) {
|
private InfinityStorageManager(Map<UUID, InfinityDataStorage> cells) {
|
||||||
// 确保使用已加载的数据
|
// 确保使用已加载的数据
|
||||||
this.cells = cells;
|
this.cells = cells;
|
||||||
// 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘
|
|
||||||
this.setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,7 +71,7 @@ public class InfinityStorageManager extends SavedData {
|
||||||
// 获取当前索引的 CompoundTag,表示单个磁盘的数据
|
// 获取当前索引的 CompoundTag,表示单个磁盘的数据
|
||||||
CompoundTag cell = cellList.getCompound(i);
|
CompoundTag cell = cellList.getCompound(i);
|
||||||
// 从 CompoundTag 中读取 UUID 和 DataStorage 数据,并存入 cells 映射
|
// 从 CompoundTag 中读取 UUID 和 DataStorage 数据,并存入 cells 映射
|
||||||
cells.put(cell.getUUID(InfinityConstants.INFINITY_CELL_UUID), InfinityDataStorage.loadFromNBT(cell.getCompound(InfinityConstants.INFINITY_CELL_DATA)));
|
cells.put(cell.getUUID(InfinityConstants.INFINITY_CELL_UUID), InfinityDataStorage.loadFromNBT(cell.getCompound(InfinityConstants.INFINITY_CELL_DATA), registries));
|
||||||
}
|
}
|
||||||
// 使用加载的 cells 数据创建新的 StorageManager 实例
|
// 使用加载的 cells 数据创建新的 StorageManager 实例
|
||||||
return new InfinityStorageManager(cells);
|
return new InfinityStorageManager(cells);
|
||||||
|
|
@ -63,13 +79,13 @@ public class InfinityStorageManager extends SavedData {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag save(CompoundTag nbt, HolderLookup.Provider provider) {
|
public @NotNull CompoundTag save(@NotNull CompoundTag nbt, HolderLookup.@NotNull Provider provider) {
|
||||||
// 将内存中的所有 cell 序列化为一个 ListTag
|
// 将内存中的所有 cell 序列化为一个 ListTag
|
||||||
ListTag cellList = new ListTag();
|
ListTag cellList = new ListTag();
|
||||||
for (Map.Entry<UUID, InfinityDataStorage> entry : cells.entrySet()) {
|
for (Map.Entry<UUID, InfinityDataStorage> entry : cells.entrySet()) {
|
||||||
CompoundTag cell = new CompoundTag();
|
CompoundTag cell = new CompoundTag();
|
||||||
cell.putUUID(InfinityConstants.INFINITY_CELL_UUID, entry.getKey());
|
cell.putUUID(InfinityConstants.INFINITY_CELL_UUID, entry.getKey());
|
||||||
cell.put(InfinityConstants.INFINITY_CELL_DATA, entry.getValue().serializeNBT());
|
cell.put(InfinityConstants.INFINITY_CELL_DATA, entry.getValue().serializeNBT(provider));
|
||||||
cellList.add(cell);
|
cellList.add(cell);
|
||||||
}
|
}
|
||||||
nbt.put(InfinityConstants.INFINITY_CELL_LIST, cellList);
|
nbt.put(InfinityConstants.INFINITY_CELL_LIST, cellList);
|
||||||
|
|
@ -83,13 +99,6 @@ public class InfinityStorageManager extends SavedData {
|
||||||
return Collections.unmodifiableSet(cells.keySet());
|
return Collections.unmodifiableSet(cells.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新或添加某个 UUID 对应的数据并标记为脏(需要保存)
|
|
||||||
public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) {
|
|
||||||
cells.put(uuid, infinityDataStorage);
|
|
||||||
// 标记数据为“脏”,确保修改后的数据会在下次保存时写入磁盘
|
|
||||||
setDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除某个 UUID 的持久化记录并标记为脏
|
// 删除某个 UUID 的持久化记录并标记为脏
|
||||||
public void removeCell(UUID uuid) {
|
public void removeCell(UUID uuid) {
|
||||||
cells.remove(uuid);
|
cells.remove(uuid);
|
||||||
|
|
@ -107,45 +116,18 @@ public class InfinityStorageManager extends SavedData {
|
||||||
public InfinityDataStorage getOrCreateCell(UUID uuid) {
|
public InfinityDataStorage getOrCreateCell(UUID uuid) {
|
||||||
// 检查 cells 映射中是否不存在指定 UUID
|
// 检查 cells 映射中是否不存在指定 UUID
|
||||||
if (!cells.containsKey(uuid)) {
|
if (!cells.containsKey(uuid)) {
|
||||||
updateCell(uuid, new InfinityDataStorage());
|
InfinityDataStorage cell = new InfinityDataStorage();
|
||||||
|
cells.put(uuid, cell);
|
||||||
|
return cell;
|
||||||
}
|
}
|
||||||
// 返回指定 UUID 对应的 DataStorage 对象
|
// 返回指定 UUID 对应的 DataStorage 对象
|
||||||
return cells.get(uuid);
|
return cells.get(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改指定 UUID 的磁盘数据,包括堆栈键、数量和总项目数
|
public static @Nullable InfinityStorageManager getInstance() {
|
||||||
public void modifyDisk(UUID uuid, ListTag keys, ListTag amounts, BigInteger itemCount) {
|
// 用neo提供的钩子拿服务端,再从服务端拿信息,注册表信息也无需在这里拿取,getCurrentServer始终会返回对当前来说正确的服务端
|
||||||
// 获取或创建指定 UUID 的 DataStorage 对象
|
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
InfinityDataStorage cellToModify = getOrCreateCell(uuid);
|
if (server == null) return null;
|
||||||
if (keys != null && amounts != null) {
|
return server.overworld().getDataStorage().computeIfAbsent(FACTORY, InfinityConstants.SAVE_FILE_NAME);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user