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