Phase 19: wire save_on_death + save_on_respawn (dead config) to fix keep-charm edge cases

User report: Twilight Forest 'Charm of Keeping' + ReviveMe interaction loses
inventory over multiple deaths. Analysis concludes this is a Twilight/ReviveMe
event-priority interaction, not a PlayerSync bug — but two dead config options
hid admin-facing levers that help mitigate the case.

(1) save_on_death was declared in JdbcConfig but NEVER read in code. The death
    snapshot ran unconditionally. Now gated: setting save_on_death=false
    disables the LivingDeathEvent-driven pre-drop snapshot. The normal
    onPlayerLogout save still fires on disconnect, so nothing is lost — but
    admins diagnosing a keeping-charm interaction can quickly turn off the
    aggressive death snapshot to rule PlayerSync in or out.

(2) save_on_respawn was also declared but never read. Added a new
    @SubscribeEvent PlayerEvent.PlayerRespawnEvent handler that calls
    snapshotAndQueueSave(player, 'RESPAWN') after the respawn completes.
    This captures the post-death state AFTER keeping-charms / Corail
    Tombstone / similar mods have restored their preserved items, so
    PlayerSync's DB row reflects the actual post-respawn inventory rather
    than the pre-drop snapshot from onPlayerDeath.

    Excludes end-portal exit (isEndConquered) since that's not a death
    respawn — no need to overwrite.

Combined effect: if a player dies, charm-keeps items, respawns, the DB
ends up with:
  t=0  death snapshot (pre-drop, full inventory)
  t=X  respawn snapshot (post-drop, kept items + whatever charm restored)
The respawn snapshot overwrites the death one by virtue of running later.
A disconnect between t=0 and t=X still saves via onPlayerLogout anyway,
so no loss window opens.

No change to the duplication-safety guarantees from Phases 15-18:
onPlayerDeath still checks event.isCanceled() for ReviveMe, the RESPAWN
snapshot goes through the normal snapshotAndQueueSave pipeline with all
the P0-a/b/c guards and the 2-phase-commit logout_started_at tracking.

Answer to the user's question: the keep-charm inventory loss is
overwhelmingly likely to be a ReviveMe x Twilight Forest event-priority
bug outside PlayerSync's control, but this commit exposes two levers
(save_on_death, save_on_respawn) that let admins test whether PlayerSync
is contributing — setting save_on_death=false should make the symptom
unchanged if the root cause is external.
This commit is contained in:
laforetbrut 2026-04-22 20:15:30 +02:00
parent 6c986faa3f
commit be816cb359

View File

@ -1136,6 +1136,32 @@ public class VanillaSync {
snapshotAndQueueSave(event.getEntity(), "SaveToFile");
}
/**
* PHASE 19: optional save on respawn gated by {@code save_on_respawn}.
* Runs AFTER the respawn is complete so the snapshot captures the final
* post-death inventory (vanilla drops + whatever keeping-charms preserved).
* This OVERWRITES the pre-death snapshot taken in onPlayerDeath with the
* correct authoritative state, so the next restore sees the real inventory.
*
* <p>Essential when mods like Twilight Forest's Charm of Keeping or
* Corail Tombstone restore items on respawn without this event,
* PlayerSync's DB row stays at the pre-death snapshot until the next
* auto-save, and a quick disconnect loses the keep-charm state.
*/
@SubscribeEvent
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
try {
if (!JdbcConfig.SAVE_ON_RESPAWN.get()) return;
if (event.isEndConquered()) return; // End-portal exit, not a death respawn
Player player = event.getEntity();
SyncLogger.playerEvent(player.getUUID().toString(), "RESPAWN",
"Snapshot post-respawn inventory (keeping-charm / tombstone mods)");
snapshotAndQueueSave(player, "RESPAWN");
} catch (Exception e) {
PlayerSync.LOGGER.warn("[respawn-save] trigger failed: {}", e.getMessage());
}
}
/**
* Phase 4: optional save on dimension change gated by
* {@code save_on_dimension_change} config. Protects against mid-teleport
@ -2372,9 +2398,20 @@ public class VanillaSync {
// Always cache curios on death (API returns empty for dead players later)
CuriosCache.tryStoreCuriosToCache(player);
// PHASE 19: honour save_on_death config. Keeping-charm / death-drop-replacement
// mods (Twilight Forest Charm of Keeping, Corail Tombstone items, etc.) run
// their own event handlers during LivingDeathEvent. When their priority is
// higher than ours (LOW), they've already moved items out of the drops list
// our snapshot at this point captures the post-keep inventory, which is
// usually the desired behaviour.
// If admins diagnose a keeping-charm interaction, setting save_on_death=false
// disables this snapshot entirely; the normal onPlayerLogout save still fires
// on disconnect and captures the post-respawn state.
if (!JdbcConfig.SAVE_ON_DEATH.get()) return;
// Immediately save ALL player data on death (snapshot + async).
// LivingDeathEvent fires BEFORE items are dropped, so the snapshot captures
// the full pre-death inventory including backpack contents.
// LivingDeathEvent fires BEFORE vanilla items are dropped, so the snapshot
// captures whatever keeping-charms have already reserved + the rest.
// This protects against: server crash after death, network disconnect before
// onPlayerLogout fires, or any scenario where the logout handler is skipped.
// The normal logout save will overwrite this with the final post-death state.