Add Refined Storage 2 disk sync + Extra Disks support
- Sync RS2 disk storage contents between servers (storageReference UUID) - Support both refinedstorage and extradisks namespaces - Save: extract individual entries from StorageRepository SavedData - Restore: decode via RS2 codec and inject into target server repository - Skip restore if storage already exists on target server (no overwrite) - Scan inventory + ender chest for disks Vyrriox
This commit is contained in:
parent
87d320c1f4
commit
2e0269ee62
|
|
@ -129,6 +129,9 @@ dependencies {
|
|||
// Mod compatibility - The Aether + Accessories API
|
||||
compileOnly "curse.maven:aether-255308:7043502"
|
||||
compileOnly "curse.maven:accessories-938917:7046407"
|
||||
// Mod compatibility - Refined Storage 2 + Extra Disks
|
||||
compileOnly "curse.maven:refined-storage-243076:7610477"
|
||||
compileOnly "curse.maven:extra-disks-351491:7032487"
|
||||
|
||||
runtimeOnly "curse.maven:curios-309927:6529130"
|
||||
runtimeOnly "curse.maven:sophisticated-backpacks-422301:7169832"
|
||||
|
|
|
|||
|
|
@ -378,6 +378,9 @@ public class VanillaSync {
|
|||
if (ModList.get().isLoaded("sophisticatedstorage")) {
|
||||
ModsSupport.restoreSophisticatedStorageItems(serverPlayer);
|
||||
}
|
||||
if (ModList.get().isLoaded("refinedstorage")) {
|
||||
ModsSupport.restoreRefinedStorageDisks(serverPlayer);
|
||||
}
|
||||
// Restore mod compatibility data (Accessories/Aether, CosmeticArmor)
|
||||
ModCompatSync.restoreAll(serverPlayer);
|
||||
|
||||
|
|
@ -629,6 +632,9 @@ public class VanillaSync {
|
|||
if (ModList.get().isLoaded("sophisticatedstorage")) {
|
||||
ModsSupport.storeSophisticatedStorageItems(player);
|
||||
}
|
||||
if (ModList.get().isLoaded("refinedstorage")) {
|
||||
ModsSupport.storeRefinedStorageDisks(player);
|
||||
}
|
||||
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player.getUUID().toString());
|
||||
PlayerSync.LOGGER.info("Saved player {} data on server shutdown", player.getUUID());
|
||||
} catch (Exception e) {
|
||||
|
|
@ -785,6 +791,9 @@ public class VanillaSync {
|
|||
if(ModList.get().isLoaded("sophisticatedstorage")){
|
||||
ModsSupport.storeSophisticatedStorageItems(player);
|
||||
}
|
||||
if(ModList.get().isLoaded("refinedstorage")){
|
||||
ModsSupport.storeRefinedStorageDisks(player);
|
||||
}
|
||||
|
||||
// Effects
|
||||
Map<Holder<MobEffect>, MobEffectInstance> effects = player.getActiveEffectsMap();
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@ import vip.fubuki.playersync.util.LocalJsonUtil;
|
|||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class ModsSupport {
|
||||
|
|
@ -376,4 +373,193 @@ public class ModsSupport {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Refined Storage 2 Disks
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* Saves RS2 disk storage contents for all disks in the player's inventory.
|
||||
* RS2 disks reference their storage via a UUID DataComponent (storageReference).
|
||||
* The actual storage data lives in a world-level SavedData (StorageRepositoryImpl).
|
||||
* We extract individual entries from the saved data and store them in our DB.
|
||||
*/
|
||||
public static void storeRefinedStorageDisks(Player player) {
|
||||
if (!ModList.get().isLoaded("refinedstorage")) return;
|
||||
if (!(player instanceof net.minecraft.server.level.ServerPlayer sp)) return;
|
||||
|
||||
List<UUID> diskUuids = collectRS2DiskUuids(player);
|
||||
if (diskUuids.isEmpty()) return;
|
||||
|
||||
try {
|
||||
com.refinedmods.refinedstorage.common.api.storage.StorageRepository repo =
|
||||
com.refinedmods.refinedstorage.common.api.RefinedStorageApi.INSTANCE.getStorageRepository(sp.serverLevel());
|
||||
|
||||
// Serialize the full repository to NBT via SavedData.save()
|
||||
if (repo instanceof net.minecraft.world.level.saveddata.SavedData savedData) {
|
||||
net.minecraft.nbt.CompoundTag fullNbt = new net.minecraft.nbt.CompoundTag();
|
||||
savedData.save(fullNbt, sp.getServer().registryAccess());
|
||||
|
||||
for (UUID uuid : diskUuids) {
|
||||
net.minecraft.nbt.CompoundTag entryNbt = extractRS2Entry(fullNbt, uuid);
|
||||
if (entryNbt != null && !entryNbt.isEmpty()) {
|
||||
// Store the entry NBT along with a wrapper that includes the UUID key
|
||||
// so we can reconstruct the map format on restore
|
||||
net.minecraft.nbt.CompoundTag wrapper = new net.minecraft.nbt.CompoundTag();
|
||||
wrapper.put(uuid.toString(), entryNbt);
|
||||
saveStorageContents(uuid, wrapper);
|
||||
PlayerSync.LOGGER.info("Saved RS2 disk data for UUID {}", uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("Error saving RS2 disk data for player {}", player.getUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores RS2 disk storage contents from the database.
|
||||
* Uses reflection to access the StorageRepositoryImpl's codec for proper deserialization,
|
||||
* then calls the public set() method to inject entries into the live repository.
|
||||
*/
|
||||
public static void restoreRefinedStorageDisks(Player player) {
|
||||
if (!ModList.get().isLoaded("refinedstorage")) return;
|
||||
if (!(player instanceof net.minecraft.server.level.ServerPlayer sp)) return;
|
||||
|
||||
List<UUID> diskUuids = collectRS2DiskUuids(player);
|
||||
if (diskUuids.isEmpty()) return;
|
||||
|
||||
try {
|
||||
com.refinedmods.refinedstorage.common.api.storage.StorageRepository repo =
|
||||
com.refinedmods.refinedstorage.common.api.RefinedStorageApi.INSTANCE.getStorageRepository(sp.serverLevel());
|
||||
|
||||
for (UUID uuid : diskUuids) {
|
||||
// Check if storage already exists on this server (don't overwrite)
|
||||
if (repo.get(uuid).isPresent()) {
|
||||
PlayerSync.LOGGER.debug("RS2 storage {} already exists on this server, skipping restore", uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
restoreStorageContents(uuid, (nbt) -> {
|
||||
try {
|
||||
injectRS2StorageEntry(repo, nbt, sp);
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("Error injecting RS2 storage for UUID {}", uuid, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("Error restoring RS2 disk data for player {}", player.getUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all RS2/ExtraDisks storage reference UUIDs from the player's inventory and ender chest.
|
||||
*/
|
||||
private static List<UUID> collectRS2DiskUuids(Player player) {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
// Check main inventory
|
||||
collectRS2DiskUuidsFromContainer(player.getInventory(), uuids);
|
||||
// Check ender chest
|
||||
for (int i = 0; i < player.getEnderChestInventory().getContainerSize(); i++) {
|
||||
ItemStack stack = player.getEnderChestInventory().getItem(i);
|
||||
if (stack.isEmpty()) continue;
|
||||
UUID ref = getRS2StorageReference(stack);
|
||||
if (ref != null) uuids.add(ref);
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
private static void collectRS2DiskUuidsFromContainer(Inventory inv, List<UUID> uuids) {
|
||||
for (int i = 0; i < inv.getContainerSize(); i++) {
|
||||
ItemStack stack = inv.getItem(i);
|
||||
if (stack.isEmpty()) continue;
|
||||
UUID ref = getRS2StorageReference(stack);
|
||||
if (ref != null) uuids.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the storageReference UUID from an RS2 disk item using the RS2 DataComponent.
|
||||
* Returns null if the item is not an RS2 disk or doesn't have a storage reference.
|
||||
*/
|
||||
private static UUID getRS2StorageReference(ItemStack stack) {
|
||||
try {
|
||||
net.minecraft.resources.ResourceLocation loc =
|
||||
net.minecraft.core.registries.BuiltInRegistries.ITEM.getKey(stack.getItem());
|
||||
if (!loc.getNamespace().equals("refinedstorage") && !loc.getNamespace().equals("extradisks")) {
|
||||
return null;
|
||||
}
|
||||
net.minecraft.core.component.DataComponentType<UUID> storageRefType =
|
||||
com.refinedmods.refinedstorage.common.content.DataComponents.INSTANCE.getStorageReference();
|
||||
return stack.get(storageRefType);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an individual storage entry from the full StorageRepository NBT by UUID.
|
||||
* The save() format uses UUID strings as CompoundTag keys (unboundedMap codec).
|
||||
*/
|
||||
private static net.minecraft.nbt.CompoundTag extractRS2Entry(net.minecraft.nbt.CompoundTag fullNbt, UUID uuid) {
|
||||
String uuidStr = uuid.toString();
|
||||
// Direct key lookup (standard unboundedMap format)
|
||||
if (fullNbt.contains(uuidStr, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
return fullNbt.getCompound(uuidStr);
|
||||
}
|
||||
// Some SavedData implementations wrap data under a "data" key
|
||||
for (String key : fullNbt.getAllKeys()) {
|
||||
if (fullNbt.contains(key, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
net.minecraft.nbt.CompoundTag sub = fullNbt.getCompound(key);
|
||||
if (sub.contains(uuidStr, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
return sub.getCompound(uuidStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a storage entry back into the RS2 StorageRepository.
|
||||
* Uses the repository's codec (via reflection) to properly deserialize the entry,
|
||||
* then calls set() to inject it into the live repository.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void injectRS2StorageEntry(
|
||||
com.refinedmods.refinedstorage.common.api.storage.StorageRepository repo,
|
||||
net.minecraft.nbt.CompoundTag wrapperNbt,
|
||||
net.minecraft.server.level.ServerPlayer sp) throws Exception {
|
||||
|
||||
// The wrapper contains { "uuid-string": { ...entry data... } }
|
||||
// We need to decode this using the same codec that StorageRepositoryImpl uses
|
||||
|
||||
// Get the map codec via reflection from StorageRepositoryImpl
|
||||
java.lang.reflect.Method getMapCodecMethod =
|
||||
repo.getClass().getDeclaredMethod("getMapCodec", Runnable.class);
|
||||
getMapCodecMethod.setAccessible(true);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
com.mojang.serialization.Codec codec = (com.mojang.serialization.Codec)
|
||||
getMapCodecMethod.invoke(null, (Runnable) () -> {});
|
||||
|
||||
// Decode the single-entry wrapper using the codec
|
||||
var ops = sp.getServer().registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
|
||||
com.mojang.serialization.DataResult<?> dataResult = codec.decode(ops, wrapperNbt);
|
||||
|
||||
Optional<?> resultOpt = dataResult.result();
|
||||
if (resultOpt.isPresent()) {
|
||||
// DataResult contains Pair<Map<UUID, SerializableStorage>, Tag>
|
||||
com.mojang.datafixers.util.Pair<?, ?> pair = (com.mojang.datafixers.util.Pair<?, ?>) resultOpt.get();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<UUID, ?> decoded = (Map<UUID, ?>) pair.getFirst();
|
||||
for (Map.Entry<UUID, ?> entry : decoded.entrySet()) {
|
||||
repo.set(entry.getKey(),
|
||||
(com.refinedmods.refinedstorage.common.api.storage.SerializableStorage) entry.getValue());
|
||||
PlayerSync.LOGGER.info("Restored RS2 disk storage for UUID {}", entry.getKey());
|
||||
}
|
||||
} else {
|
||||
PlayerSync.LOGGER.warn("Failed to decode RS2 storage data from wrapper NBT: {}", wrapperNbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user