-
Bug
-
Resolution: Fixed
-
1.20.2
-
None
-
Plausible
-
Save Data
-
Important
-
Platform
Notice: player.dat represents to player data file(<PlayerUUID>.dat) here, you'll find them in world\playerdata
Steps to Reproduce:
1. Run your test server.
2. Enter the server, /give yourself a bedrock or whatever, then quit the server.
3. Delete your player.dat in world\playerdata, it may appear as 3752d12c-3924-4d32-8407-6c19a4bd409f.dat
4. Enter the server again, and you'll find your stuff is gone even if your player.dat_old still stores your inventory. If you quit the server, your player.dat_old will be overwritten and you will never find your stuff back.
Expected Results:
In Step 4, your stuff shouldn't be gone since player.dat_old has stored it.
(Rename it into player.dat when it is not overwritten and return to the server, you'll see your stuff)
The behavior of player.dat_old should be the same as level.dat_old.
If you delete the level.dat of a world, reenter it and you'll see the data is still there since the game will read the data of the level.dat_old if level_dat doesn't exist.
Notes:
You can't reproduce this in single-player mode since your data is stored in another place.
This bug may cause player data lost if player.dat is corrupted somehow but player.dat_old isn't corrupted.
Code analysis:
This is from net.minecraft.world.WorldSaveHandler method, yarn mapping.
You'll see the game won't do anything if player.dat doesn't exist.
public NbtCompound loadPlayerData(PlayerEntity player) { NbtCompound nbtCompound = null; try { File file = new File(this.playerDataDir, player.getUuidAsString() + ".dat"); if (file.exists() && file.isFile()) { nbtCompound = NbtIo.readCompressed(file); } } catch (Exception var4) { LOGGER.warn("Failed to load player data for {}", player.getName().getString()); } ... }
However, the game does do something in loading level.dat. If it goes wrong, the game will restore the level.dat from level.dat_old.
This is from net.minecraft.world.level.storage.LevelStorage method, yarn mapping.
public CompletableFuture<List<LevelSummary>> loadSummaries(LevelList levels) { ... while(var3.hasNext()) { ... try { LevelSummary levelSummary = (LevelSummary)this.readLevelProperties(levelSave, this.createLevelDataParser(levelSave, bl)); return levelSummary != null ? levelSummary : null; ... } catch (StackOverflowError var5) { LOGGER.error(LogUtils.FATAL_MARKER, "Ran out of stack trying to read summary of {}. Assuming corruption; attempting to restore from from level.dat_old.", levelSave.getRootPath()); Util.backupAndReplace(levelSave.getLevelDatPath(), levelSave.getLevelDatOldPath(), levelSave.getCorruptedLevelDatPath(LocalDateTime.now()), true); throw var5; } }... }