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 }