/* ChunkClaim Plugin for Minecraft Bukkit Servers Copyright (C) 2012 Felix Schmidt This file is part of ChunkClaim. ChunkClaim is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ChunkClaim is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ChunkClaim. If not, see <http://www.gnu.org/licenses/>. */ package com.github.schmidtbochum.chunkclaim; import java.io.*; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import org.bukkit.Bukkit; public class FlatFileDataStore extends DataStore { private final static String playerDataFolderPath = dataLayerFolderPath + File.separator + "PlayerData"; private final static String worldDataFolderPath = dataLayerFolderPath + File.separator + "ChunkData"; static boolean hasData() { File playerDataFolder = new File(playerDataFolderPath); File worldDataFolder = new File(worldDataFolderPath); return playerDataFolder.exists() || worldDataFolder.exists(); } FlatFileDataStore() throws Exception { this.initialize(); } @Override void initialize() throws Exception { //ensure data folders exist new File(playerDataFolderPath).mkdirs(); new File(worldDataFolderPath).mkdirs(); //load worlds List<String> worldNameList = ChunkClaim.plugin.config_worlds; for(int i = 0; i < worldNameList.size(); i++) { String worldName = worldNameList.get(i); if(Bukkit.getServer().getWorld(worldName) == null) continue; //skips unloaded worlds this.loadWorldData(worldName); } super.initialize(); } @Override synchronized void loadWorldData(String worldName) throws Exception { //create a new world object and register it ChunkWorld world = new ChunkWorld(worldName); this.worlds.put(worldName,world); //load chunks data into memory //get a list of all the chunks in the world folder String chunkDataFolderPath = worldDataFolderPath + File.separator + worldName; File chunkDataFolder = new File(chunkDataFolderPath); //ensure data folder exist chunkDataFolder.mkdirs(); File [] files = chunkDataFolder.listFiles(); for(int j = 0; j < files.length; j++) { //avoids folders if(files[j].isFile()) { String fileName = files[j].getName(); //get the chunk coordinates from the file name String[] chunkIdSep = fileName.split(";"); //skip the file if no separator was found if(fileName.equals(chunkIdSep[0])) continue; int x; int z; try { x = Integer.parseInt(chunkIdSep[0]); z = Integer.parseInt(chunkIdSep[1]); } catch (Exception e) { continue; //skip the file } BufferedReader inStream = null; try { inStream = new BufferedReader(new FileReader(files[j].getAbsolutePath())); //1. Line: Owner Name String ownerName = inStream.readLine(); //2. Line: Chunk Creation timestamp String claimDateString = inStream.readLine(); //3. Line: Number of modified blocks int modifiedBlocks = Integer.parseInt(inStream.readLine()); //4. Line: List of Builders String [] builderNames = inStream.readLine().split(";"); inStream.close(); DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); Date claimDate = dateFormat.parse(claimDateString); Chunk chunk = new Chunk(x,z,worldName,ownerName, claimDate, builderNames); chunk.modifiedDate = new Date(files[j].lastModified()); chunk.modifiedBlocks = modifiedBlocks; this.chunks.add(chunk); if(modifiedBlocks < this.minModifiedBlocks) { this.unusedChunks.add(chunk); } this.worlds.get(chunk.worldName).addChunk(chunk); chunk.inDataStore = true; } catch(Exception e) { ChunkClaim.addLogEntry("Unable to load data for chunk \"" + worldName + "\\" + files[j].getName() + "\": " + e.getMessage()); } //close the file try { if(inStream != null) inStream.close(); } catch(IOException exception) {} } } } @Override synchronized void writeChunkToStorage(Chunk chunk) { String fileName = chunk.x + ";" + chunk.z; String worldName = chunk.worldName; String chunkDataFolderPath = worldDataFolderPath + File.separator + worldName; //ensure that the world folder exists new File(chunkDataFolderPath).mkdirs(); BufferedWriter outStream = null; try { //open the chunks's file File chunkFile = new File(chunkDataFolderPath + File.separator + fileName); chunkFile.createNewFile(); outStream = new BufferedWriter(new FileWriter(chunkFile)); //write chunk to the file this.writeChunkData(chunk, outStream); //update date chunk.modifiedDate = new Date(chunkFile.lastModified()); } catch (Exception e) { ChunkClaim.addLogEntry("Unexpected exception saving data for chunk \"" + worldName + "\\" + fileName + "\": " + e.getMessage()); } //close the file try { if(outStream != null) outStream.close(); } catch(IOException exception) {} } synchronized private void writeChunkData(Chunk chunk, BufferedWriter outStream) throws IOException { //1. Line: Owner Name outStream.write(chunk.ownerName); outStream.newLine(); //2. Line: Chunk Creation timestamp DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); outStream.write(dateFormat.format(chunk.claimDate)); outStream.newLine(); //3. Line: Number of modified blocks outStream.write(String.valueOf(chunk.modifiedBlocks)); outStream.newLine(); //4. Line: List of Builders for(int i = 0; i < chunk.builderNames.size(); i++) { outStream.write(chunk.builderNames.get(i) + ";"); } outStream.newLine(); //filled line to prevent null outStream.write("=========="); outStream.newLine(); } @Override void deleteChunkFromSecondaryStorage(Chunk chunk) { String fileName = chunk.x + ";" + chunk.z; String worldName = chunk.worldName; String chunkDataFolderPath = worldDataFolderPath + File.separator + worldName; //remove from disk File chunkFile = new File(chunkDataFolderPath + File.separator + fileName); if(chunkFile.exists() && !chunkFile.delete()) { ChunkClaim.addLogEntry("Error: Unable to delete chunk file \"" + worldName + "\\" + fileName + "\"."); } } @Override synchronized PlayerData getPlayerDataFromStorage(String playerName) { File playerFile = new File(playerDataFolderPath + File.separator + playerName); PlayerData playerData = new PlayerData(); playerData.playerName = playerName; //if it doesn't exist as a file if(!playerFile.exists()) { //create a file with defaults this.savePlayerData(playerName, playerData); } //otherwise, read the file else { BufferedReader inStream = null; try { inStream = new BufferedReader(new FileReader(playerFile.getAbsolutePath())); //first line is first join date String firstJoinTimestampString = inStream.readLine(); //convert that to a date and store it DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); try { playerData.firstJoin = dateFormat.parse(firstJoinTimestampString); } catch(ParseException parseException) { ChunkClaim.addLogEntry("Unable to load first join date for \"" + playerFile.getName() + "\"."); playerData.firstJoin = null; } //first line is last login timestamp String lastLoginTimestampString = inStream.readLine(); //convert that to a date and store it try { playerData.lastLogin = dateFormat.parse(lastLoginTimestampString); } catch(ParseException parseException) { ChunkClaim.addLogEntry("Unable to load last login for \"" + playerFile.getName() + "\"."); playerData.lastLogin = null; } //third line is credits String creditsString = inStream.readLine(); playerData.credits = Float.parseFloat(creditsString); //fourth line is any bonus credits granted by administrators String bonusString = inStream.readLine(); playerData.bonus = Float.parseFloat(bonusString); //5. line: list of builders String [] b = inStream.readLine().split(";"); for(int i = 0; i < b.length; i++) { if(!b[i].equals("")) playerData.builderNames.add(b[i]); } inStream.close(); } catch(Exception e) { ChunkClaim.addLogEntry("Unable to load data for player \"" + playerName + "\": " + e.getMessage()); } //close the file try { if(inStream != null) inStream.close(); } catch(IOException exception) {} } return playerData; } @Override synchronized public void savePlayerData(String playerName, PlayerData playerData) { BufferedWriter outStream = null; try { //open the player's file File playerDataFile = new File(playerDataFolderPath + File.separator + playerName); playerDataFile.createNewFile(); outStream = new BufferedWriter(new FileWriter(playerDataFile)); //first line is first join date DateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss"); outStream.write(dateFormat.format(playerData.firstJoin)); outStream.newLine(); //second line is last login timestamp if(playerData.lastLogin == null) playerData.lastLogin = new Date(); outStream.write(dateFormat.format(playerData.lastLogin)); outStream.newLine(); //third line is credits outStream.write(String.valueOf(playerData.credits)); outStream.newLine(); //fourth line is bonus outStream.write(String.valueOf(playerData.bonus)); outStream.newLine(); //fifth line are trusted builders (trusted on all chunks); for(int i = 0; i < playerData.builderNames.size(); i++) { outStream.write(playerData.builderNames.get(i) + ";"); } outStream.newLine(); //filled line to prevent null outStream.write("=========="); outStream.newLine(); } catch(Exception e) { ChunkClaim.addLogEntry("Unexpected exception saving data for player \"" + playerName + "\": " + e.getMessage()); } //close the file try { if(outStream != null) outStream.close(); } catch(IOException exception) {} } @Override synchronized void close() {} }