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                    if (FMLCommonHandler.instance().shouldServerBeKilledQuietly())
142                    {
143                        throw (RuntimeException)var5;
144                    }
145                    var5.printStackTrace();
146                }
147            }
148    
149            var1 = new File(this.worldDirectory, "level.dat_old");
150    
151            if (var1.exists())
152            {
153                try
154                {
155                    var2 = CompressedStreamTools.readCompressed(new FileInputStream(var1));
156                    var3 = var2.getCompoundTag("Data");
157                    worldInfo = new WorldInfo(var3);
158                    FMLCommonHandler.instance().handleWorldDataLoad(this, worldInfo, var2);
159                    return worldInfo;
160                }
161                catch (Exception var4)
162                {
163                    var4.printStackTrace();
164                }
165            }
166    
167            return null;
168        }
169    
170        /**
171         * Saves the given World Info with the given NBTTagCompound as the Player.
172         */
173        public void saveWorldInfoWithPlayer(WorldInfo par1WorldInfo, NBTTagCompound par2NBTTagCompound)
174        {
175            NBTTagCompound var3 = par1WorldInfo.cloneNBTCompound(par2NBTTagCompound);
176            NBTTagCompound var4 = new NBTTagCompound();
177            var4.setTag("Data", var3);
178            FMLCommonHandler.instance().handleWorldDataSave(this, par1WorldInfo, var4);
179            try
180            {
181                File var5 = new File(this.worldDirectory, "level.dat_new");
182                File var6 = new File(this.worldDirectory, "level.dat_old");
183                File var7 = new File(this.worldDirectory, "level.dat");
184                CompressedStreamTools.writeCompressed(var4, new FileOutputStream(var5));
185    
186                if (var6.exists())
187                {
188                    var6.delete();
189                }
190    
191                var7.renameTo(var6);
192    
193                if (var7.exists())
194                {
195                    var7.delete();
196                }
197    
198                var5.renameTo(var7);
199    
200                if (var5.exists())
201                {
202                    var5.delete();
203                }
204            }
205            catch (Exception var8)
206            {
207                var8.printStackTrace();
208            }
209        }
210    
211        /**
212         * Saves the passed in world info.
213         */
214        public void saveWorldInfo(WorldInfo par1WorldInfo)
215        {
216            NBTTagCompound var2 = par1WorldInfo.getNBTTagCompound();
217            NBTTagCompound var3 = new NBTTagCompound();
218            var3.setTag("Data", var2);
219            FMLCommonHandler.instance().handleWorldDataSave(this, par1WorldInfo, var3);
220            try
221            {
222                File var4 = new File(this.worldDirectory, "level.dat_new");
223                File var5 = new File(this.worldDirectory, "level.dat_old");
224                File var6 = new File(this.worldDirectory, "level.dat");
225                CompressedStreamTools.writeCompressed(var3, new FileOutputStream(var4));
226    
227                if (var5.exists())
228                {
229                    var5.delete();
230                }
231    
232                var6.renameTo(var5);
233    
234                if (var6.exists())
235                {
236                    var6.delete();
237                }
238    
239                var4.renameTo(var6);
240    
241                if (var4.exists())
242                {
243                    var4.delete();
244                }
245            }
246            catch (Exception var7)
247            {
248                var7.printStackTrace();
249            }
250        }
251    
252        /**
253         * Writes the player data to disk from the specified PlayerEntityMP.
254         */
255        public void writePlayerData(EntityPlayer par1EntityPlayer)
256        {
257            try
258            {
259                NBTTagCompound var2 = new NBTTagCompound();
260                par1EntityPlayer.writeToNBT(var2);
261                File var3 = new File(this.playersDirectory, par1EntityPlayer.username + ".dat.tmp");
262                File var4 = new File(this.playersDirectory, par1EntityPlayer.username + ".dat");
263                CompressedStreamTools.writeCompressed(var2, new FileOutputStream(var3));
264    
265                if (var4.exists())
266                {
267                    var4.delete();
268                }
269    
270                var3.renameTo(var4);
271            }
272            catch (Exception var5)
273            {
274                logger.warning("Failed to save player data for " + par1EntityPlayer.username);
275            }
276        }
277    
278        /**
279         * Reads the player data from disk into the specified PlayerEntityMP.
280         */
281        public void readPlayerData(EntityPlayer par1EntityPlayer)
282        {
283            NBTTagCompound var2 = this.getPlayerData(par1EntityPlayer.username);
284    
285            if (var2 != null)
286            {
287                par1EntityPlayer.readFromNBT(var2);
288            }
289        }
290    
291        /**
292         * Gets the player data for the given playername as a NBTTagCompound.
293         */
294        public NBTTagCompound getPlayerData(String par1Str)
295        {
296            try
297            {
298                File var2 = new File(this.playersDirectory, par1Str + ".dat");
299    
300                if (var2.exists())
301                {
302                    return CompressedStreamTools.readCompressed(new FileInputStream(var2));
303                }
304            }
305            catch (Exception var3)
306            {
307                logger.warning("Failed to load player data for " + par1Str);
308            }
309    
310            return null;
311        }
312    
313        /**
314         * returns null if no saveHandler is relevent (eg. SMP)
315         */
316        public IPlayerFileData getSaveHandler()
317        {
318            return this;
319        }
320    
321        /**
322         * Returns an array of usernames for which player.dat exists for.
323         */
324        public String[] getAvailablePlayerDat()
325        {
326            String[] var1 = this.playersDirectory.list();
327    
328            for (int var2 = 0; var2 < var1.length; ++var2)
329            {
330                if (var1[var2].endsWith(".dat"))
331                {
332                    var1[var2] = var1[var2].substring(0, var1[var2].length() - 4);
333                }
334            }
335    
336            return var1;
337        }
338    
339        /**
340         * Called to flush all changes to disk, waiting for them to complete.
341         */
342        public void flush() {}
343    
344        /**
345         * Gets the file location of the given map
346         */
347        public File getMapFileFromName(String par1Str)
348        {
349            return new File(this.mapDataDir, par1Str + ".dat");
350        }
351    
352        /**
353         * Returns the name of the directory where world information is saved.
354         */
355        public String getSaveDirectoryName()
356        {
357            return this.saveDirectoryName;
358        }
359    }