Fix NeoForge attachment sync, kick system, and backpack upgrades
1. NeoForge attachments (SOL Onion, Ars Nouveau, etc.):
- deserializeAttachments signature is (Provider, CompoundTag) not
(CompoundTag) - reflection was failing silently, nothing restored
- Use serializeAttachments(Provider) directly for saving instead of
saveWithoutId() for cleaner approach
- This fixes SOL Onion food diversity, Ars Nouveau mana/glyphs,
Iron's Spellbooks, Pehkui scale, and all other NeoForge attachments
2. Multi-server kick:
- Add secondary kick check in PlayerLoggedInEvent as fallback
- Mark online=1 SYNCHRONOUSLY on login to close race condition
where async doPlayerJoin hasn't set online=1 yet
3. Backpack upgrades:
- Call refreshInventoryForInputOutput() before reading from
BackpackStorage to flush pending wrapper changes
Vyrriox
This commit is contained in:
parent
fc7d81f914
commit
a85131708f
|
|
@ -411,6 +411,31 @@ public class VanillaSync {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FIX: Secondary kick check during PlayerLoggedInEvent.
|
||||
* PlayerNegotiationEvent fires very early and disconnect() may not always work.
|
||||
* This provides a reliable fallback that kicks the player from the server thread.
|
||||
* Also marks online=1 SYNCHRONOUSLY here to close the race condition window
|
||||
* where doPlayerJoin (async) hasn't set online=1 yet.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLoggedInKickCheck(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
if (!JdbcConfig.KICK_WHEN_ALREADY_ONLINE.get()) return;
|
||||
ServerPlayer player = (ServerPlayer) event.getEntity();
|
||||
String player_uuid = player.getUUID().toString();
|
||||
|
||||
try {
|
||||
// Mark online=1 SYNCHRONOUSLY to prevent race conditions.
|
||||
// Without this, a player joining Server B while still on Server A might slip through
|
||||
// because the async doPlayerJoin on Server A hasn't set online=1 yet.
|
||||
JDBCsetUp.executePreparedUpdate(
|
||||
"UPDATE player_data SET online=1, last_server=? WHERE uuid=?",
|
||||
JdbcConfig.SERVER_ID.get(), player_uuid);
|
||||
} catch (SQLException e) {
|
||||
PlayerSync.LOGGER.error("Error setting online flag for player {}", player_uuid, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
executorService.submit(() -> {
|
||||
|
|
|
|||
|
|
@ -272,20 +272,21 @@ public class ModCompatSync {
|
|||
try {
|
||||
if (!(player instanceof net.minecraft.server.level.ServerPlayer serverPlayer)) return;
|
||||
|
||||
net.minecraft.nbt.CompoundTag playerNbt = new net.minecraft.nbt.CompoundTag();
|
||||
serverPlayer.saveWithoutId(playerNbt);
|
||||
// FIX: Use serializeAttachments(Provider) directly instead of saveWithoutId()
|
||||
// This is the exact method NeoForge uses to save attachments, no full player save needed
|
||||
java.lang.reflect.Method serializeMethod = net.neoforged.neoforge.attachment.AttachmentHolder.class
|
||||
.getDeclaredMethod("serializeAttachments", net.minecraft.core.HolderLookup.Provider.class);
|
||||
serializeMethod.setAccessible(true);
|
||||
net.minecraft.nbt.CompoundTag attachments = (net.minecraft.nbt.CompoundTag)
|
||||
serializeMethod.invoke(player, serverPlayer.getServer().registryAccess());
|
||||
|
||||
// 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());
|
||||
}
|
||||
if (attachments != null && !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);
|
||||
|
|
@ -296,9 +297,15 @@ public class ModCompatSync {
|
|||
* 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.
|
||||
*
|
||||
* FIX: The method signature is deserializeAttachments(HolderLookup.Provider, CompoundTag),
|
||||
* NOT deserializeAttachments(CompoundTag). The old code passed wrong parameters causing
|
||||
* silent failure - no NeoForge attachment data (SOL Onion, Ars Nouveau, etc.) was restored.
|
||||
*/
|
||||
public static void restoreNeoForgeAttachments(Player player) {
|
||||
try {
|
||||
if (!(player instanceof net.minecraft.server.level.ServerPlayer serverPlayer)) return;
|
||||
|
||||
String serialized;
|
||||
try (JDBCsetUp.QueryResult qr = JDBCsetUp.executePreparedQuery(
|
||||
"SELECT data_value FROM mod_player_data WHERE uuid=? AND mod_id=?",
|
||||
|
|
@ -313,16 +320,17 @@ public class ModCompatSync {
|
|||
net.minecraft.nbt.CompoundTag attachments = VanillaSync.deserializeBinaryBase64Tag(serialized);
|
||||
if (attachments.isEmpty()) return;
|
||||
|
||||
// Create a wrapper CompoundTag with the attachments key
|
||||
// FIX: Correct method signature is (HolderLookup.Provider, CompoundTag), not (CompoundTag)
|
||||
// The wrapper must contain the "neoforge:attachments" key for the method to find the data
|
||||
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);
|
||||
.getDeclaredMethod("deserializeAttachments",
|
||||
net.minecraft.core.HolderLookup.Provider.class,
|
||||
net.minecraft.nbt.CompoundTag.class);
|
||||
deserializeMethod.setAccessible(true);
|
||||
deserializeMethod.invoke(player, wrapper);
|
||||
deserializeMethod.invoke(player, serverPlayer.getServer().registryAccess(), wrapper);
|
||||
|
||||
PlayerSync.LOGGER.info("Restored NeoForge attachments for player {} ({} keys)",
|
||||
player.getUUID(), attachments.getAllKeys().size());
|
||||
|
|
|
|||
|
|
@ -259,6 +259,15 @@ public class ModsSupport {
|
|||
Optional<UUID> uuidOpt = backpackWrapper.getContentsUuid();
|
||||
if (uuidOpt.isPresent()) {
|
||||
UUID contentsUuid = uuidOpt.get();
|
||||
|
||||
// FIX: Read the full contents NBT from the wrapper's in-memory state,
|
||||
// not from BackpackStorage which may have stale data if the wrapper
|
||||
// hasn't flushed recent changes (e.g. upgrade modifications).
|
||||
// refreshInventoryForInputOutput triggers an internal save to BackpackStorage.
|
||||
try {
|
||||
backpackWrapper.refreshInventoryForInputOutput();
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
CompoundTag backpackNbt = net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().getOrCreateBackpackContents(contentsUuid);
|
||||
saveStorageContents(contentsUuid, backpackNbt);
|
||||
PlayerSync.LOGGER.info("Saved backpack data for UUID {}", contentsUuid);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user