Fix Accessories/CosmeticArmor duplication + guard remaining online=0

Accessories & CosmeticArmor duplication fix:
- snapshotAccessories() and snapshotCosmeticArmor() returned null when
  slots were empty, causing writeModSnapshot to SKIP the write. The DB
  kept stale data from when slots had items, restoring them on next join.
- Now return "{}" (like snapshotCuriosData already does), so empty state
  is properly written to DB. On restore, apply*FromData clears slots
  when it sees "{}" (length <= 2).

Guard remaining online=0 writes:
- deadPlayerWhileLogging and syncNotCompletedPlayer logout paths now
  use AND last_server=? to prevent setting online=0 for a player that
  already moved to another server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
laforetbrut 2026-04-05 20:26:10 +02:00
parent 8f40d5b27f
commit f042058e5b
2 changed files with 12 additions and 4 deletions

View File

@ -1003,7 +1003,8 @@ public class VanillaSync {
if (deadPlayerWhileLogging.remove(player_uuid)) {
PlayerSync.LOGGER.warn("A dead or dying player was kicked, uuid: {}", player_uuid);
try {
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player_uuid);
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=? AND last_server=?",
player_uuid, JdbcConfig.SERVER_ID.get());
} catch (SQLException e) {
PlayerSync.LOGGER.error("Error marking dead player offline: {}", player_uuid, e);
}
@ -1015,7 +1016,8 @@ public class VanillaSync {
if (syncNotCompletedPlayer.remove(player_uuid)) {
PlayerSync.LOGGER.warn("Player {} logged out with uncompleted sync. Data won't be saved for safety.", player_uuid);
try {
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=?", player_uuid);
JDBCsetUp.executePreparedUpdate("UPDATE player_data SET online=0 WHERE uuid=? AND last_server=?",
player_uuid, JdbcConfig.SERVER_ID.get());
} catch (SQLException e) {
PlayerSync.LOGGER.error("Error marking unsynced player offline: {}", player_uuid, e);
}

View File

@ -487,7 +487,10 @@ public class ModCompatSync {
}
}
}
return flatMap.isEmpty() ? null : flatMap.toString();
// FIX ANTI-DUPLICATION: Return "{}" for empty slots, NOT null.
// Null causes writeModSnapshot to SKIP the write, keeping stale data in DB.
// "{}" is written to DB, and on restore applyAccessoriesFromData clears slots.
return flatMap.toString();
} catch (Exception e) {
PlayerSync.LOGGER.error("Error snapshotting Accessories for player {}", player.getUUID(), e);
return null;
@ -511,7 +514,10 @@ public class ModCompatSync {
flatMap.put(i, VanillaSync.getNbtForStorage(stack));
}
}
return flatMap.isEmpty() ? null : flatMap.toString();
// FIX ANTI-DUPLICATION: Return "{}" for empty slots, NOT null.
// Null causes writeModSnapshot to SKIP the write, keeping stale data in DB.
// "{}" is written to DB, and on restore applyCosmeticArmorFromData clears slots.
return flatMap.toString();
} catch (Exception e) {
PlayerSync.LOGGER.error("Error snapshotting CosmeticArmor for player {}", player.getUUID(), e);
return null;