    001    package net.minecraft.world.storage;
    002
    003    import java.io.DataInputStream;
    004    import java.io.DataOutputStream;
    005    import java.io.File;
    006    import java.io.FileInputStream;
    007    import java.io.FileOutputStream;
    008    import java.io.IOException;
    009    import java.util.logging.Logger;
    010
    011    import cpw.mods.fml.common.FMLCommonHandler;
    012    import net.minecraft.entity.player.EntityPlayer;
    013    import net.minecraft.nbt.CompressedStreamTools;
    014    import net.minecraft.nbt.NBTTagCompound;
    015    import net.minecraft.world.MinecraftException;
    016    import net.minecraft.world.WorldProvider;
    017    import net.minecraft.world.chunk.storage.IChunkLoader;
    018
    019    public class SaveHandler implements ISaveHandler, IPlayerFileData
    020    {
    021        /** Reference to the logger. */
    022        private static final Logger logger = Logger.getLogger("Minecraft");
    023
    024        /** The directory in which to save world data. */
    025        private final File worldDirectory;
    026
    027        /** The directory in which to save player data. */
    028        private final File playersDirectory;
    029        private final File mapDataDir;
    030
    031        /**
    032         * The time in milliseconds when this field was initialized. Stored in the session lock file.
    033         */
    034        private final long initializationTime = System.currentTimeMillis();
    035
    036        /** The directory name of the world */
    037        private final String saveDirectoryName;
    038
    039        public SaveHandler(File par1File, String par2Str, boolean par3)
    040        {
    041            this.worldDirectory = new File(par1File, par2Str);
    042            this.worldDirectory.mkdirs();
    043            this.playersDirectory = new File(this.worldDirectory, "players");
    044            this.mapDataDir = new File(this.worldDirectory, "data");
    045            this.mapDataDir.mkdirs();
    046            this.saveDirectoryName = par2Str;
    047
    048            if (par3)
    049            {
    050                this.playersDirectory.mkdirs();
    051            }
    052
    053            this.setSessionLock();
    054        }
    055
    056        /**
    057         * Creates a session lock file for this process
    058         */
    059        private void setSessionLock()
    060        {
    061            try
    062            {
    063                File var1 = new File(this.worldDirectory, "session.lock");
    064                DataOutputStream var2 = new DataOutputStream(new FileOutputStream(var1));
    065
    066                try
    067                {
    068                    var2.writeLong(this.initializationTime);
    069                }
    070                finally
    071                {
    072                    var2.close();
    073                }
    074            }
    075            catch (IOException var7)
    076            {
    077                var7.printStackTrace();
    078                throw new RuntimeException("Failed to check session lock, aborting");
    079            }
    080        }
    081
    082        /**
    083         * gets the File object corresponding to the base directory of this save (saves/404 for a save called 404 etc)
    084         */
    085        protected File getSaveDirectory()
    086        {
    087            return this.worldDirectory;
    088        }
    089
    090        /**
    091         * Checks the session lock to prevent save collisions
    092         */
    093        public void checkSessionLock() throws MinecraftException
    094        {
    095            try
    096            {
    097                File var1 = new File(this.worldDirectory, "session.lock");
    098                DataInputStream var2 = new DataInputStream(new FileInputStream(var1));
    099
    100                try
    101                {
    102                    if (var2.readLong() != this.initializationTime)
    103                    {
    104                        throw new MinecraftException("The save is being accessed from another location, aborting");
    105                    }
    106                }
    107                finally
    108                {
    109                    var2.close();
    110                }
    111            }
    112            catch (IOException var7)
    113            {
    114                throw new MinecraftException("Failed to check session lock, aborting");
    115            }
    116        }
    117
    118        /**
    119         * Returns the chunk loader with the provided world provider
    120         */
    121        public IChunkLoader getChunkLoader(WorldProvider par1WorldProvider)
    122        {
    123            throw new RuntimeException("Old Chunk Storage is no longer supported.");
    124        }
    125
    126        /**
    127         * Loads and returns the world info
    128         */
    129        public WorldInfo loadWorldInfo()
    130        {
    131            File var1 = new File(this.worldDirectory, "level.dat");
    132            NBTTagCompound var2;
    133            NBTTagCompound var3;
    134
    135            WorldInfo worldInfo = null;
    136
    137            if (var1.exists())
    138            {
    139                try
    140                {
    141                    var2 = CompressedStreamTools.readCompressed(new FileInputStream(var1));
    142                    var3 = var2.getCompoundTag("Data");
    143                    worldInfo = new WorldInfo(var3);
    144                    FMLCommonHandler.instance().handleWorldDataLoad(this, worldInfo, var2);
    145                    return worldInfo;
    146                }
    147                catch (Exception var5)
    148                {
    149                    if (FMLCommonHandler.instance().shouldServerBeKilledQuietly())
    150                    {
    151                        throw (RuntimeException)var5;
    152                    }
    153                    var5.printStackTrace();
    154                }
    155            }
    156
    157            var1 = new File(this.worldDirectory, "level.dat_old");
    158
    159            if (var1.exists())
    160            {
    161                try
    162                {
    163                    var2 = CompressedStreamTools.readCompressed(new FileInputStream(var1));
    164                    var3 = var2.getCompoundTag("Data");
    165                    worldInfo = new WorldInfo(var3);
    166                    FMLCommonHandler.instance().handleWorldDataLoad(this, worldInfo, var2);
    167                    return worldInfo;
    168                }
    169                catch (Exception var4)
    170                {
    171                    var4.printStackTrace();
    172                }
    173            }
    174
    175            return null;
    176        }
    177
    178        /**
    179         * Saves the given World Info with the given NBTTagCompound as the Player.
    180         */
    181        public void saveWorldInfoWithPlayer(WorldInfo par1WorldInfo, NBTTagCompound par2NBTTagCompound)
    182        {
    183            NBTTagCompound var3 = par1WorldInfo.cloneNBTCompound(par2NBTTagCompound);
    184            NBTTagCompound var4 = new NBTTagCompound();
    185            var4.setTag("Data", var3);
    186
    187            FMLCommonHandler.instance().handleWorldDataSave(this, par1WorldInfo, var4);
    188
    189            try
    190            {
    191                File var5 = new File(this.worldDirectory, "level.dat_new");
    192                File var6 = new File(this.worldDirectory, "level.dat_old");
    193                File var7 = new File(this.worldDirectory, "level.dat");
    194                CompressedStreamTools.writeCompressed(var4, new FileOutputStream(var5));
    195
    196                if (var6.exists())
    197                {
    198                    var6.delete();
    199                }
    200
    201                var7.renameTo(var6);
    202
    203                if (var7.exists())
    204                {
    205                    var7.delete();
    206                }
    207
    208                var5.renameTo(var7);
    209
    210                if (var5.exists())
    211                {
    212                    var5.delete();
    213                }
    214            }
    215            catch (Exception var8)
    216            {
    217                var8.printStackTrace();
    218            }
    219        }
    220
    221        /**
    222         * Saves the passed in world info.
    223         */
    224        public void saveWorldInfo(WorldInfo par1WorldInfo)
    225        {
    226            NBTTagCompound var2 = par1WorldInfo.getNBTTagCompound();
    227            NBTTagCompound var3 = new NBTTagCompound();
    228            var3.setTag("Data", var2);
    229
    230            FMLCommonHandler.instance().handleWorldDataSave(this, par1WorldInfo, var3);
    231
    232            try
    233            {
    234                File var4 = new File(this.worldDirectory, "level.dat_new");
    235                File var5 = new File(this.worldDirectory, "level.dat_old");
    236                File var6 = new File(this.worldDirectory, "level.dat");
    237                CompressedStreamTools.writeCompressed(var3, new FileOutputStream(var4));
    238
    239                if (var5.exists())
    240                {
    241                    var5.delete();
    242                }
    243
    244                var6.renameTo(var5);
    245
    246                if (var6.exists())
    247                {
    248                    var6.delete();
    249                }
    250
    251                var4.renameTo(var6);
    252
    253                if (var4.exists())
    254                {
    255                    var4.delete();
    256                }
    257            }
    258            catch (Exception var7)
    259            {
    260                var7.printStackTrace();
    261            }
    262        }
    263
    264        /**
    265         * Writes the player data to disk from the specified PlayerEntityMP.
    266         */
    267        public void writePlayerData(EntityPlayer par1EntityPlayer)
    268        {
    269            try
    270            {
    271                NBTTagCompound var2 = new NBTTagCompound();
    272                par1EntityPlayer.writeToNBT(var2);
    273                File var3 = new File(this.playersDirectory, par1EntityPlayer.username + ".dat.tmp");
    274                File var4 = new File(this.playersDirectory, par1EntityPlayer.username + ".dat");
    275                CompressedStreamTools.writeCompressed(var2, new FileOutputStream(var3));
    276
    277                if (var4.exists())
    278                {
    279                    var4.delete();
    280                }
    281
    282                var3.renameTo(var4);
    283            }
    284            catch (Exception var5)
    285            {
    286                logger.warning("Failed to save player data for " + par1EntityPlayer.username);
    287            }
    288        }
    289
    290        /**
    291         * Reads the player data from disk into the specified PlayerEntityMP.
    292         */
    293        public void readPlayerData(EntityPlayer par1EntityPlayer)
    294        {
    295            NBTTagCompound var2 = this.getPlayerData(par1EntityPlayer.username);
    296
    297            if (var2 != null)
    298            {
    299                par1EntityPlayer.readFromNBT(var2);
    300            }
    301        }
    302
    303        /**
    304         * Gets the player data for the given playername as a NBTTagCompound.
    305         */
    306        public NBTTagCompound getPlayerData(String par1Str)
    307        {
    308            try
    309            {
    310                File var2 = new File(this.playersDirectory, par1Str + ".dat");
    311
    312                if (var2.exists())
    313                {
    314                    return CompressedStreamTools.readCompressed(new FileInputStream(var2));
    315                }
    316            }
    317            catch (Exception var3)
    318            {
    319                logger.warning("Failed to load player data for " + par1Str);
    320            }
    321
    322            return null;
    323        }
    324
    325        /**
    326         * returns null if no saveHandler is relevent (eg. SMP)
    327         */
    328        public IPlayerFileData getSaveHandler()
    329        {
    330            return this;
    331        }
    332
    333        /**
    334         * Returns an array of usernames for which player.dat exists for.
    335         */
    336        public String[] getAvailablePlayerDat()
    337        {
    338            String[] var1 = this.playersDirectory.list();
    339
    340            for (int var2 = 0; var2 < var1.length; ++var2)
    341            {
    342                if (var1[var2].endsWith(".dat"))
    343                {
    344                    var1[var2] = var1[var2].substring(0, var1[var2].length() - 4);
    345                }
    346            }
    347
    348            return var1;
    349        }
    350
    351        /**
    352         * Called to flush all changes to disk, waiting for them to complete.
    353         */
    354        public void flush() {}
    355
    356        /**
    357         * Gets the file location of the given map
    358         */
    359        public File getMapFileFromName(String par1Str)
    360        {
    361            return new File(this.mapDataDir, par1Str + ".dat");
    362        }
    363
    364        /**
    365         * Returns the name of the directory where world information is saved.
    366         */
    367        public String getSaveDirectoryName()
    368        {
    369            return this.saveDirectoryName;
    370        }
    371    }