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