Armor stands weren't handled well - if a player directed an armor stand, he may come back later to find some other items like paintings for example missing. Now if at the limit, no placing new armor stands or interact with (putting items on) existing stands.
890 lines
32 KiB
Java
890 lines
32 KiB
Java
/*
|
|
GriefPrevention Server Plugin for Minecraft
|
|
Copyright (C) 2012 Ryan Hamshire
|
|
|
|
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/>.
|
|
*/
|
|
|
|
package me.ryanhamshire.GriefPrevention;
|
|
|
|
import java.util.*;
|
|
|
|
import org.bukkit.*;
|
|
import org.bukkit.World.Environment;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.Player;
|
|
|
|
//represents a player claim
|
|
//creating an instance doesn't make an effective claim
|
|
//only claims which have been added to the datastore have any effect
|
|
public class Claim
|
|
{
|
|
//two locations, which together define the boundaries of the claim
|
|
//note that the upper Y value is always ignored, because claims ALWAYS extend up to the sky
|
|
Location lesserBoundaryCorner;
|
|
Location greaterBoundaryCorner;
|
|
|
|
//modification date. this comes from the file timestamp during load, and is updated with runtime changes
|
|
public Date modifiedDate;
|
|
|
|
//id number. unique to this claim, never changes.
|
|
Long id = null;
|
|
|
|
//ownerID. for admin claims, this is NULL
|
|
//use getOwnerName() to get a friendly name (will be "an administrator" for admin claims)
|
|
public UUID ownerID;
|
|
|
|
//list of players who (beyond the claim owner) have permission to grant permissions in this claim
|
|
public ArrayList<String> managers = new ArrayList<String>();
|
|
|
|
//permissions for this claim, see ClaimPermission class
|
|
private HashMap<String, ClaimPermission> playerIDToClaimPermissionMap = new HashMap<String, ClaimPermission>();
|
|
|
|
//whether or not this claim is in the data store
|
|
//if a claim instance isn't in the data store, it isn't "active" - players can't interract with it
|
|
//why keep this? so that claims which have been removed from the data store can be correctly
|
|
//ignored even though they may have references floating around
|
|
public boolean inDataStore = false;
|
|
|
|
public boolean areExplosivesAllowed = false;
|
|
|
|
//parent claim
|
|
//only used for claim subdivisions. top level claims have null here
|
|
public Claim parent = null;
|
|
|
|
//children (subdivisions)
|
|
//note subdivisions themselves never have children
|
|
public ArrayList<Claim> children = new ArrayList<Claim>();
|
|
|
|
//information about a siege involving this claim. null means no siege is impacting this claim
|
|
public SiegeData siegeData = null;
|
|
|
|
//following a siege, buttons/levers are unlocked temporarily. this represents that state
|
|
public boolean doorsOpen = false;
|
|
|
|
//whether or not this is an administrative claim
|
|
//administrative claims are created and maintained by players with the griefprevention.adminclaims permission.
|
|
public boolean isAdminClaim()
|
|
{
|
|
if(this.parent != null) return this.parent.isAdminClaim();
|
|
|
|
return (this.ownerID == null);
|
|
}
|
|
|
|
//accessor for ID
|
|
public Long getID()
|
|
{
|
|
return this.id;
|
|
}
|
|
|
|
//basic constructor, just notes the creation time
|
|
//see above declarations for other defaults
|
|
Claim()
|
|
{
|
|
this.modifiedDate = Calendar.getInstance().getTime();
|
|
}
|
|
|
|
//players may only siege someone when he's not in an admin claim
|
|
//and when he has some level of permission in the claim
|
|
public boolean canSiege(Player defender)
|
|
{
|
|
if(this.isAdminClaim()) return false;
|
|
|
|
if(this.allowAccess(defender) != null) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//removes any lava above sea level in a claim
|
|
//exclusionClaim is another claim indicating an sub-area to be excluded from this operation
|
|
//it may be null
|
|
public void removeSurfaceFluids(Claim exclusionClaim)
|
|
{
|
|
//don't do this for administrative claims
|
|
if(this.isAdminClaim()) return;
|
|
|
|
//don't do it for very large claims
|
|
if(this.getArea() > 10000) return;
|
|
|
|
//only in creative mode worlds
|
|
if(!GriefPrevention.instance.creativeRulesApply(this.lesserBoundaryCorner)) return;
|
|
|
|
Location lesser = this.getLesserBoundaryCorner();
|
|
Location greater = this.getGreaterBoundaryCorner();
|
|
|
|
if(lesser.getWorld().getEnvironment() == Environment.NETHER) return; //don't clean up lava in the nether
|
|
|
|
int seaLevel = 0; //clean up all fluids in the end
|
|
|
|
//respect sea level in normal worlds
|
|
if(lesser.getWorld().getEnvironment() == Environment.NORMAL) seaLevel = GriefPrevention.instance.getSeaLevel(lesser.getWorld());
|
|
|
|
for(int x = lesser.getBlockX(); x <= greater.getBlockX(); x++)
|
|
{
|
|
for(int z = lesser.getBlockZ(); z <= greater.getBlockZ(); z++)
|
|
{
|
|
for(int y = seaLevel - 1; y <= lesser.getWorld().getMaxHeight(); y++)
|
|
{
|
|
//dodge the exclusion claim
|
|
Block block = lesser.getWorld().getBlockAt(x, y, z);
|
|
if(exclusionClaim != null && exclusionClaim.contains(block.getLocation(), true, false)) continue;
|
|
|
|
if(block.getType() == Material.STATIONARY_LAVA || block.getType() == Material.WATER || block.getType() == Material.STATIONARY_WATER || block.getType() == Material.LAVA)
|
|
{
|
|
block.setType(Material.AIR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//determines whether or not a claim has surface lava
|
|
//used to warn players when they abandon their claims about automatic fluid cleanup
|
|
boolean hasSurfaceFluids()
|
|
{
|
|
Location lesser = this.getLesserBoundaryCorner();
|
|
Location greater = this.getGreaterBoundaryCorner();
|
|
|
|
//don't bother for very large claims, too expensive
|
|
if(this.getArea() > 10000) return false;
|
|
|
|
int seaLevel = 0; //clean up all fluids in the end
|
|
|
|
//respect sea level in normal worlds
|
|
if(lesser.getWorld().getEnvironment() == Environment.NORMAL) seaLevel = GriefPrevention.instance.getSeaLevel(lesser.getWorld());
|
|
|
|
for(int x = lesser.getBlockX(); x <= greater.getBlockX(); x++)
|
|
{
|
|
for(int z = lesser.getBlockZ(); z <= greater.getBlockZ(); z++)
|
|
{
|
|
for(int y = seaLevel - 1; y <= lesser.getWorld().getMaxHeight(); y++)
|
|
{
|
|
//dodge the exclusion claim
|
|
Block block = lesser.getWorld().getBlockAt(x, y, z);
|
|
|
|
if(block.getType() == Material.STATIONARY_LAVA || block.getType() == Material.WATER || block.getType() == Material.STATIONARY_WATER || block.getType() == Material.LAVA)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//main constructor. note that only creating a claim instance does nothing - a claim must be added to the data store to be effective
|
|
Claim(Location lesserBoundaryCorner, Location greaterBoundaryCorner, UUID ownerID, List<String> builderIDs, List<String> containerIDs, List<String> accessorIDs, List<String> managerIDs, Long id)
|
|
{
|
|
//modification date
|
|
this.modifiedDate = Calendar.getInstance().getTime();
|
|
|
|
//id
|
|
this.id = id;
|
|
|
|
//store corners
|
|
this.lesserBoundaryCorner = lesserBoundaryCorner;
|
|
this.greaterBoundaryCorner = greaterBoundaryCorner;
|
|
|
|
//owner
|
|
this.ownerID = ownerID;
|
|
|
|
//other permissions
|
|
for(String builderID : builderIDs)
|
|
{
|
|
if(builderID != null && !builderID.isEmpty())
|
|
{
|
|
this.playerIDToClaimPermissionMap.put(builderID, ClaimPermission.Build);
|
|
}
|
|
}
|
|
|
|
for(String containerID : containerIDs)
|
|
{
|
|
if(containerID != null && !containerID.isEmpty())
|
|
{
|
|
this.playerIDToClaimPermissionMap.put(containerID, ClaimPermission.Inventory);
|
|
}
|
|
}
|
|
|
|
for(String accessorID : accessorIDs)
|
|
{
|
|
if(accessorID != null && !accessorID.isEmpty())
|
|
{
|
|
this.playerIDToClaimPermissionMap.put(accessorID, ClaimPermission.Access);
|
|
}
|
|
}
|
|
|
|
for(String managerID : managerIDs)
|
|
{
|
|
if(managerID != null && !managerID.isEmpty())
|
|
{
|
|
this.managers.add(managerID);
|
|
}
|
|
}
|
|
}
|
|
|
|
//measurements. all measurements are in blocks
|
|
public int getArea()
|
|
{
|
|
int claimWidth = this.greaterBoundaryCorner.getBlockX() - this.lesserBoundaryCorner.getBlockX() + 1;
|
|
int claimHeight = this.greaterBoundaryCorner.getBlockZ() - this.lesserBoundaryCorner.getBlockZ() + 1;
|
|
|
|
return claimWidth * claimHeight;
|
|
}
|
|
|
|
public int getWidth()
|
|
{
|
|
return this.greaterBoundaryCorner.getBlockX() - this.lesserBoundaryCorner.getBlockX() + 1;
|
|
}
|
|
|
|
public int getHeight()
|
|
{
|
|
return this.greaterBoundaryCorner.getBlockZ() - this.lesserBoundaryCorner.getBlockZ() + 1;
|
|
}
|
|
|
|
//distance check for claims, distance in this case is a band around the outside of the claim rather then euclidean distance
|
|
public boolean isNear(Location location, int howNear)
|
|
{
|
|
Claim claim = new Claim
|
|
(new Location(this.lesserBoundaryCorner.getWorld(), this.lesserBoundaryCorner.getBlockX() - howNear, this.lesserBoundaryCorner.getBlockY(), this.lesserBoundaryCorner.getBlockZ() - howNear),
|
|
new Location(this.greaterBoundaryCorner.getWorld(), this.greaterBoundaryCorner.getBlockX() + howNear, this.greaterBoundaryCorner.getBlockY(), this.greaterBoundaryCorner.getBlockZ() + howNear),
|
|
null, new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), null);
|
|
|
|
return claim.contains(location, false, true);
|
|
}
|
|
|
|
//permissions. note administrative "public" claims have different rules than other claims
|
|
//all of these return NULL when a player has permission, or a String error message when the player doesn't have permission
|
|
public String allowEdit(Player player)
|
|
{
|
|
//if we don't know who's asking, always say no (i've been told some mods can make this happen somehow)
|
|
if(player == null) return "";
|
|
|
|
//special cases...
|
|
|
|
//admin claims need adminclaims permission only.
|
|
if(this.isAdminClaim())
|
|
{
|
|
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
|
}
|
|
|
|
//anyone with deleteclaims permission can modify non-admin claims at any time
|
|
else
|
|
{
|
|
if(player.hasPermission("griefprevention.deleteclaims")) return null;
|
|
}
|
|
|
|
//no resizing, deleting, and so forth while under siege
|
|
if(player.getUniqueId().equals(this.ownerID))
|
|
{
|
|
if(this.siegeData != null)
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NoModifyDuringSiege);
|
|
}
|
|
|
|
//otherwise, owners can do whatever
|
|
return null;
|
|
}
|
|
|
|
//permission inheritance for subdivisions
|
|
if(this.parent != null)
|
|
return this.parent.allowEdit(player);
|
|
|
|
//error message if all else fails
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.OnlyOwnersModifyClaims, this.getOwnerName());
|
|
}
|
|
|
|
private List<Material> placeableFarmingBlocksList = Arrays.asList(
|
|
Material.PUMPKIN_STEM,
|
|
Material.CROPS,
|
|
Material.MELON_STEM,
|
|
Material.CARROT,
|
|
Material.POTATO,
|
|
Material.NETHER_WARTS);
|
|
|
|
private boolean placeableForFarming(Material material)
|
|
{
|
|
return this.placeableFarmingBlocksList.contains(material);
|
|
}
|
|
|
|
//build permission check
|
|
public String allowBuild(Player player, Material material)
|
|
{
|
|
//if we don't know who's asking, always say no (i've been told some mods can make this happen somehow)
|
|
if(player == null) return "";
|
|
|
|
//when a player tries to build in a claim, if he's under siege, the siege may extend to include the new claim
|
|
GriefPrevention.instance.dataStore.tryExtendSiege(player, this);
|
|
|
|
//admin claims can always be modified by admins, no exceptions
|
|
if(this.isAdminClaim())
|
|
{
|
|
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
|
}
|
|
|
|
//no building while under siege
|
|
if(this.siegeData != null)
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildUnderSiege, this.siegeData.attacker.getName());
|
|
}
|
|
|
|
//no building while in pvp combat
|
|
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId());
|
|
if(playerData.inPvpCombat())
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildPvP);
|
|
}
|
|
|
|
//owners can make changes, or admins with ignore claims mode enabled
|
|
if(player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) return null;
|
|
|
|
//anyone with explicit build permission can make changes
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Build)) return null;
|
|
|
|
//also everyone is a member of the "public", so check for public permission
|
|
ClaimPermission permissionLevel = this.playerIDToClaimPermissionMap.get("public");
|
|
if(ClaimPermission.Build == permissionLevel) return null;
|
|
|
|
//allow for farming with /containertrust permission
|
|
if(this.allowContainers(player) == null)
|
|
{
|
|
//do allow for farming, if player has /containertrust permission
|
|
if(this.placeableForFarming(material))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
//subdivision permission inheritance
|
|
if(this.parent != null)
|
|
return this.parent.allowBuild(player, material);
|
|
|
|
//failure message for all other cases
|
|
String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoBuildPermission, this.getOwnerName());
|
|
if(player.hasPermission("griefprevention.ignoreclaims"))
|
|
reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement);
|
|
|
|
return reason;
|
|
}
|
|
|
|
private boolean hasExplicitPermission(Player player, ClaimPermission level)
|
|
{
|
|
String playerID = player.getUniqueId().toString();
|
|
Set<String> keys = this.playerIDToClaimPermissionMap.keySet();
|
|
Iterator<String> iterator = keys.iterator();
|
|
while(iterator.hasNext())
|
|
{
|
|
String identifier = iterator.next();
|
|
if(playerID.equalsIgnoreCase(identifier) && this.playerIDToClaimPermissionMap.get(identifier) == level) return true;
|
|
|
|
else if(identifier.startsWith("[") && identifier.endsWith("]"))
|
|
{
|
|
//drop the brackets
|
|
String permissionIdentifier = identifier.substring(1, identifier.length() - 1);
|
|
|
|
//defensive coding
|
|
if(permissionIdentifier == null || permissionIdentifier.isEmpty()) continue;
|
|
|
|
//check permission
|
|
if(player.hasPermission(permissionIdentifier) && this.playerIDToClaimPermissionMap.get(identifier) == level) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//break permission check
|
|
public String allowBreak(Player player, Material material)
|
|
{
|
|
//if under siege, some blocks will be breakable
|
|
if(this.siegeData != null || this.doorsOpen)
|
|
{
|
|
boolean breakable = false;
|
|
|
|
//search for block type in list of breakable blocks
|
|
for(int i = 0; i < GriefPrevention.instance.config_siege_blocks.size(); i++)
|
|
{
|
|
Material breakableMaterial = GriefPrevention.instance.config_siege_blocks.get(i);
|
|
if(breakableMaterial == material)
|
|
{
|
|
breakable = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//custom error messages for siege mode
|
|
if(!breakable)
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NonSiegeMaterial);
|
|
}
|
|
else if(player.getUniqueId().equals(this.ownerID))
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NoOwnerBuildUnderSiege);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
//if not under siege, build rules apply
|
|
return this.allowBuild(player, material);
|
|
}
|
|
|
|
//access permission check
|
|
public String allowAccess(Player player)
|
|
{
|
|
//following a siege where the defender lost, the claim will allow everyone access for a time
|
|
if(this.doorsOpen) return null;
|
|
|
|
//admin claims need adminclaims permission only.
|
|
if(this.isAdminClaim())
|
|
{
|
|
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
|
}
|
|
|
|
//claim owner and admins in ignoreclaims mode have access
|
|
if(player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) return null;
|
|
|
|
//look for explicit individual access, inventory, or build permission
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Access)) return null;
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Inventory)) return null;
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Build)) return null;
|
|
|
|
//also check for public permission
|
|
ClaimPermission permissionLevel = this.playerIDToClaimPermissionMap.get("public");
|
|
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel || ClaimPermission.Access == permissionLevel) return null;
|
|
|
|
//permission inheritance for subdivisions
|
|
if(this.parent != null)
|
|
return this.parent.allowAccess(player);
|
|
|
|
//catch-all error message for all other cases
|
|
String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoAccessPermission, this.getOwnerName());
|
|
if(player.hasPermission("griefprevention.ignoreclaims"))
|
|
reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement);
|
|
return reason;
|
|
}
|
|
|
|
//inventory permission check
|
|
public String allowContainers(Player player)
|
|
{
|
|
//if we don't know who's asking, always say no (i've been told some mods can make this happen somehow)
|
|
if(player == null) return "";
|
|
|
|
//trying to access inventory in a claim may extend an existing siege to include this claim
|
|
GriefPrevention.instance.dataStore.tryExtendSiege(player, this);
|
|
|
|
//if under siege, nobody accesses containers
|
|
if(this.siegeData != null)
|
|
{
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersSiege, siegeData.attacker.getName());
|
|
}
|
|
|
|
//owner and administrators in ignoreclaims mode have access
|
|
if(player.getUniqueId().equals(this.ownerID) || GriefPrevention.instance.dataStore.getPlayerData(player.getUniqueId()).ignoreClaims) return null;
|
|
|
|
//admin claims need adminclaims permission only.
|
|
if(this.isAdminClaim())
|
|
{
|
|
if(player.hasPermission("griefprevention.adminclaims")) return null;
|
|
}
|
|
|
|
//check for explicit individual container or build permission
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Inventory)) return null;
|
|
if(this.hasExplicitPermission(player, ClaimPermission.Build)) return null;
|
|
|
|
//check for public container or build permission
|
|
ClaimPermission permissionLevel = this.playerIDToClaimPermissionMap.get("public");
|
|
if(ClaimPermission.Build == permissionLevel || ClaimPermission.Inventory == permissionLevel) return null;
|
|
|
|
//permission inheritance for subdivisions
|
|
if(this.parent != null)
|
|
return this.parent.allowContainers(player);
|
|
|
|
//error message for all other cases
|
|
String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoContainersPermission, this.getOwnerName());
|
|
if(player.hasPermission("griefprevention.ignoreclaims"))
|
|
reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement);
|
|
return reason;
|
|
}
|
|
|
|
//grant permission check, relatively simple
|
|
public String allowGrantPermission(Player player)
|
|
{
|
|
//if we don't know who's asking, always say no (i've been told some mods can make this happen somehow)
|
|
if(player == null) return "";
|
|
|
|
//anyone who can modify the claim can do this
|
|
if(this.allowEdit(player) == null) return null;
|
|
|
|
//anyone who's in the managers (/PermissionTrust) list can do this
|
|
for(int i = 0; i < this.managers.size(); i++)
|
|
{
|
|
String managerID = this.managers.get(i);
|
|
if(player.getUniqueId().toString().equals(managerID)) return null;
|
|
|
|
else if(managerID.startsWith("[") && managerID.endsWith("]"))
|
|
{
|
|
managerID = managerID.substring(1, managerID.length() - 1);
|
|
if(managerID == null || managerID.isEmpty()) continue;
|
|
if(player.hasPermission(managerID)) return null;
|
|
}
|
|
}
|
|
|
|
//permission inheritance for subdivisions
|
|
if(this.parent != null)
|
|
return this.parent.allowGrantPermission(player);
|
|
|
|
//generic error message
|
|
String reason = GriefPrevention.instance.dataStore.getMessage(Messages.NoPermissionTrust, this.getOwnerName());
|
|
if(player.hasPermission("griefprevention.ignoreclaims"))
|
|
reason += " " + GriefPrevention.instance.dataStore.getMessage(Messages.IgnoreClaimsAdvertisement);
|
|
return reason;
|
|
}
|
|
|
|
//grants a permission for a player or the public
|
|
public void setPermission(String playerID, ClaimPermission permissionLevel)
|
|
{
|
|
this.playerIDToClaimPermissionMap.put(playerID.toLowerCase(), permissionLevel);
|
|
}
|
|
|
|
//revokes a permission for a player or the public
|
|
public void dropPermission(String playerID)
|
|
{
|
|
this.playerIDToClaimPermissionMap.remove(playerID.toLowerCase());
|
|
|
|
for(Claim child : this.children)
|
|
{
|
|
child.dropPermission(playerID);
|
|
}
|
|
}
|
|
|
|
//clears all permissions (except owner of course)
|
|
public void clearPermissions()
|
|
{
|
|
this.playerIDToClaimPermissionMap.clear();
|
|
this.managers.clear();
|
|
|
|
for(Claim child : this.children)
|
|
{
|
|
child.clearPermissions();
|
|
}
|
|
}
|
|
|
|
//gets ALL permissions
|
|
//useful for making copies of permissions during a claim resize and listing all permissions in a claim
|
|
public void getPermissions(ArrayList<String> builders, ArrayList<String> containers, ArrayList<String> accessors, ArrayList<String> managers)
|
|
{
|
|
//loop through all the entries in the hash map
|
|
Iterator<Map.Entry<String, ClaimPermission>> mappingsIterator = this.playerIDToClaimPermissionMap.entrySet().iterator();
|
|
while(mappingsIterator.hasNext())
|
|
{
|
|
Map.Entry<String, ClaimPermission> entry = mappingsIterator.next();
|
|
|
|
//build up a list for each permission level
|
|
if(entry.getValue() == ClaimPermission.Build)
|
|
{
|
|
builders.add(entry.getKey());
|
|
}
|
|
else if(entry.getValue() == ClaimPermission.Inventory)
|
|
{
|
|
containers.add(entry.getKey());
|
|
}
|
|
else
|
|
{
|
|
accessors.add(entry.getKey());
|
|
}
|
|
}
|
|
|
|
//managers are handled a little differently
|
|
for(int i = 0; i < this.managers.size(); i++)
|
|
{
|
|
managers.add(this.managers.get(i));
|
|
}
|
|
}
|
|
|
|
//returns a copy of the location representing lower x, y, z limits
|
|
public Location getLesserBoundaryCorner()
|
|
{
|
|
return this.lesserBoundaryCorner.clone();
|
|
}
|
|
|
|
//returns a copy of the location representing upper x, y, z limits
|
|
//NOTE: remember upper Y will always be ignored, all claims always extend to the sky
|
|
public Location getGreaterBoundaryCorner()
|
|
{
|
|
return this.greaterBoundaryCorner.clone();
|
|
}
|
|
|
|
//returns a friendly owner name (for admin claims, returns "an administrator" as the owner)
|
|
public String getOwnerName()
|
|
{
|
|
if(this.parent != null)
|
|
return this.parent.getOwnerName();
|
|
|
|
if(this.ownerID == null)
|
|
return GriefPrevention.instance.dataStore.getMessage(Messages.OwnerNameForAdminClaims);
|
|
|
|
return GriefPrevention.lookupPlayerName(this.ownerID);
|
|
}
|
|
|
|
//whether or not a location is in a claim
|
|
//ignoreHeight = true means location UNDER the claim will return TRUE
|
|
//excludeSubdivisions = true means that locations inside subdivisions of the claim will return FALSE
|
|
public boolean contains(Location location, boolean ignoreHeight, boolean excludeSubdivisions)
|
|
{
|
|
//not in the same world implies false
|
|
if(!location.getWorld().equals(this.lesserBoundaryCorner.getWorld())) return false;
|
|
|
|
double x = location.getX();
|
|
double y = location.getY();
|
|
double z = location.getZ();
|
|
|
|
//main check
|
|
boolean inClaim = (ignoreHeight || y >= this.lesserBoundaryCorner.getY()) &&
|
|
x >= this.lesserBoundaryCorner.getX() &&
|
|
x < this.greaterBoundaryCorner.getX() + 1 &&
|
|
z >= this.lesserBoundaryCorner.getZ() &&
|
|
z < this.greaterBoundaryCorner.getZ() + 1;
|
|
|
|
if(!inClaim) return false;
|
|
|
|
//additional check for subdivisions
|
|
//you're only in a subdivision when you're also in its parent claim
|
|
//NOTE: if a player creates subdivions then resizes the parent claim, it's possible that
|
|
//a subdivision can reach outside of its parent's boundaries. so this check is important!
|
|
if(this.parent != null)
|
|
{
|
|
return this.parent.contains(location, ignoreHeight, false);
|
|
}
|
|
|
|
//code to exclude subdivisions in this check
|
|
else if(excludeSubdivisions)
|
|
{
|
|
//search all subdivisions to see if the location is in any of them
|
|
for(int i = 0; i < this.children.size(); i++)
|
|
{
|
|
//if we find such a subdivision, return false
|
|
if(this.children.get(i).contains(location, ignoreHeight, true))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//otherwise yes
|
|
return true;
|
|
}
|
|
|
|
//whether or not two claims overlap
|
|
//used internally to prevent overlaps when creating claims
|
|
boolean overlaps(Claim otherClaim)
|
|
{
|
|
//NOTE: if trying to understand this makes your head hurt, don't feel bad - it hurts mine too.
|
|
//try drawing pictures to visualize test cases.
|
|
|
|
if(!this.lesserBoundaryCorner.getWorld().equals(otherClaim.getLesserBoundaryCorner().getWorld())) return false;
|
|
|
|
//first, check the corners of this claim aren't inside any existing claims
|
|
if(otherClaim.contains(this.lesserBoundaryCorner, true, false)) return true;
|
|
if(otherClaim.contains(this.greaterBoundaryCorner, true, false)) return true;
|
|
if(otherClaim.contains(new Location(this.lesserBoundaryCorner.getWorld(), this.lesserBoundaryCorner.getBlockX(), 0, this.greaterBoundaryCorner.getBlockZ()), true, false)) return true;
|
|
if(otherClaim.contains(new Location(this.lesserBoundaryCorner.getWorld(), this.greaterBoundaryCorner.getBlockX(), 0, this.lesserBoundaryCorner.getBlockZ()), true, false)) return true;
|
|
|
|
//verify that no claim's lesser boundary point is inside this new claim, to cover the "existing claim is entirely inside new claim" case
|
|
if(this.contains(otherClaim.getLesserBoundaryCorner(), true, false)) return true;
|
|
|
|
//verify this claim doesn't band across an existing claim, either horizontally or vertically
|
|
if( this.getLesserBoundaryCorner().getBlockZ() <= otherClaim.getGreaterBoundaryCorner().getBlockZ() &&
|
|
this.getLesserBoundaryCorner().getBlockZ() >= otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
|
this.getLesserBoundaryCorner().getBlockX() < otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
|
this.getGreaterBoundaryCorner().getBlockX() > otherClaim.getGreaterBoundaryCorner().getBlockX() )
|
|
return true;
|
|
|
|
if( this.getGreaterBoundaryCorner().getBlockZ() <= otherClaim.getGreaterBoundaryCorner().getBlockZ() &&
|
|
this.getGreaterBoundaryCorner().getBlockZ() >= otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
|
this.getLesserBoundaryCorner().getBlockX() < otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
|
this.getGreaterBoundaryCorner().getBlockX() > otherClaim.getGreaterBoundaryCorner().getBlockX() )
|
|
return true;
|
|
|
|
if( this.getLesserBoundaryCorner().getBlockX() <= otherClaim.getGreaterBoundaryCorner().getBlockX() &&
|
|
this.getLesserBoundaryCorner().getBlockX() >= otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
|
this.getLesserBoundaryCorner().getBlockZ() < otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
|
this.getGreaterBoundaryCorner().getBlockZ() > otherClaim.getGreaterBoundaryCorner().getBlockZ() )
|
|
return true;
|
|
|
|
if( this.getGreaterBoundaryCorner().getBlockX() <= otherClaim.getGreaterBoundaryCorner().getBlockX() &&
|
|
this.getGreaterBoundaryCorner().getBlockX() >= otherClaim.getLesserBoundaryCorner().getBlockX() &&
|
|
this.getLesserBoundaryCorner().getBlockZ() < otherClaim.getLesserBoundaryCorner().getBlockZ() &&
|
|
this.getGreaterBoundaryCorner().getBlockZ() > otherClaim.getGreaterBoundaryCorner().getBlockZ() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//whether more entities may be added to a claim
|
|
public String allowMoreEntities(boolean remove)
|
|
{
|
|
if(this.parent != null) return this.parent.allowMoreEntities(remove);
|
|
|
|
//this rule only applies to creative mode worlds
|
|
if(!GriefPrevention.instance.creativeRulesApply(this.getLesserBoundaryCorner())) return null;
|
|
|
|
//admin claims aren't restricted
|
|
if(this.isAdminClaim()) return null;
|
|
|
|
//don't apply this rule to very large claims
|
|
if(this.getArea() > 10000) return null;
|
|
|
|
//determine maximum allowable entity count, based on claim size
|
|
int maxEntities = this.getArea() / 50;
|
|
if(maxEntities == 0) return GriefPrevention.instance.dataStore.getMessage(Messages.ClaimTooSmallForEntities);
|
|
|
|
//count current entities (ignoring players)
|
|
int totalEntities = 0;
|
|
ArrayList<Chunk> chunks = this.getChunks();
|
|
for(Chunk chunk : chunks)
|
|
{
|
|
Entity [] entities = chunk.getEntities();
|
|
for(int i = 0; i < entities.length; i++)
|
|
{
|
|
Entity entity = entities[i];
|
|
if(!(entity instanceof Player) && this.contains(entity.getLocation(), false, false))
|
|
{
|
|
totalEntities++;
|
|
if(remove && totalEntities > maxEntities) entity.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(totalEntities >= maxEntities) return GriefPrevention.instance.dataStore.getMessage(Messages.TooManyEntitiesInClaim);
|
|
|
|
return null;
|
|
}
|
|
|
|
//implements a strict ordering of claims, used to keep the claims collection sorted for faster searching
|
|
boolean greaterThan(Claim otherClaim)
|
|
{
|
|
Location thisCorner = this.getLesserBoundaryCorner();
|
|
Location otherCorner = otherClaim.getLesserBoundaryCorner();
|
|
|
|
if(thisCorner.getBlockX() > otherCorner.getBlockX()) return true;
|
|
|
|
if(thisCorner.getBlockX() < otherCorner.getBlockX()) return false;
|
|
|
|
if(thisCorner.getBlockZ() > otherCorner.getBlockZ()) return true;
|
|
|
|
if(thisCorner.getBlockZ() < otherCorner.getBlockZ()) return false;
|
|
|
|
return thisCorner.getWorld().getName().compareTo(otherCorner.getWorld().getName()) < 0;
|
|
}
|
|
|
|
long getPlayerInvestmentScore()
|
|
{
|
|
//decide which blocks will be considered player placed
|
|
Location lesserBoundaryCorner = this.getLesserBoundaryCorner();
|
|
ArrayList<Integer> playerBlocks = RestoreNatureProcessingTask.getPlayerBlocks(lesserBoundaryCorner.getWorld().getEnvironment(), lesserBoundaryCorner.getBlock().getBiome());
|
|
|
|
//scan the claim for player placed blocks
|
|
double score = 0;
|
|
|
|
boolean creativeMode = GriefPrevention.instance.creativeRulesApply(lesserBoundaryCorner);
|
|
|
|
for(int x = this.lesserBoundaryCorner.getBlockX(); x <= this.greaterBoundaryCorner.getBlockX(); x++)
|
|
{
|
|
for(int z = this.lesserBoundaryCorner.getBlockZ(); z <= this.greaterBoundaryCorner.getBlockZ(); z++)
|
|
{
|
|
int y = this.lesserBoundaryCorner.getBlockY();
|
|
for(; y < GriefPrevention.instance.getSeaLevel(this.lesserBoundaryCorner.getWorld()) - 5; y++)
|
|
{
|
|
Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z);
|
|
if(playerBlocks.contains(block.getTypeId()))
|
|
{
|
|
if(block.getType() == Material.CHEST && !creativeMode)
|
|
{
|
|
score += 10;
|
|
}
|
|
else
|
|
{
|
|
score += .5;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(; y < this.lesserBoundaryCorner.getWorld().getMaxHeight(); y++)
|
|
{
|
|
Block block = this.lesserBoundaryCorner.getWorld().getBlockAt(x, y, z);
|
|
if(playerBlocks.contains(block.getTypeId()))
|
|
{
|
|
if(block.getType() == Material.CHEST && !creativeMode)
|
|
{
|
|
score += 10;
|
|
}
|
|
else if(creativeMode && (block.getType() == Material.LAVA || block.getType() == Material.STATIONARY_LAVA))
|
|
{
|
|
score -= 10;
|
|
}
|
|
else
|
|
{
|
|
score += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (long)score;
|
|
}
|
|
|
|
public ArrayList<Chunk> getChunks()
|
|
{
|
|
ArrayList<Chunk> chunks = new ArrayList<Chunk>();
|
|
|
|
World world = this.getLesserBoundaryCorner().getWorld();
|
|
Chunk lesserChunk = this.getLesserBoundaryCorner().getChunk();
|
|
Chunk greaterChunk = this.getGreaterBoundaryCorner().getChunk();
|
|
|
|
for(int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++)
|
|
{
|
|
for(int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++)
|
|
{
|
|
chunks.add(world.getChunkAt(x, z));
|
|
}
|
|
}
|
|
|
|
return chunks;
|
|
}
|
|
|
|
public ArrayList<String> getChunkStrings()
|
|
{
|
|
ArrayList<String> chunkStrings = new ArrayList<String>();
|
|
int smallX = this.getLesserBoundaryCorner().getBlockX() >> 4;
|
|
int smallZ = this.getLesserBoundaryCorner().getBlockZ() >> 4;
|
|
int largeX = this.getGreaterBoundaryCorner().getBlockX() >> 4;
|
|
int largeZ = this.getGreaterBoundaryCorner().getBlockZ() >> 4;
|
|
|
|
for(int x = smallX; x <= largeX; x++)
|
|
{
|
|
for(int z = smallZ; z <= largeZ; z++)
|
|
{
|
|
chunkStrings.add(String.valueOf(x) + z);
|
|
}
|
|
}
|
|
|
|
return chunkStrings;
|
|
}
|
|
}
|