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 }