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    }