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