package com.forgeessentials.economy.plots;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.permission.PermissionLevel;
import com.forgeessentials.api.APIRegistry;
import com.forgeessentials.api.UserIdent;
import com.forgeessentials.api.permissions.AreaZone;
import com.forgeessentials.api.permissions.FEPermissions;
import com.forgeessentials.api.permissions.IPermissionsHelper;
import com.forgeessentials.api.permissions.RootZone;
import com.forgeessentials.api.permissions.ServerZone;
import com.forgeessentials.api.permissions.WorldZone;
import com.forgeessentials.api.permissions.Zone;
import com.forgeessentials.commons.selections.AreaBase;
import com.forgeessentials.commons.selections.WorldArea;
import com.forgeessentials.commons.selections.WorldPoint;
import com.forgeessentials.economy.ModuleEconomy;
import com.forgeessentials.protection.ModuleProtection;
import com.forgeessentials.util.ServerUtil;
import com.forgeessentials.util.events.EventCancelledException;
import com.forgeessentials.util.events.PlotEvent;
import com.forgeessentials.util.events.PlotEvent.OwnerChanged;
import com.forgeessentials.util.output.ChatOutputHandler;
public class Plot
{
public static final String GROUP_ALL = Zone.GROUP_DEFAULT;
public static final String GROUP_PLOT_OWNER = "PLOT_OWNER";
public static final String GROUP_PLOT_MOD = "PLOT_MOD";
public static final String GROUP_PLOT_USER = "PLOT_USER";
public static final String SERVER_OWNER = "SERVER";
// Internal data permission properties (should NEVER be edited by hand)
public static final String PERM_OWNER = FEPermissions.FE_INTERNAL + ".plot.owner";
// Basic plot permissions
public static final String PERM = ModuleEconomy.PERM + ".plot";
public static final String PERM_PRICE = PERM + ".price";
public static final String PERM_COLUMN = PERM + ".column";
// public static final String PERM_ADMIN = PERM + ".admin";
public static final String PERM_COMMAND = PERM + ".command";
public static final String PERM_DEFINE = PERM_COMMAND + ".define";
public static final String PERM_CLAIM = PERM_COMMAND + ".claim";
public static final String PERM_DELETE = PERM_COMMAND + ".delete";
public static final String PERM_BUY = PERM_COMMAND + ".buy";
public static final String PERM_SELL = PERM_COMMAND + ".sell";
public static final String PERM_MODS = PERM_COMMAND + ".mods";
public static final String PERM_SET = PERM_COMMAND + ".set";
public static final String PERM_SET_PRICE = PERM_COMMAND + ".price";
public static final String PERM_SET_FEE = PERM_COMMAND + ".fee";
public static final String PERM_PERMS = PERM_COMMAND + ".perms";
public static final String PERM_PERMS_BUILD = PERM_SET + ".build";
public static final String PERM_PERMS_USE = PERM_SET + ".use";
public static final String PERM_PERMS_INTERACT = PERM_SET + ".interact";
public static final String PERM_PERMS_CHEST = PERM_SET + ".chest";
public static final String PERM_LIST = PERM_COMMAND + ".list";
public static final String PERM_LIST_OWN = PERM_LIST + ".own";
public static final String PERM_LIST_ALL = PERM_LIST + ".all";
public static final String PERM_LIST_SALE = PERM_LIST + ".sale";
// Maximum number and total size of plots a user is allowed to claim
public static final String PERM_LIMIT = PERM + ".limit";
public static final String PERM_LIMIT_COUNT = PERM_LIMIT + ".count";
public static final String PERM_LIMIT_SIZE = PERM_LIMIT + ".size";
// Maximum and minimum size a plot can be
public static final String PERM_SIZE = PERM + ".size";
public static final String PERM_SIZE_MIN = PERM_SIZE + ".min";
public static final String PERM_SIZE_MAX = PERM_SIZE + ".max";
// User editable plot data permissions
public static final String PERM_DATA = PERM + ".data";
public static final String PERM_NAME = PERM_DATA + ".name";
public static final String PERM_FEE = PERM_DATA + ".fee";
public static final String PERM_FEE_TIMEOUT = PERM_DATA + ".fee.timeout";
public static final String PERM_SELL_PRICE = PERM_DATA + ".price";
private static Map<Integer, Plot> plots = new HashMap<>();
private AreaZone zone;
private UserIdent owner;
private Plot(AreaZone zone, UserIdent owner)
{
this.zone = zone;
this.owner = owner;
}
public AreaZone getZone()
{
return zone;
}
public int getDimension()
{
return zone.getWorldZone().getDimensionID();
}
public WorldPoint getCenter()
{
return new WorldPoint(getDimension(), zone.getArea().getCenter());
}
public boolean hasOwner()
{
return owner != null;
}
public UserIdent getOwner()
{
return owner;
}
public void setOwner(UserIdent newOwner)
{
if (newOwner == owner || (newOwner != null && newOwner.equals(owner)))
return;
OwnerChanged event = new PlotEvent.OwnerChanged(this, owner);
if (owner != null)
zone.removePlayerFromGroup(owner, GROUP_PLOT_OWNER);
// Set new owner
owner = newOwner;
if (owner == null)
zone.clearGroupPermission(GROUP_ALL, PERM_OWNER);
else
zone.setGroupPermissionProperty(GROUP_ALL, PERM_OWNER, owner.getOrGenerateUuid().toString());
if (owner != null)
zone.addPlayerToGroup(newOwner, GROUP_PLOT_OWNER);
APIRegistry.getFEEventBus().post(event);
}
public String getOwnerName()
{
if (owner == null)
return SERVER_OWNER;
else
return owner.getUsernameOrUuid();
}
public String getName()
{
String name = zone.getGroupPermission(GROUP_ALL, PERM_NAME);
if (name == null)
name = APIRegistry.perms.getGroupPermissionProperty(GROUP_ALL, PERM_NAME);
if (name == null)
return null;
return name.replaceAll("@p", owner.getUsernameOrUuid());
}
public String getNameNotNull()
{
String name = getName();
if (name != null)
return name;
return "<unnamed>";
}
public WorldPoint getPlotCenter()
{
return new WorldPoint(getDimension(), zone.getArea().getCenter());
}
/**
* Gets the size that counts for price / limit calculation. Depending on whether the column flag is set or not, this
* is the area or volume of the plot.
*
* @return accounted size
*/
public long getAccountedSize()
{
AreaBase area = zone.getArea();
boolean columnMode = APIRegistry.perms.checkGroupPermission(GROUP_ALL, zone, PERM_PRICE);
return columnMode ? area.getXLength() * area.getZLength() : area.getXLength() * area.getYLength() * area.getZLength();
}
public long getCalculatedPrice()
{
String priceStr = APIRegistry.perms.getGroupPermissionProperty(GROUP_ALL, getPlotCenter(), PERM_PRICE);
if (priceStr == null)
return 0;
double pricePerUnit = ServerUtil.parseDoubleDefault(priceStr, 0);
if (pricePerUnit == 0)
return 0;
return (long) (getAccountedSize() * pricePerUnit);
}
public long getPrice()
{
return ServerUtil.parseLongDefault(zone.getGroupPermission(GROUP_ALL, PERM_SELL_PRICE), -1);
}
public void setPrice(long value)
{
if (value < 0)
zone.clearGroupPermission(GROUP_ALL, PERM_SELL_PRICE);
else
zone.setGroupPermissionProperty(GROUP_ALL, PERM_SELL_PRICE, Long.toString(value));
}
public boolean isForSale()
{
return getPrice() >= 0;
}
/* ------------------------------------------------------------ */
public int getFee()
{
return Math.max(0, ServerUtil.parseIntDefault(zone.getGroupPermission(GROUP_ALL, PERM_FEE), 0));
}
public void setFee(int value)
{
if (value < 0)
zone.setGroupPermissionProperty(GROUP_ALL, PERM_FEE, Long.toString(value));
else
zone.clearGroupPermission(GROUP_ALL, PERM_FEE);
}
public int getFeeTimeout()
{
return Math.max(0, ServerUtil.parseIntDefault(zone.getGroupPermission(GROUP_ALL, PERM_FEE_TIMEOUT), 0));
}
public void setFeeTimeout(int minutes)
{
if (minutes <= 0)
zone.setGroupPermissionProperty(GROUP_ALL, PERM_FEE_TIMEOUT, Long.toString(minutes));
else
zone.clearGroupPermission(GROUP_ALL, PERM_FEE_TIMEOUT);
}
/* ------------------------------------------------------------ */
public void setPermission(String permission, boolean userPerm, boolean value)
{
zone.setGroupPermission(GROUP_PLOT_OWNER, permission, true);
zone.setGroupPermission(GROUP_PLOT_MOD, permission, true);
zone.setGroupPermission(userPerm ? GROUP_PLOT_USER : GROUP_ALL, permission, value);
}
public void setDefaultPermissions()
{
zone.setGroupPermissionProperty(GROUP_ALL, PERM_OWNER, owner == null ? SERVER_OWNER : owner.getOrGenerateUuid().toString());
if (owner != null)
zone.addPlayerToGroup(owner, GROUP_PLOT_OWNER);
setPermission(ModuleProtection.PERM_BREAK + Zone.ALL_PERMS, false, false);
setPermission(ModuleProtection.PERM_PLACE + Zone.ALL_PERMS, false, false);
setPermission(ModuleProtection.PERM_USE + Zone.ALL_PERMS, false, false);
setPermission(ModuleProtection.PERM_INTERACT + Zone.ALL_PERMS, false, false);
setPermission(ModuleProtection.PERM_BREAK + Zone.ALL_PERMS, true, false);
setPermission(ModuleProtection.PERM_PLACE + Zone.ALL_PERMS, true, true);
setPermission(ModuleProtection.PERM_USE + Zone.ALL_PERMS, true, true);
setPermission(ModuleProtection.PERM_INTERACT + Zone.ALL_PERMS, true, true);
zone.setGroupPermission(GROUP_ALL, ModuleProtection.PERM_BREAK + Zone.ALL_PERMS, false);
zone.setGroupPermission(GROUP_ALL, ModuleProtection.PERM_PLACE + Zone.ALL_PERMS, false);
zone.setGroupPermission(GROUP_ALL, ModuleProtection.PERM_USE + Zone.ALL_PERMS, false);
zone.setGroupPermission(GROUP_ALL, ModuleProtection.PERM_INTERACT + Zone.ALL_PERMS, false);
// zone.setGroupPermission(GROUP_ALL, ModuleProtection.PERM_INTERACT_ENTITY + Zone.ALL_PERMS, false);
}
public void printInfo(ICommandSender sender)
{
ChatOutputHandler.chatNotification(sender, String.format("#%d: \"%s\" at %s", zone.getId(), getName(), getCenter().toString()));
}
public void printDetails(ICommandSender sender)
{
ChatOutputHandler.chatNotification(sender, String.format("Plot #%d: %s", zone.getId(), getName()));
ChatOutputHandler.chatNotification(sender, String.format(" Owner: %s", owner.getUsernameOrUuid()));
ChatOutputHandler.chatNotification(sender,
String.format(" Location between %s and %s", zone.getArea().getHighPoint().toString(), zone.getArea().getLowPoint().toString()));
long price = getPrice();
if (price >= 0)
ChatOutputHandler.chatNotification(sender, String.format(" Price: %d", price));
else
ChatOutputHandler.chatNotification(sender, " Not open for sale");
}
/* ------------------------------------------------------------ */
/* Static plot management functions */
/* ------------------------------------------------------------ */
public static boolean isPlot(AreaZone zone)
{
return plots.containsKey(zone.getId());
}
public static boolean isColumnMode(int dimension)
{
ServerZone s = APIRegistry.perms.getServerZone();
List<Zone> zones = new ArrayList<>();
zones.add(s.getWorldZone(dimension));
zones.add(s);
zones.add(s.getRootZone());
String permValue = s.getPermissionProperty(zones, null, Arrays.asList(GROUP_ALL), PERM_COLUMN);
return APIRegistry.perms.checkBooleanPermission(permValue);
}
/**
* Gets the size that counts for price / limit calculation. Depending on whether the column flag is set or not, this
* is the area or volume of the plot.
*
* @return accounted size
*/
public static long getAccountedSize(WorldArea area)
{
boolean columnMode = isColumnMode(area.getDimension());
return columnMode ? area.getXLength() * area.getZLength() : area.getXLength() * area.getYLength() * area.getZLength();
}
public static long getCalculatedPrice(WorldArea area)
{
String priceStr = APIRegistry.perms.getGroupPermissionProperty(GROUP_ALL, area.getCenter(), PERM_PRICE);
if (priceStr == null)
return 0;
double pricePerUnit = ServerUtil.parseDoubleDefault(priceStr, 0);
if (pricePerUnit == 0)
return 0;
return (long) (getAccountedSize(area) * pricePerUnit);
}
/* ------------------------------------------------------------ */
public static Plot getPlot(int zoneId)
{
return plots.get(zoneId);
}
public static Plot getPlot(WorldPoint point)
{
List<Zone> zones = APIRegistry.perms.getServerZone().getZonesAt(point);
for (Zone zone : zones)
{
Plot plot = plots.get(zone.getId());
if (plot != null)
return plot;
}
return null;
}
public static Collection<Plot> getPlots()
{
return plots.values();
}
/* ------------------------------------------------------------ */
private static void registerPlot(Plot plot)
{
plots.put(plot.getZone().getId(), plot);
}
public static Plot define(WorldArea area, UserIdent owner) throws EventCancelledException, PlotRedefinedException
{
WorldZone worldZone = APIRegistry.perms.getServerZone().getWorldZone(area.getDimension());
for (AreaZone zone : worldZone.getAreaZones())
if (isPlot(zone) && zone.getArea().contains(area))
throw new PlotRedefinedException();
if (isColumnMode(area.getDimension()))
area = new WorldArea(area.getDimension(), area.getHighPoint().setY(MinecraftServer.getServer().getBuildLimit()), area.getLowPoint().setY(0));
AreaZone zone = new AreaZone(worldZone, "_PLOT_" + (APIRegistry.perms.getServerZone().getMaxZoneID() + 1), area);
Plot plot = new Plot(zone, owner);
registerPlot(plot);
zone.setHidden(true);
plot.setDefaultPermissions();
return plot;
}
public static void loadPlots()
{
plots.clear();
for (Zone zone : APIRegistry.perms.getZones())
if (zone instanceof AreaZone)
{
String ownerId = zone.getGroupPermission(GROUP_ALL, PERM_OWNER);
if (ownerId != null)
registerPlot(new Plot((AreaZone) zone, ownerId.equals(SERVER_OWNER) ? null : UserIdent.get(ownerId)));
}
}
static void registerPermissions()
{
IPermissionsHelper perms = APIRegistry.perms;
RootZone root = perms.getServerZone().getRootZone();
perms.registerPermissionDescription(PERM_OWNER, "UUID of the owner of this plot (DO NOT MODIFY!)");
perms.registerPermissionDescription(PERM, "Plot permissions");
perms.registerPermissionProperty(PERM_PRICE, "1", "Price multiplier for plots (plot size will be multiplied with this value)");
// perms.registerPermissionPropertyOp(PERM_PRICE, "0");
perms.registerPermission(PERM_COLUMN, PermissionLevel.TRUE,
"If true, all plots will always extend from bottom to top of the world. Price will only depend on X and Z dimensions.");
perms.registerPermissionProperty(PERM_LIMIT_COUNT, "20", "Maximum number of plots a player can claim");
perms.registerPermissionDescription(PERM_LIMIT_SIZE, "Maximum total size of all plots a player can claim");
perms.registerPermissionProperty(PERM_SIZE_MIN, "3", "Minimum size of one plot axis");
perms.registerPermissionDescription(PERM_SIZE_MAX, "Maximum size of one plot axis");
perms.registerPermissionDescription(PERM_DATA, "Individual settings for plots");
perms.registerPermissionProperty(PERM_NAME, "@p's plot", "Name of the plot (@p inserts owner name)");
perms.registerPermissionDescription(PERM_FEE, "Price players have to pay in order to enter this plot");
perms.registerPermissionDescription(PERM_FEE_TIMEOUT, "Duration that a player can access the plot after paying the fee");
perms.registerPermissionDescription(PERM_SELL_PRICE, "Price that the plot can be bought for by other players (sell offer)");
perms.registerPermission(PERM_COMMAND, PermissionLevel.TRUE, "Plot management command");
perms.registerPermission(PERM_DEFINE, PermissionLevel.OP, "Allows to define plots without paying");
perms.registerPermission(PERM_CLAIM, PermissionLevel.TRUE, "Allows to claim plots in exchange for money");
perms.registerPermission(PERM_BUY, PermissionLevel.TRUE, "Allows buying plots");
perms.registerPermission(PERM_SELL, PermissionLevel.OP, "Allows selling plots");
perms.registerPermission(PERM_MODS, PermissionLevel.OP, "Allows managing plot administrators");
perms.registerPermission(PERM_LIST, PermissionLevel.TRUE, "Allows listing plots");
perms.registerPermission(PERM_LIST_ALL, PermissionLevel.OP, "List all plots");
perms.registerPermission(PERM_LIST_OWN, PermissionLevel.TRUE, "List own plots");
perms.registerPermission(PERM_LIST_SALE, PermissionLevel.TRUE, "List plots open for sale");
perms.registerPermission(PERM_SET, PermissionLevel.OP, "Control plot settings");
perms.registerPermission(PERM_PERMS, PermissionLevel.OP, "Control plot settings");
perms.registerPermission(PERM_PERMS_BUILD, PermissionLevel.OP, "Control build permissions");
perms.registerPermission(PERM_PERMS_USE, PermissionLevel.OP, "Control item usage permissions");
perms.registerPermission(PERM_PERMS_INTERACT, PermissionLevel.OP, "Control interaction permissions");
perms.registerPermission(PERM_PERMS_CHEST, PermissionLevel.OP, "Control chest permissions");
root.setGroupPermission(GROUP_PLOT_OWNER, PERM_SET, true);
root.setGroupPermission(GROUP_PLOT_OWNER, PERM_SELL, true);
root.setGroupPermission(GROUP_PLOT_OWNER, PERM_PERMS, true);
root.setGroupPermission(GROUP_PLOT_OWNER, PERM_MODS, true);
}
public static class PlotRedefinedException extends Exception
{
}
}