/*
* 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.LWCProtectionRegisterEvent;
import com.griefcraft.util.config.Configuration;
import org.apache.commons.lang.StringUtils;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachmentInfo;
public class LimitsModule extends JavaModule {
/**
* The permission node for type: default protections
*/
public static final String PERMISSION_NODE_GLOBAL = "lwc.limit.";
/**
* NODE.BLOCK.limit
*/
public static final String PERMISSION_NODE_BLOCK = "lwc.limit.block.";
/**
* Limits type
*/
public enum Type {
/**
* Undefined type
*/
NULL,
/**
* Combines all blocks together into one
* This type ignores chest:, furnace:, etc
*/
DEFAULT,
/**
* Limits are defined per-block
*/
CUSTOM;
public static Type resolve(String name) {
for (Type type : values()) {
if (type.toString().equalsIgnoreCase(name)) {
return type;
}
}
return NULL;
}
}
private Configuration configuration = Configuration.load("limits.yml", false);
/**
* Integer value that represents unlimited protections
*/
private int UNLIMITED = Integer.MAX_VALUE;
/**
* If protection limits are enabled
*/
private boolean enabled = true;
public LimitsModule() {
enabled = LWC.getInstance().getConfiguration().getBoolean("optional.useProtectionLimits", true);
}
/**
* Set a config value in the configuration
*
* @param path
* @param value
*/
public void set(String path, Object value) {
configuration.setProperty(path, value);
}
/**
* Save the configuration
*/
public boolean save() {
return configuration.save();
}
/**
* Check if a player has reached their protection limit for a specific block type
*
* @param player
* @param block
* @return true if the player reached their limit
*/
public boolean hasReachedLimit(Player player, Block block) {
if (configuration == null) {
return false;
}
LWC lwc = LWC.getInstance();
int limit = mapProtectionLimit(player, block.getTypeId());
// if they're limit is unlimited, how could they get above it? :)
if (limit == UNLIMITED) {
return false;
}
Type type = Type.resolve(resolveString(player, "type"));
int protections; // 0 = *
switch (type) {
case CUSTOM:
protections = lwc.getPhysicalDatabase().getProtectionCount(player.getName(), block.getTypeId());
break;
case DEFAULT:
protections = lwc.getPhysicalDatabase().getProtectionCount(player.getName());
break;
default:
throw new UnsupportedOperationException("Limit type " + type.toString() + " is undefined in LimitsModule::hasReachedLimit");
}
return protections >= limit;
}
/**
* Search the player's permissions for a permission and return it
* Depending on this is used, this can become O(scary)
*
* @param player
* @param prefix
* @return
*/
public PermissionAttachmentInfo searchPermissions(Player player, String prefix) {
for (PermissionAttachmentInfo attachment : player.getEffectivePermissions()) {
String permission = attachment.getPermission();
// check for the perm node
if (attachment.getValue() && permission.startsWith(prefix)) {
// Bingo!
return attachment;
}
}
return null;
}
/**
* Search permissions for an integer and if found, return it
*
* @param player
* @param prefix
* @return
*/
public int searchPermissionsForInteger(Player player, String prefix) {
PermissionAttachmentInfo attachment = searchPermissions(player, prefix);
// Not found
if (attachment == null) {
return -1;
}
// Found
return Integer.parseInt(attachment.getPermission().substring(prefix.length()));
}
/**
* Get the protection limits for a player
*
* @param player
* @param blockId
* @return
*/
public int mapProtectionLimit(Player player, int blockId) {
if (configuration == null) {
return 0;
}
int limit = -1;
Type type = Type.resolve(resolveString(player, "type"));
// Try permissions
int globalLimit = searchPermissionsForInteger(player, PERMISSION_NODE_GLOBAL);
// Was it found?
if (globalLimit >= 0) {
return globalLimit;
}
// Try the block id now
int blockLimit = searchPermissionsForInteger(player, PERMISSION_NODE_BLOCK + blockId + ".");
if (blockLimit != -1) {
return blockLimit;
}
switch (type) {
case DEFAULT:
limit = resolveInteger(player, "limit");
break;
case CUSTOM:
// first try the block id
limit = resolveInteger(player, blockId + "");
// and now try the name
if (limit == -1 && blockId > 0) {
String name = StringUtils.replace(Material.getMaterial(blockId).toString().toLowerCase(), "block", "");
if (name.endsWith("_")) {
name = name.substring(0, name.length() - 1);
}
limit = resolveInteger(player, name);
}
// if it's STILL null, fall back
if (limit == -1) {
limit = resolveInteger(player, "limit");
}
break;
}
return limit == -1 ? UNLIMITED : limit;
}
/**
* Resolve a configuration node for a player. Tries nodes in this order:
* <pre>
* players.PLAYERNAME.node
* groups.GROUPNAME.node
* master.node
* </pre>
*
* @param player
* @param node
* @return
*/
private String resolveString(Player player, String node) {
LWC lwc = LWC.getInstance();
// resolve the limits type
String value;
// try the player
value = configuration.getString("players." + player.getName() + "." + node);
// try the player's groups
if (value == null) {
for (String groupName : lwc.getPermissions().getGroups(player)) {
if (groupName != null && !groupName.isEmpty() && value == null) {
value = map("groups." + groupName + "." + node);
}
}
}
// if all else fails, use master
if (value == null) {
value = map("master." + node);
}
return value != null && !value.isEmpty() ? value : null;
}
/**
* Resolve an integer for a player
*
* @param player
* @param node
* @return
*/
private int resolveInteger(Player player, String node) {
LWC lwc = LWC.getInstance();
// resolve the limits type
int value = -1;
// try the player
String temp = configuration.getString("players." + player.getName() + "." + node);
if (temp != null && !temp.isEmpty()) {
value = parseInt(temp);
}
// try the player's groups
if (value == -1) {
for (String groupName : lwc.getPermissions().getGroups(player)) {
if (groupName != null && !groupName.isEmpty()) {
temp = map("groups." + groupName + "." + node);
if (temp != null && !temp.isEmpty()) {
int resolved = parseInt(temp);
// Is it higher than what we already have?
if (resolved > value) {
value = resolved;
}
}
}
}
}
// if all else fails, use master
if (value == -1) {
temp = map("master." + node);
if (temp != null && !temp.isEmpty()) {
value = parseInt(temp);
}
}
// Default to 0, not -1 if it is still -1
return value;
}
/**
* Parse an int
*
* @param input
* @return
*/
private int parseInt(String input) {
if (input.equalsIgnoreCase("unlimited")) {
return UNLIMITED;
}
return Integer.parseInt(input);
}
/**
* Get the value from either the path or the master value if it's null
*
* @param path
* @return
*/
private String map(String path) {
String value = configuration.getString(path);
if (value == null) {
int lastIndex = path.lastIndexOf(".");
String node = path.substring(lastIndex + 1);
value = configuration.getString("master." + node);
}
if (value == null) {
value = "";
}
return value;
}
@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)) {
lwc.sendLocale(player, "protection.exceeded");
event.setCancelled(true);
}
}
}