com.wasteofplastic.askyblock.LevelCalcByChunk.java Source code

Java tutorial

Introduction

Here is the source code for com.wasteofplastic.askyblock.LevelCalcByChunk.java

Source

/*******************************************************************************
 * This file is part of ASkyBlock.
 *
 *     ASkyBlock 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.
 *
 *     ASkyBlock 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 ASkyBlock.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/

package com.wasteofplastic.askyblock;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.bukkit.ChatColor;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.material.MaterialData;
import org.bukkit.permissions.PermissionAttachmentInfo;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets;
import com.wasteofplastic.askyblock.events.IslandLevelEvent;

/**
 * A class that calculates the level of an island very quickly by copying island
 * chunks to a list and then processing asynchronously.
 * 
 * @author tastybento
 * 
 */
public class LevelCalcByChunk {

    private List<String> reportLines = new ArrayList<String>();

    public LevelCalcByChunk(ASkyBlock plugin, UUID targetPlayer, CommandSender asker) {
        this(plugin, targetPlayer, asker, false);
    }

    /**
     * Calculates the level of an island
     * @param plugin
     * @param targetPlayer - UUID of island owner or team member
     * @param sender - requester of the level calculation, if anyone
     * @param silent - if true, calculation will be done silently
     * @param report - provide a report to the asker
     */
    public LevelCalcByChunk(final ASkyBlock plugin, final UUID targetPlayer, final CommandSender sender,
            final boolean report) {
        if (report && plugin.getServer().getVersion().contains("(MC: 1.7")) {
            sender.sendMessage(ChatColor.RED + "This option is not available in V1.7 servers, sorry.");
            return;
        }
        //plugin.getLogger().info("DEBUG: running level calc " + silent);
        // Get player's island
        final Island island = plugin.getGrid().getIsland(targetPlayer,
                ((Player) sender).getWorld().getEnvironment());
        if (island != null) {
            // Get the permission multiplier if it is available
            Player player = plugin.getServer().getPlayer(targetPlayer);
            int multiplier = 1;
            if (player != null) {
                // Get permission multiplier                
                for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) {
                    if (perms.getPermission().startsWith(Settings.PERMPREFIX + "island.multiplier.")) {
                        // Get the max value should there be more than one
                        multiplier = Math.max(multiplier, Integer.valueOf(
                                perms.getPermission().split(Settings.PERMPREFIX + "island.multiplier.")[1]));
                    }
                    // Do some sanity checking
                    if (multiplier < 1) {
                        multiplier = 1;
                    }
                }
            }
            final int levelMultiplier = multiplier;
            // Get the handicap
            final int levelHandicap = island.getLevelHandicap();
            // Get the death handicap
            int deaths = plugin.getPlayers().getDeaths(targetPlayer);
            if (plugin.getPlayers().inTeam(targetPlayer)) {
                // Get the team leader's deaths
                deaths = plugin.getPlayers().getDeaths(plugin.getPlayers().getTeamLeader(targetPlayer));
                if (Settings.sumTeamDeaths) {
                    deaths = 0;
                    //plugin.getLogger().info("DEBUG: player is in team");
                    for (UUID member : plugin.getPlayers().getMembers(targetPlayer)) {
                        deaths += plugin.getPlayers().getDeaths(member);
                    }
                }
            }
            final int deathHandicap = deaths;
            // Check if player's island world is the nether or overworld and adjust accordingly
            final World world = plugin.getPlayers().getIslandLocation(targetPlayer).getWorld();
            // Get the chunks
            //long nano = System.nanoTime();
            Set<ChunkSnapshot> chunkSnapshot = new HashSet<ChunkSnapshot>();
            for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionSize()
                    + 16); x += 16) {
                for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionSize()
                        + 16); z += 16) {
                    if (!world.getBlockAt(x, 0, z).getChunk().isLoaded()) {
                        world.getBlockAt(x, 0, z).getChunk().load();
                        chunkSnapshot.add(world.getBlockAt(x, 0, z).getChunk().getChunkSnapshot());
                        world.getBlockAt(x, 0, z).getChunk().unload();
                    } else {
                        chunkSnapshot.add(world.getBlockAt(x, 0, z).getChunk().getChunkSnapshot());
                    }
                    //plugin.getLogger().info("DEBUG: getting chunk at " + x + ", " + z);
                }
            }
            //plugin.getLogger().info("DEBUG: time = " + (System.nanoTime() - nano) / 1000000 + " ms");
            //plugin.getLogger().info("DEBUG: size of chunk ss = " + chunkSnapshot.size());
            final Set<ChunkSnapshot> finalChunk = chunkSnapshot;
            final int worldHeight = world.getMaxHeight();
            //plugin.getLogger().info("DEBUG:world height = " +worldHeight);
            plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {

                @SuppressWarnings("deprecation")
                @Override
                public void run() {
                    // Logging
                    File log = null;
                    PrintWriter out = null;
                    List<MaterialData> mdLog = null;
                    List<MaterialData> uwLog = null;
                    List<MaterialData> noCountLog = null;
                    List<MaterialData> overflowLog = null;
                    if (Settings.levelLogging) {
                        log = new File(plugin.getDataFolder(), "level.log");
                        try {
                            if (log.exists()) {
                                out = new PrintWriter(new FileWriter(log, true));
                            } else {
                                out = new PrintWriter(log);
                            }
                        } catch (FileNotFoundException e) {
                            System.out.println("Level log (level.log) could not be opened...");
                            e.printStackTrace();
                        } catch (IOException e) {
                            System.out.println("Level log (level.log) could not be opened...");
                            e.printStackTrace();
                        }
                    }
                    if (Settings.levelLogging || report) {
                        mdLog = new ArrayList<MaterialData>();
                        uwLog = new ArrayList<MaterialData>();
                        noCountLog = new ArrayList<MaterialData>();
                        overflowLog = new ArrayList<MaterialData>();
                    }
                    // Copy the limits hashmap
                    HashMap<MaterialData, Integer> limitCount = new HashMap<MaterialData, Integer>(
                            Settings.blockLimits);
                    // Calculate the island score
                    int blockCount = 0;
                    int underWaterBlockCount = 0;
                    for (ChunkSnapshot chunk : finalChunk) {
                        for (int x = 0; x < 16; x++) {
                            // Check if the block coord is inside the protection zone and if not, don't count it
                            if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16
                                    + x >= island.getMinProtectedX() + island.getProtectionSize()) {
                                //plugin.getLogger().info("Block is outside protected area - x = " + (chunk.getX() * 16 + x));
                                continue;
                            }
                            for (int z = 0; z < 16; z++) {
                                // Check if the block coord is inside the protection zone and if not, don't count it
                                if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16
                                        + z >= island.getMinProtectedZ() + island.getProtectionSize()) {
                                    //plugin.getLogger().info("Block is outside protected area - z = " + (chunk.getZ() * 16 + z));
                                    continue;
                                }
                                for (int y = 0; y < worldHeight; y++) {
                                    int type = chunk.getBlockTypeId(x, y, z);
                                    int data = chunk.getBlockData(x, y, z);
                                    MaterialData md = new MaterialData(type, (byte) data);
                                    MaterialData generic = new MaterialData(type);
                                    if (type != 0) { // AIR
                                        if (limitCount.containsKey(md) && Settings.blockValues.containsKey(md)) {
                                            int count = limitCount.get(md);
                                            //plugin.getLogger().info("DEBUG: Count for non-generic " + md + " is " + count);
                                            if (count > 0) {
                                                limitCount.put(md, --count);
                                                if (Settings.sea_level > 0 && y <= Settings.sea_level) {
                                                    underWaterBlockCount += Settings.blockValues.get(md);
                                                    if (Settings.levelLogging || report) {
                                                        uwLog.add(md);
                                                    }
                                                } else {
                                                    blockCount += Settings.blockValues.get(md);
                                                    if (Settings.levelLogging || report) {
                                                        mdLog.add(md);
                                                    }
                                                }
                                            } else if (Settings.levelLogging || report) {
                                                overflowLog.add(md);
                                            }
                                        } else if (limitCount.containsKey(generic)
                                                && Settings.blockValues.containsKey(generic)) {
                                            int count = limitCount.get(generic);
                                            //plugin.getLogger().info("DEBUG: Count for generic " + generic + " is " + count);
                                            if (count > 0) {
                                                limitCount.put(generic, --count);
                                                if (Settings.sea_level > 0 && y <= Settings.sea_level) {
                                                    underWaterBlockCount += Settings.blockValues.get(generic);
                                                    if (Settings.levelLogging || report) {
                                                        uwLog.add(md);
                                                    }
                                                } else {
                                                    blockCount += Settings.blockValues.get(generic);
                                                    if (Settings.levelLogging || report) {
                                                        mdLog.add(md);
                                                    }
                                                }
                                            } else if (Settings.levelLogging || report) {
                                                overflowLog.add(md);
                                            }
                                        } else if (Settings.blockValues.containsKey(md)) {
                                            //plugin.getLogger().info("DEBUG: Adding " + md + " = " + Settings.blockValues.get(md));
                                            if (Settings.sea_level > 0 && y <= Settings.sea_level) {
                                                underWaterBlockCount += Settings.blockValues.get(md);
                                                if (Settings.levelLogging || report) {
                                                    uwLog.add(md);
                                                }
                                            } else {
                                                blockCount += Settings.blockValues.get(md);
                                                if (Settings.levelLogging || report) {
                                                    mdLog.add(md);
                                                }
                                            }
                                        } else if (Settings.blockValues.containsKey(generic)) {
                                            //plugin.getLogger().info("DEBUG: Adding " + generic + " = " + Settings.blockValues.get(generic));
                                            if (Settings.sea_level > 0 && y <= Settings.sea_level) {
                                                underWaterBlockCount += Settings.blockValues.get(generic);
                                                if (Settings.levelLogging || report) {
                                                    uwLog.add(md);
                                                }
                                            } else {
                                                blockCount += Settings.blockValues.get(generic);
                                                if (Settings.levelLogging || report) {
                                                    mdLog.add(md);
                                                }
                                            }
                                        } else if (Settings.levelLogging || report) {
                                            noCountLog.add(md);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    blockCount += (int) ((double) underWaterBlockCount * Settings.underWaterMultiplier);
                    //System.out.println("block count = "+blockCount);

                    final int score = (((blockCount * levelMultiplier) - (deathHandicap * Settings.deathpenalty))
                            / Settings.levelCost) - levelHandicap;
                    // Logging or report
                    if (Settings.levelLogging || report) {
                        // provide counts
                        Multiset<MaterialData> uwCount = HashMultiset.create(uwLog);
                        Multiset<MaterialData> mdCount = HashMultiset.create(mdLog);
                        Multiset<MaterialData> ncCount = HashMultiset.create(noCountLog);
                        Multiset<MaterialData> ofCount = HashMultiset.create(overflowLog);
                        reportLines.add("Level Log for island at " + island.getCenter());
                        if (sender instanceof Player) {
                            reportLines.add("Asker is " + sender.getName() + " ("
                                    + ((Player) sender).getUniqueId().toString() + ")");
                        } else {
                            reportLines.add("Asker is console");
                        }
                        reportLines.add("Target player UUID = " + targetPlayer.toString());
                        reportLines.add("Total block value count = " + String.format("%,d", blockCount));
                        reportLines.add("Level cost = " + Settings.levelCost);
                        reportLines.add("Level multiplier = " + levelMultiplier
                                + " (Player must be online to get a permission multiplier)");
                        reportLines.add("Schematic level handicap = " + levelHandicap
                                + " (level is reduced by this amount)");
                        reportLines.add("Deaths handicap = " + (deathHandicap * Settings.deathpenalty) + " ("
                                + deathHandicap + " deaths)");
                        reportLines.add("Level calculated = " + score);
                        reportLines.add("==================================");
                        int total = 0;
                        if (!uwCount.isEmpty()) {
                            reportLines.add("Underwater block count (Multiplier = x" + Settings.underWaterMultiplier
                                    + ") value");
                            reportLines.add(
                                    "Total number of underwater blocks = " + String.format("%,d", uwCount.size()));
                            Iterable<Multiset.Entry<MaterialData>> entriesSortedByCount = Multisets
                                    .copyHighestCountFirst(uwCount).entrySet();
                            Iterator<Entry<MaterialData>> it = entriesSortedByCount.iterator();
                            while (it.hasNext()) {
                                Entry<MaterialData> type = it.next();
                                int value = 0;
                                if (Settings.blockValues.containsKey(type)) {
                                    // Specific
                                    value = Settings.blockValues.get(type);
                                } else if (Settings.blockValues
                                        .containsKey(new MaterialData(type.getElement().getItemType()))) {
                                    // Generic
                                    value = Settings.blockValues
                                            .get(new MaterialData(type.getElement().getItemType()));
                                }
                                if (value > 0) {
                                    reportLines.add(type.getElement().toString() + ":"
                                            + String.format("%,d", type.getCount()) + " blocks x " + value + " = "
                                            + (value * type.getCount()));
                                    total += (value * type.getCount());
                                }
                            }
                            reportLines.add("Subtotal = " + total);
                            reportLines.add("==================================");
                        }
                        reportLines.add("Regular block count");
                        reportLines.add("Total number of blocks = " + String.format("%,d", mdCount.size()));
                        Iterable<Multiset.Entry<MaterialData>> entriesSortedByCount = Multisets
                                .copyHighestCountFirst(mdCount).entrySet();
                        Iterator<Entry<MaterialData>> it = entriesSortedByCount.iterator();
                        while (it.hasNext()) {
                            Entry<MaterialData> type = it.next();
                            int value = 0;
                            if (Settings.blockValues.containsKey(type)) {
                                // Specific
                                value = Settings.blockValues.get(type);
                            } else if (Settings.blockValues
                                    .containsKey(new MaterialData(type.getElement().getItemType()))) {
                                // Generic
                                value = Settings.blockValues.get(new MaterialData(type.getElement().getItemType()));
                            }
                            if (value > 0) {
                                reportLines.add(
                                        type.getElement().toString() + ":" + String.format("%,d", type.getCount())
                                                + " blocks x " + value + " = " + (value * type.getCount()));
                                total += (value * type.getCount());
                            }
                        }
                        reportLines.add("Total = " + total);
                        reportLines.add("==================================");
                        reportLines.add("Blocks not counted because they exceeded limits: "
                                + String.format("%,d", ofCount.size()));
                        entriesSortedByCount = Multisets.copyHighestCountFirst(ofCount).entrySet();
                        it = entriesSortedByCount.iterator();
                        while (it.hasNext()) {
                            Entry<MaterialData> type = it.next();
                            Integer limit = Settings.blockLimits.get(type.getElement());
                            String explain = ")";
                            if (limit == null) {
                                MaterialData generic = new MaterialData(type.getElement().getItemType());
                                limit = Settings.blockLimits.get(generic);
                                explain = " - All types)";
                            }
                            reportLines.add(type.getElement().toString() + ": "
                                    + String.format("%,d", type.getCount()) + " blocks (max " + limit + explain);
                        }
                        reportLines.add("==================================");
                        reportLines.add("Blocks on island that are not in blockvalues.yml");
                        reportLines.add("Total number = " + String.format("%,d", ncCount.size()));
                        entriesSortedByCount = Multisets.copyHighestCountFirst(ncCount).entrySet();
                        it = entriesSortedByCount.iterator();
                        while (it.hasNext()) {
                            Entry<MaterialData> type = it.next();
                            reportLines.add(type.getElement().toString() + ": "
                                    + String.format("%,d", type.getCount()) + " blocks");
                        }
                        reportLines.add("=================================");
                    }
                    if (out != null) {
                        // Write to file
                        for (String line : reportLines) {
                            out.println(line);
                        }
                        System.out.println("Finished writing level log.");
                        out.close();
                    }

                    // Return to main thread
                    plugin.getServer().getScheduler().runTask(plugin, new Runnable() {

                        @Override
                        public void run() {
                            //plugin.getLogger().info("DEBUG: updating player");
                            int oldLevel = plugin.getPlayers().getIslandLevel(targetPlayer);
                            // Update player and team mates
                            plugin.getPlayers().setIslandLevel(targetPlayer, score);
                            //plugin.getLogger().info("DEBUG: set island level, now trying to save player");
                            plugin.getPlayers().save(targetPlayer);
                            //plugin.getLogger().info("DEBUG: save player, now looking at team members");
                            // Update any team members too
                            if (plugin.getPlayers().inTeam(targetPlayer)) {
                                //plugin.getLogger().info("DEBUG: player is in team");
                                for (UUID member : plugin.getPlayers().getMembers(targetPlayer)) {
                                    //plugin.getLogger().info("DEBUG: updating team member level too");
                                    plugin.getPlayers().setIslandLevel(member, score);
                                    plugin.getPlayers().save(member);
                                }
                            }
                            //plugin.getLogger().info("DEBUG: finished team member saving");
                            if (sender != null) {
                                if (!(sender instanceof Player)) {
                                    // Console  
                                    if (!report) {
                                        sender.sendMessage(ChatColor.GREEN + plugin.myLocale().islandislandLevelis
                                                + " " + ChatColor.WHITE
                                                + plugin.getPlayers().getIslandLevel(targetPlayer));
                                    } else {
                                        for (String line : reportLines) {
                                            sender.sendMessage(line);
                                        }
                                    }
                                } else {
                                    if (!report) {
                                        // Tell offline team members the island level increased.
                                        if (plugin.getPlayers().getIslandLevel(targetPlayer) > oldLevel) {
                                            //plugin.getLogger().info("DEBUG: telling offline players");
                                            plugin.getMessages().tellOfflineTeam(targetPlayer,
                                                    ChatColor.GREEN
                                                            + plugin.myLocale(targetPlayer).islandislandLevelis
                                                            + " " + ChatColor.WHITE
                                                            + plugin.getPlayers().getIslandLevel(targetPlayer));
                                        }
                                        if (sender instanceof Player && ((Player) sender).isOnline()) {
                                            String message = ChatColor.GREEN
                                                    + plugin.myLocale(
                                                            ((Player) sender).getUniqueId()).islandislandLevelis
                                                    + " " + ChatColor.WHITE
                                                    + plugin.getPlayers().getIslandLevel(targetPlayer);
                                            if (Settings.deathpenalty != 0) {
                                                message += " " + plugin
                                                        .myLocale(((Player) sender).getUniqueId()).levelDeaths
                                                                .replace("[number]", String.valueOf(plugin
                                                                        .getPlayers().getDeaths(targetPlayer)));
                                            }
                                            sender.sendMessage(message);
                                        }
                                    } else {
                                        if (((Player) sender).isOnline()) {
                                            for (String line : reportLines) {
                                                sender.sendMessage(line);
                                            }
                                        }
                                    }
                                }
                            }
                            //plugin.getLogger().info("DEBUG: updating top ten");
                            // Only update top ten if the asker doesn't have this permission
                            if (!(sender instanceof Player)) {
                                return;
                            }
                            if (!(((Player) sender).getUniqueId().equals(targetPlayer)
                                    && sender.hasPermission(Settings.PERMPREFIX + "excludetopten"))) {
                                if (plugin.getPlayers().inTeam(targetPlayer)) {
                                    UUID leader = plugin.getPlayers().getTeamLeader(targetPlayer);
                                    if (leader != null) {
                                        TopTen.topTenAddEntry(leader, score);
                                    }
                                } else {
                                    TopTen.topTenAddEntry(targetPlayer, score);
                                }
                            }
                            // Fire the level event
                            Island island = plugin.getGrid().getIsland(targetPlayer,
                                    ((Player) sender).getWorld().getEnvironment());
                            final IslandLevelEvent event = new IslandLevelEvent(targetPlayer, island, score);
                            plugin.getServer().getPluginManager().callEvent(event);
                        }
                    });
                }
            });
        }
    }

}