/*
* Copyright 2011 Tyler Blair. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and contributors and should not be interpreted as representing official policies,
* either expressed or implied, of anybody else.
*/
package com.griefcraft.modules.limits;
import com.griefcraft.lwc.LWC;
import com.griefcraft.scripting.JavaModule;
import com.griefcraft.scripting.event.LWCCommandEvent;
import com.griefcraft.scripting.event.LWCProtectionRegisterEvent;
import com.griefcraft.scripting.event.LWCReloadEvent;
import com.griefcraft.util.Colors;
import com.griefcraft.util.config.Configuration;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachmentInfo;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class LimitsV2 extends JavaModule {
/**
* The limit represented by unlimited
*/
public final static int UNLIMITED = Integer.MAX_VALUE;
/**
* If the limits module is enabled
*/
private boolean enabled = true;
/**
* The limits configuration
*/
private final Configuration configuration = Configuration.load("limitsv2.yml");
/**
* A map of the default limits
*/
private final List<Limit> defaultLimits = new LinkedList<Limit>();
/**
* A map of all of the player limits
*/
private final Map<String, List<Limit>> playerLimits = new HashMap<String, List<Limit>>();
/**
* A map of all of the group limits - downcasted to lowercase to simplify comparisons
*/
private final Map<String, List<Limit>> groupLimits = new HashMap<String, List<Limit>>();
/**
* A map mapping string representations of materials to their Material counterpart
*/
private final Map<String, Material> materialCache = new HashMap<String, Material>();
{
for (Material material : Material.values()) {
String materialName = LWC.normalizeMaterialName(material);
// add the name & the block id
materialCache.put(materialName, material);
materialCache.put(material.getId() + "", material);
materialCache.put(materialName, material);
if (!materialName.equals(material.toString().toLowerCase())) {
materialCache.put(material.toString().toLowerCase(), material);
}
}
}
public abstract class Limit {
/**
* The limit
*/
private final int limit;
public Limit(int limit) {
this.limit = limit;
}
/**
* Get the player's protection count that should be used with this limit
*
* @param player
* @param material
* @return
*/
public abstract int getProtectionCount(Player player, Material material);
/**
* @return
*/
public int getLimit() {
return limit;
}
}
public final class DefaultLimit extends Limit {
public DefaultLimit(int limit) {
super(limit);
}
@Override
public int getProtectionCount(Player player, Material material) {
return LWC.getInstance().getPhysicalDatabase().getProtectionCount(player.getName());
}
}
public final class BlockLimit extends Limit {
/**
* The block material to limit
*/
private final Material material;
public BlockLimit(Material material, int limit) {
super(limit);
this.material = material;
}
@Override
public int getProtectionCount(Player player, Material material) {
return LWC.getInstance().getPhysicalDatabase().getProtectionCount(player.getName(), material.getId());
}
/**
* @return
*/
public Material getMaterial() {
return material;
}
}
public final class SignLimit extends Limit {
public SignLimit(int limit) {
super(limit);
}
@Override
public int getProtectionCount(Player player, Material material) {
LWC lwc = LWC.getInstance();
return lwc.getPhysicalDatabase().getProtectionCount(player.getName(), Material.SIGN_POST.getId())
+ lwc.getPhysicalDatabase().getProtectionCount(player.getName(), Material.WALL_SIGN.getId());
}
}
public LimitsV2() {
enabled = LWC.getInstance().getConfiguration().getBoolean("optional.useProtectionLimits", true);
if (enabled) {
loadLimits();
}
}
@Override
public void onReload(LWCReloadEvent event) {
if (enabled) {
reload();
}
}
@Override
public void onRegisterProtection(LWCProtectionRegisterEvent event) {
if (!enabled || event.isCancelled()) {
return;
}
LWC lwc = event.getLWC();
Player player = event.getPlayer();
Block block = event.getBlock();
if (hasReachedLimit(player, block.getType())) {
lwc.sendLocale(player, "protection.exceeded");
event.setCancelled(true);
}
}
@Override
public void onCommand(LWCCommandEvent event) {
if (!enabled || event.isCancelled()) {
return;
}
if (!event.hasFlag("limits")) {
return;
}
LWC lwc = event.getLWC();
CommandSender sender = event.getSender();
String[] args = event.getArgs();
event.setCancelled(true);
String playerName;
if (args.length == 0) {
if (args.length == 0 && !(sender instanceof Player)) {
sender.sendMessage(Colors.Red + "You are not a player!");
return;
}
playerName = sender.getName();
} else {
if (lwc.isAdmin(sender)) {
playerName = args[0];
} else {
lwc.sendLocale(sender, "protection.accessdenied");
return;
}
}
Player player = lwc.getPlugin().getServer().getPlayer(playerName);
if (player == null) {
sender.sendMessage(Colors.Red + "That player is not online!");
return;
}
// send their limits to them
sendLimits(sender, player, getPlayerLimits(player));
}
/**
* Gets the raw limits configuration
*
* @return
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* Reload the limits
*/
public void reload() {
loadLimits();
}
/**
* Sends the list of limits to the player
*
* @param sender the commandsender to send the limits to
* @param target the player limits are being shown for, can be null
* @param limits
*/
public void sendLimits(CommandSender sender, Player target, List<Limit> limits) {
LWC lwc = LWC.getInstance();
for (Limit limit : limits) {
if (limit == null) {
continue;
}
String stringLimit = limit.getLimit() == UNLIMITED ? "Unlimited" : Integer.toString(limit.getLimit());
String colour = Colors.Yellow;
if (target != null) {
Material material = null;
if (limit instanceof BlockLimit) {
material = ((BlockLimit) limit).getMaterial();
} else if (limit instanceof SignLimit) {
material = Material.SIGN_POST;
}
boolean reachedLimit = hasReachedLimit(target, material);
colour = reachedLimit ? Colors.Red : Colors.Green;
}
if (limit instanceof DefaultLimit) {
String currentProtected = target != null ? (Integer.toString(limit.getProtectionCount(target, null)) + "/") : "";
sender.sendMessage("Default: " + colour + currentProtected + stringLimit);
} else if (limit instanceof BlockLimit) {
BlockLimit blockLimit = (BlockLimit) limit;
String currentProtected = target != null ? (Integer.toString(limit.getProtectionCount(target, blockLimit.getMaterial())) + "/") : "";
sender.sendMessage(lwc.materialToString(blockLimit.getMaterial()) + ": " + colour + currentProtected + stringLimit);
} else if (limit instanceof SignLimit) {
String currentProtected = target != null ? (Integer.toString(limit.getProtectionCount(target, null)) + "/") : "";
sender.sendMessage("Sign: " + colour + currentProtected + stringLimit);
} else {
sender.sendMessage(limit.getClass().getSimpleName() + ": " + Colors.Yellow + stringLimit);
}
}
}
/**
* Checks if a player has reached their protection limit
*
* @param player
* @param material the material type the player has interacted with
* @return
*/
public boolean hasReachedLimit(Player player, Material material) {
Limit limit = getEffectiveLimit(player, material);
// if they don't have a limit it's not possible to reach it ^^
// ... but if it's null, what the hell did the server owner do?
if (limit == null) {
return false;
}
// Get the effective limit placed upon them
int neverPassThisNumber = limit.getLimit();
// get the amount of protections the player has
int protections = limit.getProtectionCount(player, material);
return protections >= neverPassThisNumber;
}
/**
* Find limits that are attached to the player via permissions (e.g lwc.protect.*.10 = 10 of any protection)
*
* @param player
* @return
*/
private List<Limit> findLimitsViaPermissions(Player player) {
List<Limit> limits = new LinkedList<Limit>();
for (PermissionAttachmentInfo pai : player.getEffectivePermissions()) {
String permission = pai.getPermission();
boolean value = pai.getValue();
if (!value || !permission.startsWith("lwc.protect.")) {
continue;
}
String[] split = permission.substring("lwc.protect.".length()).split(".");
if (split.length != 2) {
continue;
}
String matchName = split[0];
String strCount = split[1];
int count;
try {
count = Integer.parseInt(strCount);
} catch (NumberFormatException e) {
continue;
}
if (matchName.equals("*")) {
limits.add(new DefaultLimit(count));
} else if (matchName.equals("sign")) {
limits.add(new SignLimit(count));
} else {
Material material = materialCache.get(matchName);
if (material == null) {
continue;
}
limits.add(new BlockLimit(material, count));
}
}
return limits;
}
/**
* Gets the list of limits that may apply to the player.
* For group limits, it uses the highest one found.
*
* @param player
* @return
*/
public List<Limit> getPlayerLimits(Player player) {
LWC lwc = LWC.getInstance();
List<Limit> limits = new LinkedList<Limit>();
// get all of their own limits
String playerName = player.getName().toLowerCase();
if (playerLimits.containsKey(playerName)) {
limits.addAll(playerLimits.get(playerName));
}
for (Limit limit : findLimitsViaPermissions(player)) {
Limit matched = findLimit(limits, limit);
if (matched != null) {
// Is our limit better?
if (limit.getLimit() > matched.getLimit()) {
limits.remove(matched);
limits.add(limit);
}
} else {
limits.add(limit);
}
}
// Look over the group limits
for (String group : lwc.getPermissions().getGroups(player)) {
if (groupLimits.containsKey(group.toLowerCase())) {
for (Limit limit : groupLimits.get(group.toLowerCase())) {
// try to match one already inside what we found
Limit matched = findLimit(limits, limit);
if (matched != null) {
// Is our limit better?
if (limit.getLimit() > matched.getLimit()) {
limits.remove(matched);
limits.add(limit);
}
} else {
limits.add(limit);
}
}
}
}
// Look at the default limits
for (Limit limit : defaultLimits) {
// try to match one already inside what we found
Limit matched = findLimit(limits, limit);
if (matched == null) {
limits.add(limit);
}
}
return limits;
}
/**
* Get the player's effective limit that should take precedence
*
* @param player
* @param material
* @return
*/
public Limit getEffectiveLimit(Player player, Material material) {
return getEffectiveLimit(getPlayerLimits(player), material);
}
/**
* Gets an immutable list of the default limits
*
* @return
*/
public List<Limit> getDefaultLimits() {
return Collections.unmodifiableList(defaultLimits);
}
/**
* Gets an unmodiable map of the player limits
*
* @return
*/
public Map<String, List<Limit>> getPlayerLimits() {
return Collections.unmodifiableMap(playerLimits);
}
/**
* Gets an unmodiable map of the group limits
*
* @return
*/
public Map<String, List<Limit>> getGroupLimits() {
return Collections.unmodifiableMap(groupLimits);
}
/**
* Orders the limits, putting the default limit at the top of the list
*
* @param limits
* @return
*/
private List<Limit> orderLimits(List<Limit> limits) {
Limit defaultLimit = null;
// Locate the default limit
for (Limit limit : limits) {
if (limit instanceof DefaultLimit) {
defaultLimit = limit;
break;
}
}
// remove it
limits.remove(defaultLimit);
// readd it at the head
limits.add(0, defaultLimit);
return limits;
}
/**
* Gets the material's effective limit that should take precedence
*
* @param limits
* @param material
* @return Limit object if one is found otherwise NULL
*/
private Limit getEffectiveLimit(List<Limit> limits, Material material) {
if (limits == null) {
return null;
}
// Temporary storage to use if the default is found so we save time if no override was found
Limit defaultLimit = null;
for (Limit limit : limits) {
// Record the default limit if found
if (limit instanceof DefaultLimit) {
defaultLimit = limit;
} else if (limit instanceof SignLimit) {
if (material == Material.WALL_SIGN || material == Material.SIGN_POST) {
return limit;
}
} else if (limit instanceof BlockLimit) {
BlockLimit blockLimit = (BlockLimit) limit;
// If it's the appropriate material we can return immediately
if (blockLimit.getMaterial() == material) {
return blockLimit;
}
}
}
return defaultLimit;
}
/**
* Load all of the limits
*/
private void loadLimits() {
// make sure we're working on a clean slate
defaultLimits.clear();
playerLimits.clear();
groupLimits.clear();
// add the default limits
defaultLimits.addAll(findLimits("defaults"));
// add all of the player limits
try {
for (String player : configuration.getKeys("players")) {
playerLimits.put(player.toLowerCase(), findLimits("players." + player));
}
} catch (NullPointerException e) { }
// add all of the group limits
try {
for (String group : configuration.getKeys("groups")) {
groupLimits.put(group.toLowerCase(), findLimits("groups." + group));
}
} catch (NullPointerException e) { }
}
/**
* Find and match all of the limits in a given list of nodes for the config
*
* @param node
* @return
*/
private List<Limit> findLimits(String node) {
List<Limit> limits = new LinkedList<Limit>();
List<String> keys = configuration.getKeys(node);
for (String key : keys) {
String value = configuration.getString(node + "." + key);
int limit;
if (value.equalsIgnoreCase("unlimited")) {
limit = UNLIMITED;
} else {
limit = Integer.parseInt(value);
}
// Match default
if (key.equalsIgnoreCase("default")) {
limits.add(new DefaultLimit(limit));
} else if (key.equalsIgnoreCase("sign")) {
limits.add(new SignLimit(limit));
} else {
// attempt to resolve it as a block id
int blockId = -1;
try {
blockId = Integer.parseInt(key);
} catch (NumberFormatException e) { }
// resolve the material
Material material;
if (blockId >= 0) {
material = Material.getMaterial(blockId);
} else {
material = Material.getMaterial(key.toUpperCase());
}
if (material != null) {
limits.add(new BlockLimit(material, limit));
}
}
}
// Order it
orderLimits(limits);
return limits;
}
/**
* Find a limit in the list of limits that equals the given limit in class;
* The LIMIT itself does not need to be equal; only the class & if it's a
* BlockLimit, the material must equal
*
* @param limits
* @param compare
* @return
*/
private Limit findLimit(List<Limit> limits, Limit compare) {
for (Limit limit : limits) {
if (limit != null && limit.getClass().isInstance(compare)) {
if (limit instanceof BlockLimit) {
BlockLimit cmp1 = (BlockLimit) limit;
BlockLimit cmp2 = (BlockLimit) compare;
if (cmp1.getMaterial() == cmp2.getMaterial()) {
return limit;
}
} else {
return limit;
}
}
}
return null;
}
}