Add generic NeoForge attachment sync for full mod compatibility
Adds a generic system that syncs ALL NeoForge player attachments, covering per-player data from every mod in the modpack: - Ars Nouveau: mana, glyph/spell knowledge - Iron's Spellbooks: mana, learned spells, cooldowns - Pehkui: player scale - Spice of Life: Onion: food diversity history - And ANY other mod using NeoForge's attachment system Implementation: - Save: extracts neoforge:attachments tag from player.saveWithoutId() - Restore: uses reflection to call NeoForge's deserializeAttachments() which ensures exact same deserialization path as normal player load - Stored as BNBT in mod_player_data table (mod_id=neoforge_attachments) Also verified CosmeticArmours (mod id: cosmeticarmoursmod) and CosmeticWeapons (mod id: cosmeticweaponsmod) are content-only mods that add craftable items - no custom player storage, fully handled by existing inventory sync. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5576d7f7e2
commit
f37e58be53
|
|
@ -252,6 +252,85 @@ public class ModCompatSync {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Generic NeoForge Attachment Sync
|
||||
// ============================
|
||||
|
||||
/**
|
||||
* Saves ALL NeoForge player attachments to the database.
|
||||
* This covers per-player data from ALL mods, including:
|
||||
* - Ars Nouveau (mana, glyph knowledge)
|
||||
* - Iron's Spellbooks (mana, learned spells)
|
||||
* - Pehkui (player scale)
|
||||
* - Spice of Life: Onion (food diversity)
|
||||
* - Any other mod using NeoForge's attachment system
|
||||
*
|
||||
* Uses player.saveWithoutId() to extract the attachments tag from the
|
||||
* player's full serialized NBT, ensuring we capture ALL mod data.
|
||||
*/
|
||||
public static void storeNeoForgeAttachments(Player player) {
|
||||
try {
|
||||
if (!(player instanceof net.minecraft.server.level.ServerPlayer serverPlayer)) return;
|
||||
|
||||
net.minecraft.nbt.CompoundTag playerNbt = new net.minecraft.nbt.CompoundTag();
|
||||
serverPlayer.saveWithoutId(playerNbt);
|
||||
|
||||
// NeoForge stores all attachment data under this key
|
||||
if (playerNbt.contains("neoforge:attachments", net.minecraft.nbt.Tag.TAG_COMPOUND)) {
|
||||
net.minecraft.nbt.CompoundTag attachments = playerNbt.getCompound("neoforge:attachments");
|
||||
if (!attachments.isEmpty()) {
|
||||
String serialized = VanillaSync.serializeTagToBinaryBase64(attachments);
|
||||
JDBCsetUp.executePreparedUpdate(
|
||||
"REPLACE INTO mod_player_data (uuid, mod_id, data_value) VALUES (?, ?, ?)",
|
||||
player.getUUID().toString(), "neoforge_attachments", serialized);
|
||||
PlayerSync.LOGGER.debug("Saved NeoForge attachments for player {} ({} keys)",
|
||||
player.getUUID(), attachments.getAllKeys().size());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("Error saving NeoForge attachments for player {}", player.getUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores NeoForge player attachments from the database.
|
||||
* Uses reflection to call NeoForge's internal deserializeAttachments method,
|
||||
* which ensures the exact same deserialization path as a normal player load.
|
||||
*/
|
||||
public static void restoreNeoForgeAttachments(Player player) {
|
||||
try {
|
||||
String serialized;
|
||||
try (JDBCsetUp.QueryResult qr = JDBCsetUp.executePreparedQuery(
|
||||
"SELECT data_value FROM mod_player_data WHERE uuid=? AND mod_id=?",
|
||||
player.getUUID().toString(), "neoforge_attachments")) {
|
||||
ResultSet rs = qr.resultSet();
|
||||
if (!rs.next()) return;
|
||||
serialized = rs.getString("data_value");
|
||||
}
|
||||
|
||||
if (serialized == null || !serialized.startsWith("BNBT:")) return;
|
||||
|
||||
net.minecraft.nbt.CompoundTag attachments = VanillaSync.deserializeBinaryBase64Tag(serialized);
|
||||
if (attachments.isEmpty()) return;
|
||||
|
||||
// Create a wrapper CompoundTag with the attachments key
|
||||
net.minecraft.nbt.CompoundTag wrapper = new net.minecraft.nbt.CompoundTag();
|
||||
wrapper.put("neoforge:attachments", attachments);
|
||||
|
||||
// Use reflection to call the package-private deserializeAttachments method
|
||||
// This ensures we use NeoForge's exact deserialization logic
|
||||
java.lang.reflect.Method deserializeMethod = net.neoforged.neoforge.attachment.AttachmentHolder.class
|
||||
.getDeclaredMethod("deserializeAttachments", net.minecraft.nbt.CompoundTag.class);
|
||||
deserializeMethod.setAccessible(true);
|
||||
deserializeMethod.invoke(player, wrapper);
|
||||
|
||||
PlayerSync.LOGGER.info("Restored NeoForge attachments for player {} ({} keys)",
|
||||
player.getUUID(), attachments.getAllKeys().size());
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("Error restoring NeoForge attachments for player {}", player.getUUID(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Convenience methods
|
||||
// ============================
|
||||
|
|
@ -263,6 +342,7 @@ public class ModCompatSync {
|
|||
public static void storeAll(Player player) {
|
||||
storeAccessories(player);
|
||||
storeCosmeticArmor(player);
|
||||
storeNeoForgeAttachments(player);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -272,5 +352,6 @@ public class ModCompatSync {
|
|||
public static void restoreAll(Player player) {
|
||||
restoreAccessories(player);
|
||||
restoreCosmeticArmor(player);
|
||||
restoreNeoForgeAttachments(player);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user