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 }