Fix advancement wipe, phantom effects on death, and advancements COALESCE
- Advancements: default to null instead of "" in snapshotPlayerData, use COALESCE(?, advancements) in SQL so failed file reads preserve DB value instead of silently wiping advancements every 5min periodic save - Effects: skip saving effects when player isDeadOrDying() — Minecraft clears effects on respawn not death, so pre-death effects were persisted in DB and restored as phantom effects on next login - Legacy store() also uses COALESCE(NULLIF(?, ''), advancements) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eec949f405
commit
1dfdd43908
|
|
@ -1222,8 +1222,9 @@ public class VanillaSync {
|
|||
"INSERT INTO player_data (uuid, armor, inventory, enderchest, advancements, effects, xp, food_level, health, score, left_hand, cursors, online) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)",
|
||||
player_uuid, equipment.toString(), inventoryMap.toString(), ender_chest.toString(), json, effectMap.toString(), XP, food_level, health, score, left_hand, cursors);
|
||||
} else {
|
||||
// FIX: Use COALESCE for advancements to avoid wiping valid DB data with empty string
|
||||
JDBCsetUp.executePreparedUpdate(
|
||||
"UPDATE player_data SET inventory=?, armor=?, xp=?, effects=?, enderchest=?, score=?, food_level=?, health=?, advancements=?, left_hand=?, cursors=? WHERE uuid=?",
|
||||
"UPDATE player_data SET inventory=?, armor=?, xp=?, effects=?, enderchest=?, score=?, food_level=?, health=?, advancements=COALESCE(NULLIF(?, ''), advancements), left_hand=?, cursors=? WHERE uuid=?",
|
||||
inventoryMap.toString(), equipment.toString(), XP, effectMap.toString(), ender_chest.toString(), score, food_level, health, json, left_hand, cursors, player_uuid);
|
||||
}
|
||||
}
|
||||
|
|
@ -1266,20 +1267,34 @@ public class VanillaSync {
|
|||
for (int i = 0; i < player.getEnderChestInventory().getContainerSize(); i++) {
|
||||
enderChestMap.put(i, getNbtForStorage(player.getEnderChestInventory().getItem(i)));
|
||||
}
|
||||
// FIX: Don't save effects for dead/dying players. Minecraft clears effects on
|
||||
// respawn, not on death — so a dead player's getActiveEffectsMap() still returns
|
||||
// pre-death effects. Previously, the death handler and logout-while-dead path both
|
||||
// saved these stale effects to DB, causing "phantom effects" on the next login
|
||||
// (player reconnects alive with effects they should have lost on death).
|
||||
Map<Integer, String> effectMap = new HashMap<>();
|
||||
for (Map.Entry<Holder<MobEffect>, MobEffectInstance> entry : player.getActiveEffectsMap().entrySet()) {
|
||||
Tag effectTag = entry.getValue().save();
|
||||
effectMap.put(BuiltInRegistries.MOB_EFFECT.getId(entry.getKey().value()), serialize(effectTag.toString()));
|
||||
if (!player.isDeadOrDying()) {
|
||||
for (Map.Entry<Holder<MobEffect>, MobEffectInstance> entry : player.getActiveEffectsMap().entrySet()) {
|
||||
Tag effectTag = entry.getValue().save();
|
||||
effectMap.put(BuiltInRegistries.MOB_EFFECT.getId(entry.getKey().value()), serialize(effectTag.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
// Advancements (file read, fast)
|
||||
String advancements = "";
|
||||
// FIX: Default to null instead of "". When null, writeSnapshotToDB preserves
|
||||
// the existing DB value via COALESCE. Previously, if the file read failed
|
||||
// (save() threw, file missing, path wrong), "" was written to DB, silently
|
||||
// wiping all advancements every 5 minutes (periodic save) or on logout.
|
||||
String advancements = null;
|
||||
if (JdbcConfig.SYNC_ADVANCEMENTS.get() && player instanceof ServerPlayer sp) {
|
||||
try { sp.getAdvancements().save(); } catch (Exception ignored) {}
|
||||
Path path = sp.getServer().getServerDirectory().resolve(getSyncWorldForServer());
|
||||
File advFile = new File(path.toFile(), "/advancements/" + uuid + ".json");
|
||||
if (advFile.exists()) {
|
||||
advancements = new String(Files.readAllBytes(advFile.toPath()), StandardCharsets.UTF_8);
|
||||
String content = new String(Files.readAllBytes(advFile.toPath()), StandardCharsets.UTF_8);
|
||||
if (content != null && !content.isEmpty()) {
|
||||
advancements = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1311,8 +1326,10 @@ public class VanillaSync {
|
|||
*/
|
||||
private static void writeSnapshotToDB(PlayerDataSnapshot s) throws Exception {
|
||||
// Core player data
|
||||
// FIX: Use COALESCE for advancements — if the snapshot has null advancements
|
||||
// (file read failed), preserve the existing DB value instead of wiping it with "".
|
||||
JDBCsetUp.executePreparedUpdate(
|
||||
"UPDATE player_data SET inventory=?, armor=?, xp=?, effects=?, enderchest=?, score=?, food_level=?, health=?, advancements=?, left_hand=?, cursors=? WHERE uuid=?",
|
||||
"UPDATE player_data SET inventory=?, armor=?, xp=?, effects=?, enderchest=?, score=?, food_level=?, health=?, advancements=COALESCE(?, advancements), left_hand=?, cursors=? WHERE uuid=?",
|
||||
s.inventory(), s.equipment(), s.xp(), s.effects(), s.enderChest(), s.score(), s.foodLevel(), s.health(), s.advancements(), s.leftHand(), s.cursors(), s.uuid());
|
||||
|
||||
// Curios (snapshotted on main thread, written here off-thread)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user