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
|
// Convenience methods
|
||||||
// ============================
|
// ============================
|
||||||
|
|
@ -263,6 +342,7 @@ public class ModCompatSync {
|
||||||
public static void storeAll(Player player) {
|
public static void storeAll(Player player) {
|
||||||
storeAccessories(player);
|
storeAccessories(player);
|
||||||
storeCosmeticArmor(player);
|
storeCosmeticArmor(player);
|
||||||
|
storeNeoForgeAttachments(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -272,5 +352,6 @@ public class ModCompatSync {
|
||||||
public static void restoreAll(Player player) {
|
public static void restoreAll(Player player) {
|
||||||
restoreAccessories(player);
|
restoreAccessories(player);
|
||||||
restoreCosmeticArmor(player);
|
restoreCosmeticArmor(player);
|
||||||
|
restoreNeoForgeAttachments(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user