Java tutorial
package de.themoep.clancontrol; import de.themoep.utils.ConfigAccessor; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Bukkit Plugins - ${project.description} * Copyright (C) 2015 Max Lee (https://github.com/Phoenix616/) * <p/> * This program 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. * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ public class RegionManager { private ClanControl plugin; private Map<Integer, Map<Integer, OccupiedChunk>> chunkCoords; private Map<Integer, Map<Integer, Region>> regionCoords; private ConfigAccessor regionYml; // Side length of one region in chunks private int dimension; private String world; private int centerX; private int centerZ; private int mapradius; private double chunkRatio; public RegionManager(ClanControl plugin) { this.plugin = plugin; loadConfig(); loadRegions(); } private void loadConfig() { plugin.reloadConfig(); dimension = plugin.getConfig().getInt("regiondimension", 16); world = plugin.getConfig().getString("map.world", "world"); mapradius = plugin.getConfig().getInt("map.radius", 2560); centerX = plugin.getConfig().getInt("map.center.x", 0); centerZ = plugin.getConfig().getInt("map.center.z", 0); double chunkRatio = plugin.getConfig().getDouble("chunkratio", 1); this.chunkRatio = (chunkRatio > 1) ? chunkRatio / 100 : chunkRatio; } private void loadRegions() { regionYml = new ConfigAccessor(plugin, "regions.yml"); getStorage().reloadConfig(); regionCoords = new HashMap<Integer, Map<Integer, Region>>(); chunkCoords = new HashMap<Integer, Map<Integer, OccupiedChunk>>(); int regionCount = 0; int chunkCount = 0; int regionFailedCount = 0; int chunkFailedCount = 0; plugin.getLogger().info("Start loading regions from storage!"); List<OccupiedChunk> noBeacons = new ArrayList<OccupiedChunk>(); FileConfiguration config = getStorage().getConfig(); for (String worldname : config.getKeys(false)) { ConfigurationSection worldsSect = config.getConfigurationSection(worldname); for (String xStr : worldsSect.getKeys(false)) { try { int regionX = Integer.parseInt(xStr); for (String zStr : worldsSect.getConfigurationSection(xStr).getKeys(false)) { try { int regionZ = Integer.parseInt(zStr); List<OccupiedChunk> occupiedChunks = new ArrayList<OccupiedChunk>(); ConfigurationSection chunksSect = worldsSect .getConfigurationSection(xStr + "." + zStr + ".chunks"); if (chunksSect != null) { for (String chunkXStr : chunksSect.getKeys(false)) { try { int chunkX = Integer.parseInt(chunkXStr); for (String chunkZStr : chunksSect.getConfigurationSection(chunkXStr) .getKeys(false)) { try { int chunkZ = Integer.parseInt(chunkZStr); ConfigurationSection chunkSect = chunksSect .getConfigurationSection(chunkX + "." + chunkZ); Block beacon = plugin.getServer().getWorld(worldname).getBlockAt( chunkSect.getInt("beacon.x"), chunkSect.getInt("beacon.y"), chunkSect.getInt("beacon.z")); OccupiedChunk chunk = new OccupiedChunk(worldname, chunkX, chunkZ, chunkSect.getString("clan"), beacon.getX(), beacon.getY(), beacon.getZ()); occupiedChunks.add(chunk); if (!chunkCoords.containsKey(chunkX)) { chunkCoords.put(chunkX, new HashMap<Integer, OccupiedChunk>()); } chunkCoords.get(chunkX).put(chunkZ, chunk); if (beacon.getType() == Material.BEACON) { chunkCount++; } else { plugin.getLogger().warning("No beacon found at " + beacon.getX() + "/" + beacon.getY() + "/" + beacon.getZ() + " for chunk " + chunkX + " - " + chunkZ + " in region " + regionX + "-" + regionZ + "!"); plugin.getLogger() .info("Removing chunk " + chunkX + " - " + chunkZ + " in region " + regionX + "-" + regionZ + " after loading!"); noBeacons.add(chunk); chunkFailedCount++; } } catch (NumberFormatException e) { plugin.getLogger() .severe("Error while loading chunks of region " + regionX + "-" + regionZ + " from the regions.yml! '" + chunkZStr + "' is not a valid integer!"); chunkFailedCount++; } } } catch (NumberFormatException e) { plugin.getLogger() .severe("Error while loading chunks of region " + regionX + "-" + regionZ + " from the regions.yml! '" + chunkXStr + "' is not a valid integer!"); chunkFailedCount++; } } } String statusStr = worldsSect.getString(xStr + "." + zStr + ".status", "FREE"); try { RegionStatus status = RegionStatus.valueOf(statusStr); String controller = worldsSect.getString(xStr + "." + zStr + ".controller", ""); Region region = newRegion(worldname, regionX, regionZ, status, controller, occupiedChunks); if (region != null) { regionCount++; } else { plugin.getLogger().severe("Failed to load region " + regionX + "-" + regionZ + "! It already exists in the RegionManager? Please contact the dev asap as this should not be able to happen!"); regionFailedCount++; } } catch (IllegalArgumentException e) { plugin.getLogger().severe("Failed to load region " + regionX + "-" + regionZ + "! '" + statusStr + "' is not a valid RegionStatus!"); regionFailedCount++; } } catch (NumberFormatException e) { plugin.getLogger().severe( "Error while loading the regions.yml! '" + zStr + "' is not a valid integer!"); regionFailedCount++; } } } catch (NumberFormatException e) { plugin.getLogger() .severe("Error while loading the regions.yml! '" + xStr + "' is not a valid integer!"); regionFailedCount++; } } } plugin.getLogger() .info("Successfully loaded " + regionCount + " regions with " + chunkCount + " occupied chunks!"); if (regionFailedCount > 0) { plugin.getLogger().warning("Failed to load " + regionFailedCount + " regions!"); } if (chunkFailedCount > 0) { plugin.getLogger().warning("Failed to load " + chunkFailedCount + " chunks!"); } if (noBeacons.size() > 0) { plugin.getLogger().info("Removing chunks without beacons from the region manager!"); for (OccupiedChunk chunk : noBeacons) { unregisterChunk(chunk); } } } /** * Registers a beacon for a clan * @param clan * @param location * @return True if it got registered, false if not (e.g. when the location is outside the board or the chunk is already registered) */ public boolean registerBeacon(String clan, Location location) { if (getChunk(location) == null) { if (Math.abs((int) (mapradius / dimension) * dimension + centerX) < Math.abs(location.getBlockX()) || Math.abs((int) (mapradius / dimension) * dimension + centerZ) < Math .abs(location.getBlockZ())) { return false; } OccupiedChunk chunk = new OccupiedChunk(location, clan); if (!chunkCoords.containsKey(chunk.getX())) { chunkCoords.put(chunk.getX(), new HashMap<Integer, OccupiedChunk>()); } else if (chunkCoords.get(chunk.getX()).containsKey(chunk.getZ())) { return false; } Region region = getRegion(chunk); if (region != null) { RegionStatus result = region.addChunk(chunk); if (result != null) { chunkCoords.get(chunk.getX()).put(chunk.getZ(), chunk); return true; } } } return false; } public boolean unregisterChunk(OccupiedChunk chunk) { if (chunkCoords.containsKey(chunk.getX()) && chunkCoords.get(chunk.getX()).containsKey(chunk.getZ()) && chunk.equals(chunkCoords.get(chunk.getX()).get(chunk.getZ()))) { chunkCoords.get(chunk.getX()).remove(chunk.getZ()); Region region = getRegion(chunk); if (region != null) { region.removeChunk(chunk); return true; } } return false; } /** * Get the OccupiedChunk from a location * @param location * @return The OccupiedChunk; null if the chunk isn't occupied */ public OccupiedChunk getChunk(Location location) { return getChunk(location.getChunk()); } /** * Get the OccupiedChunk from a chunk * @param chunk * @return The OccupiedChunk; null if the chunk isn't occupied */ public OccupiedChunk getChunk(Chunk chunk) { return getChunk(chunk.getWorld().getName(), chunk.getX(), chunk.getZ()); } /** * Get the OccupiedChunk from a chunks x and z coordinates * @param chunkX * @param chunkZ * @return The OccupiedChunk; null if the chunk isn't occupied */ public OccupiedChunk getChunk(String worldname, int chunkX, int chunkZ) { if (worldname.equalsIgnoreCase(world) && chunkCoords.containsKey(chunkX)) { if (chunkCoords.get(chunkX).containsKey(chunkZ)) { return chunkCoords.get(chunkX).get(chunkZ); } } return null; } /** * Get the Region from a Location * @param location * @return The Region this location is part of */ public Region getRegion(Location location) { return (location == null) ? null : getRegion(location.getChunk()); } /** * Get the Region from a Chunk * @param chunk * @return The Region this chunk is part of */ private Region getRegion(Chunk chunk) { return (chunk == null) ? null : getRegion(chunk.getWorld().getName(), chunkToRegionCoord(chunk.getX()), chunkToRegionCoord(chunk.getZ())); } /** * Get the Region from an OccupiedChunk * @param chunk * @return The Region this occupied chunk is part of */ public Region getRegion(OccupiedChunk chunk) { return (chunk == null) ? null : getRegion(chunk.getWorld().getName(), chunkToRegionCoord(chunk.getX()), chunkToRegionCoord(chunk.getZ())); } private int chunkToRegionCoord(int chunkCoord) { return ((chunkCoord < 0) ? chunkCoord - 16 : chunkCoord) / dimension; } /** * Get the Region from a worldname and x/z region coordinates * @param worldname * @param regionX * @param regionZ * @return The Region described by this worldname and x/z region coordinates; null if it is outside the board */ public Region getRegion(String worldname, int regionX, int regionZ) { if (Math.abs(mapradius + centerX) < Math.abs(regionX * dimension * 16) || Math.abs(mapradius + centerZ) < Math.abs(regionZ * dimension * 16)) { return null; } if (worldname.equalsIgnoreCase(world) && regionCoords.containsKey(regionX)) { if (regionCoords.get(regionX).containsKey(regionZ)) { return regionCoords.get(regionX).get(regionZ); } } return newRegion(worldname, regionX, regionZ); } /** * Convenience method for generating and registering new regions * @param worldname * @param regionX * @param regionZ * @return The newly created and registered region; null if there is already a region with these coords! */ private Region newRegion(String worldname, int regionX, int regionZ) { if (!regionCoords.containsKey(regionX)) { regionCoords.put(regionX, new HashMap<Integer, Region>()); } else if (regionCoords.get(regionX).containsKey(regionZ)) { return null; } Region region = new Region(this, worldname, regionX, regionZ); regionCoords.get(regionX).put(regionZ, region); return region; } /** * Convenience method for generating and registering new regions * @param worldname * @param regionX * @param regionZ * @return The newly created and registered region; null if there is already a region with these coords! */ private Region newRegion(String worldname, int regionX, int regionZ, RegionStatus status, String controller, List<OccupiedChunk> occupiedChunks) { if (!regionCoords.containsKey(regionX)) { regionCoords.put(regionX, new HashMap<Integer, Region>()); } else if (regionCoords.get(regionX).containsKey(regionZ)) { return null; } Region region = new Region(this, worldname, regionX, regionZ, status, controller, occupiedChunks); regionCoords.get(regionX).put(regionZ, region); return region; } /** * Recalculates the game board * @param region The region that changed */ public void recalculateBoard(Region region) { String oldController = region.getController(); RegionStatus oldStatus = region.getStatus(); String newController = region.calculateControl(chunkRatio); if (newController != null) { if (oldController.isEmpty() || oldController.equals(newController)) { if (oldStatus != region.getStatus()) { if (oldStatus == RegionStatus.FREE) { plugin.getServer().broadcastMessage(plugin.getTag() + " " + ChatColor.GREEN + plugin.getClanDisplay(newController) + ChatColor.GREEN + " hat die Region " + region.getX() + "/" + region.getZ() + " eingenommen!"); } else { plugin.getServer() .broadcastMessage(plugin.getTag() + ChatColor.GREEN + " Region " + region.getX() + "/" + region.getZ() + " (" + plugin.getClanDisplay(newController) + ChatColor.GREEN + ") " + ChatColor.DARK_GREEN + oldStatus + " -> " + region.getStatus()); } } } else if (newController.isEmpty()) { plugin.getServer() .broadcastMessage(plugin.getTag() + " " + ChatColor.GREEN + plugin.getClanDisplay(newController) + ChatColor.GREEN + " hat die Region " + region.getX() + "/" + region.getZ() + " aufgegeben!"); } else { plugin.getServer() .broadcastMessage(plugin.getTag() + " " + ChatColor.GREEN + plugin.getClanDisplay(newController) + ChatColor.GREEN + " hat die Region " + region.getX() + "/" + region.getZ() + " von " + plugin.getClanDisplay(oldController) + ChatColor.GREEN + " bernommen!"); } } } public ConfigAccessor getStorage() { return regionYml; } public World getWorld() { return Bukkit.getWorld(world); } /** * Get a map of all the regions * @param player The player who wants the map * @return A list of BaseComponent arrays with each line as an entry; empty list if there is no map in this world */ public List<BaseComponent[]> getRegionMap(Player player) { List<BaseComponent[]> msg = new ArrayList<BaseComponent[]>(); String worldname = player.getWorld().getName(); Region currentRegion = getRegion(player.getLocation()); String clan = plugin.getClan(player); if (worldname.equals(world)) { int xMin = (centerX - mapradius) / (dimension * 16); int xMax = (centerX + mapradius) / (dimension * 16); int zMin = (centerZ - mapradius) / (dimension * 16); int zMax = (centerZ + mapradius) / (dimension * 16); for (int z = zMin; z <= zMax; z++) { ComponentBuilder row = new ComponentBuilder(""); for (int x = xMin; x <= xMax; x++) { row.append(" "); Region region = getRegion(worldname, x, z); if (region != null) { String hoverText = ChatColor.AQUA + "Region " + x + "/" + z + ChatColor.RESET; hoverText += "\nStatus: " + StringUtils.capitalize(region.getStatus().toString().toLowerCase()); if (!region.getController().isEmpty()) { hoverText += "\nController: " + plugin.getClanDisplay(region.getController()); } if (region.equals(currentRegion)) { row.append("x"); hoverText += "\n" + ChatColor.YELLOW + "You are here!" + ChatColor.RESET; } else { row.append("o"); } hoverText += "\n" + ChatColor.GRAY + ChatColor.ITALIC + "Click for more Info!" + ChatColor.RESET; if (region.getStatus() == RegionStatus.CONFLICT) { row.color(ChatColor.GOLD); } else if (region.getStatus() == RegionStatus.BORDER) { if (region.getController().equals(clan)) { row.color(ChatColor.GREEN); } else if (plugin.areAllied(clan, region.getController())) { row.color(ChatColor.AQUA); } else { row.color(ChatColor.RED); } } else if (region.getStatus() == RegionStatus.CENTER) { if (region.getController().equals(clan)) { row.color(ChatColor.DARK_GREEN); } else if (plugin.areAllied(clan, region.getController())) { row.color(ChatColor.DARK_AQUA); } else { row.color(ChatColor.DARK_RED); } } else { row.color(ChatColor.DARK_GRAY); } HoverEvent chunkHover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hoverText).create()); ClickEvent chunkClick = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/control region " + region.getX() + " " + region.getZ()); row.event(chunkHover); row.event(chunkClick); } else { row.append("-"); row.color(ChatColor.DARK_GRAY); } } msg.add(row.create()); } } return msg; } /** * Get a map of all the chunks in a region * @param player The player who wants the map * @param region The region to get the map of * @return A list of BaseComponent arrays with each line as an entry; empty list if there is no map in this world */ public List<BaseComponent[]> getChunkMap(Player player, Region region) { List<BaseComponent[]> msg = new ArrayList<BaseComponent[]>(); int xMin = region.getX() * dimension; int xMax = xMin + dimension; int zMin = region.getZ() * dimension; int zMax = zMin + dimension; String worldname = player.getWorld().getName(); Chunk currentChunk = player.getLocation().getChunk(); String clan = plugin.getClan(player); if (worldname.equals(world)) { for (int z = zMin; z < zMax; z++) { ComponentBuilder row = new ComponentBuilder(""); for (int x = xMin; x < xMax; x++) { row.append(" "); String hoverText = ChatColor.AQUA + "Chunk " + x + "/" + z; if (currentChunk != null && currentChunk.getX() == x && currentChunk.getZ() == z) { row.append("x"); hoverText += "\n" + ChatColor.YELLOW + "You are here!"; } else { row.append("o"); } OccupiedChunk chunk = getChunk(worldname, x, z); if (chunk != null) { if (chunk.getClan().equals(clan)) { row.color(ChatColor.GREEN); } else if (plugin.areAllied(clan, chunk.getClan())) { row.color(ChatColor.AQUA); } else { row.color(ChatColor.RED); } hoverText += ChatColor.RESET + "\nOwned by: " + plugin.getClanDisplay(chunk.getClan()); } else { row.color(ChatColor.DARK_GRAY); } HoverEvent clanHover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(hoverText).create()); row.event(clanHover); } msg.add(row.create()); } } return msg; } /** * Check if a player can build at the specific location * @param player * @param location * @return */ public boolean canBuild(Player player, Location location) { if (location.getWorld().equals(getWorld())) { OccupiedChunk chunk = getChunk(location); Region region = getRegion(location); String clan = plugin.getClan(player); if (chunk != null && !chunk.getClan().equals(clan)) { return false; } if (region.getStatus() == RegionStatus.CENTER && !region.getController().equals(clan)) { if (chunk != null && chunk.getClan().equals(clan)) { return true; } return false; } } return true; } }