package net.minecraft.server; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; // CraftBukkit start import java.util.UUID; import org.bukkit.craftbukkit.entity.CraftPlayer; // CraftBukkit end public class WorldNBTStorage implements IDataManager, IPlayerFileData { private static final Logger b = LogManager.getLogger(); private final File baseDir; private final File playerDir; private final File dataDir; private final long sessionId = MinecraftServer.aw(); private final String g; private final DefinedStructureManager h; protected final DataConverterManager a; private UUID uuid = null; // CraftBukkit public WorldNBTStorage(File file, String s, boolean flag, DataConverterManager dataconvertermanager) { this.a = dataconvertermanager; this.baseDir = new File(file, s); this.baseDir.mkdirs(); this.playerDir = new File(this.baseDir, "playerdata"); this.dataDir = new File(this.baseDir, "data"); this.dataDir.mkdirs(); this.g = s; if (flag) { this.playerDir.mkdirs(); this.h = new DefinedStructureManager((new File(this.baseDir, "structures")).toString(), dataconvertermanager); } else { this.h = null; } this.i(); } private void i() { try { File file = new File(this.baseDir, "session.lock"); DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(file)); try { dataoutputstream.writeLong(this.sessionId); } finally { dataoutputstream.close(); } } catch (IOException ioexception) { ioexception.printStackTrace(); throw new RuntimeException("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot } } @Override public File getDirectory() { return this.baseDir; } @Override public void checkSession() throws ExceptionWorldConflict { try { File file = new File(this.baseDir, "session.lock"); DataInputStream datainputstream = new DataInputStream(new FileInputStream(file)); try { if (datainputstream.readLong() != this.sessionId) { throw new ExceptionWorldConflict("The save for world located at " + this.baseDir + " is being accessed from another location, aborting"); // Spigot } } finally { datainputstream.close(); } } catch (IOException ioexception) { throw new ExceptionWorldConflict("Failed to check session lock for world located at " + this.baseDir + ", aborting. Stop the server and delete the session.lock in this world to prevent further issues."); // Spigot } } @Override public IChunkLoader createChunkLoader(WorldProvider worldprovider) { throw new RuntimeException("Old Chunk Storage is no longer supported."); } @Override @Nullable public WorldData getWorldData() { File file = new File(this.baseDir, "level.dat"); if (file.exists()) { WorldData worlddata = WorldLoader.a(file, this.a); if (worlddata != null) { return worlddata; } } file = new File(this.baseDir, "level.dat_old"); return file.exists() ? WorldLoader.a(file, this.a) : null; } @Override public void saveWorldData(WorldData worlddata, @Nullable NBTTagCompound nbttagcompound) { NBTTagCompound nbttagcompound1 = worlddata.a(nbttagcompound); NBTTagCompound nbttagcompound2 = new NBTTagCompound(); nbttagcompound2.set("Data", nbttagcompound1); try { File file = new File(this.baseDir, "level.dat_new"); File file1 = new File(this.baseDir, "level.dat_old"); File file2 = new File(this.baseDir, "level.dat"); NBTCompressedStreamTools.a(nbttagcompound2, (new FileOutputStream(file))); if (file1.exists()) { file1.delete(); } file2.renameTo(file1); if (file2.exists()) { file2.delete(); } file.renameTo(file2); if (file.exists()) { file.delete(); } } catch (Exception exception) { exception.printStackTrace(); } } @Override public void saveWorldData(WorldData worlddata) { this.saveWorldData(worlddata, (NBTTagCompound) null); } @Override public void save(EntityHuman entityhuman) { try { NBTTagCompound nbttagcompound = entityhuman.e(new NBTTagCompound()); File file = new File(this.playerDir, entityhuman.bf() + ".dat.tmp"); File file1 = new File(this.playerDir, entityhuman.bf() + ".dat"); NBTCompressedStreamTools.a(nbttagcompound, (new FileOutputStream(file))); if (file1.exists()) { file1.delete(); } file.renameTo(file1); } catch (Exception exception) { WorldNBTStorage.b.warn("Failed to save player data for {}", new Object[] { entityhuman.getName()}); } } @Override @Nullable public NBTTagCompound load(EntityHuman entityhuman) { NBTTagCompound nbttagcompound = null; try { File file = new File(this.playerDir, entityhuman.bf() + ".dat"); // Spigot Start boolean usingWrongFile = false; if (org.bukkit.Bukkit.getOnlineMode() && !file.exists()) { // Paper - Check online mode first file = new File(this.playerDir, EntityHuman.offlinePlayerUUID(entityhuman.getName(), false) + ".dat"); if (file.exists()) { usingWrongFile = true; org.bukkit.Bukkit.getServer().getLogger().warning( "Using offline mode UUID file for player " + entityhuman.getName() + " as it is the only copy we can find." ); } else { file = new File(this.playerDir, EntityHuman.offlinePlayerUUID(entityhuman.getName()) + ".dat"); if (file.exists()) { usingWrongFile = true; org.bukkit.Bukkit.getServer().getLogger().warning( "Using offline mode UUID file for player " + entityhuman.getName() + " as it is the only copy we can find." ); } } } // Spigot End if (file.exists() && file.isFile()) { nbttagcompound = NBTCompressedStreamTools.a((new FileInputStream(file))); } // Spigot Start if (usingWrongFile) { file.renameTo( new File( file.getPath() + ".offline-read" ) ); } // Spigot End } catch (Exception exception) { WorldNBTStorage.b.warn("Failed to load player data for {}", new Object[] { entityhuman.getName()}); } if (nbttagcompound != null) { // CraftBukkit start if (entityhuman instanceof EntityPlayer) { CraftPlayer player = (CraftPlayer) entityhuman.getBukkitEntity(); // Only update first played if it is older than the one we have long modified = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat").lastModified(); if (modified < player.getFirstPlayed()) { player.setFirstPlayed(modified); } } // CraftBukkit end entityhuman.f(this.a.a(DataConverterTypes.PLAYER, nbttagcompound)); } return nbttagcompound; } // CraftBukkit start public NBTTagCompound getPlayerData(String s) { try { File file1 = new File(this.playerDir, s + ".dat"); if (file1.exists()) { return NBTCompressedStreamTools.a((new FileInputStream(file1))); } } catch (Exception exception) { b.warn("Failed to load player data for " + s); } return null; } // CraftBukkit end @Override public IPlayerFileData getPlayerFileData() { return this; } @Override public String[] getSeenPlayers() { String[] astring = this.playerDir.list(); if (astring == null) { astring = new String[0]; } for (int i = 0; i < astring.length; ++i) { if (astring[i].endsWith(".dat")) { astring[i] = astring[i].substring(0, astring[i].length() - 4); } } return astring; } @Override public void a() {} @Override public File getDataFile(String s) { return new File(this.dataDir, s + ".dat"); } @Override public DefinedStructureManager h() { return this.h; } // CraftBukkit start @Override public UUID getUUID() { if (uuid != null) return uuid; File file1 = new File(this.baseDir, "uid.dat"); if (file1.exists()) { DataInputStream dis = null; try { dis = new DataInputStream(new FileInputStream(file1)); return uuid = new UUID(dis.readLong(), dis.readLong()); } catch (IOException ex) { b.warn("Failed to read " + file1 + ", generating new random UUID", ex); } finally { if (dis != null) { try { dis.close(); } catch (IOException ex) { // NOOP } } } } uuid = UUID.randomUUID(); DataOutputStream dos = null; try { dos = new DataOutputStream(new FileOutputStream(file1)); dos.writeLong(uuid.getMostSignificantBits()); dos.writeLong(uuid.getLeastSignificantBits()); } catch (IOException ex) { b.warn("Failed to write " + file1, ex); } finally { if (dos != null) { try { dos.close(); } catch (IOException ex) { // NOOP } } } return uuid; } public File getPlayerDir() { return playerDir; } // CraftBukkit end }