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 }