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 }