diff --git a/bukkit/src/main/java/com/griefdefender/GDBootstrap.java b/bukkit/src/main/java/com/griefdefender/GDBootstrap.java new file mode 100644 index 0000000..86bbf60 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDBootstrap.java @@ -0,0 +1,202 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import com.griefdefender.util.BootstrapUtil; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class GDBootstrap extends JavaPlugin { + + private Map jarMap = new HashMap<>(); + private List relocateList = new ArrayList<>(); + private static GDBootstrap instance; + private static final String LIB_ROOT_PATH = "./plugins/GriefDefender/lib/"; + private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7"; + + public static GDBootstrap getInstance() { + return instance; + } + + @Override + public void onEnable() { + instance = this; + final JSONParser parser = new JSONParser(); + String bukkitJsonVersion = null; + this.getLogger().info("Loading libraries..."); + if (Bukkit.getVersion().contains("1.8.8")) { + bukkitJsonVersion = "1.8.8"; + } else if (Bukkit.getVersion().contains("1.12.2")) { + bukkitJsonVersion = "1.12.2"; + } else if (Bukkit.getVersion().contains("1.13.2")) { + bukkitJsonVersion = "1.13.2"; + } else if (Bukkit.getVersion().contains("1.14.2")) { + bukkitJsonVersion = "1.14.2"; + } else if (Bukkit.getVersion().contains("1.14.3")) { + bukkitJsonVersion = "1.14.3"; + } else if (Bukkit.getVersion().contains("1.14.4")) { + bukkitJsonVersion = "1.14.4"; + } else { + this.getLogger().severe("Detected unsupported version '" + Bukkit.getVersion() + "'. GriefDefender only supports 1.8.8, 1.12.2, 1.13.2, and 1.14.2. GriefDefender will NOT load."); + return; + } + try { + final InputStream in = getClass().getResourceAsStream("/" + bukkitJsonVersion + ".json"); + final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + final JSONObject a = (JSONObject) parser.parse(reader); + final JSONArray libraries = (JSONArray) a.get("libraries"); + if (libraries == null) { + this.getLogger().severe("Resource " + bukkitJsonVersion + ".json is corrupted!. Please contact author for assistance."); + return; + } + final Iterator iterator = libraries.iterator(); + while (iterator.hasNext()) { + JSONObject lib = iterator.next(); + final String name = (String) lib.get("name"); + final String sha1 = (String) lib.get("sha1"); + final String path = (String) lib.get("path"); + final String relocate = (String) lib.get("relocate"); + final String url = (String) lib.get("url"); + final Path libPath = Paths.get(LIB_ROOT_PATH).resolve(path); + final File file = libPath.toFile(); + downloadLibrary(name, relocate, sha1, url, libPath); + } + } catch (Throwable t) { + t.printStackTrace(); + } + // Inject jar-relocator and asm debug + injectRelocatorDeps(); + // Relocate all GD dependencies and inject + GDRelocator.getInstance().relocateJars(this.jarMap); + // Boot GD + GriefDefenderPlugin.getInstance().onEnable(); + } + + public List getRelocateList() { + return this.relocateList; + } + + @Override + public void onDisable() { + GriefDefenderPlugin.getInstance().onDisable(); + } + + private void injectRelocatorDeps() { + String name = "org.ow2.asm:asm-debug-all:5.2"; + File file = this.jarMap.get(name); + BootstrapUtil.addUrlToClassLoader(name, file); + name = "me.lucko:jar-relocator:1.3"; + file = this.jarMap.get(name); + BootstrapUtil.addUrlToClassLoader(name, file); + } + + public void downloadLibrary(String name, String relocate, String sha1, String url, Path libPath) { + final File file = libPath.toFile(); + this.jarMap.put(name, file); + if (relocate != null && !relocate.isEmpty() && relocate.contains(":")) { + this.relocateList.add(relocate); + } + if (!Files.exists(libPath)) { + this.getLogger().info("Downloading library " + name + " ..."); + try { + URL website = new URL(url); + URLConnection urlConnection = website.openConnection(); + // Some maven repos like nexus require a user agent so we just pass one to satisfy it + urlConnection.setRequestProperty("User-Agent", USER_AGENT); + ReadableByteChannel rbc = Channels.newChannel(urlConnection.getInputStream()); + if (!Files.exists(libPath)) { + file.getParentFile().mkdirs(); + } + FileOutputStream fos = new FileOutputStream(file); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + } catch (IOException e) { + this.getLogger().severe("An error occured while downloading library '" + name + "'. Skipping..."); + e.printStackTrace(); + return; + } + + + final String hash = getLibraryHash(file); + + if (hash == null || !sha1.equals(hash)) { + this.getLogger().severe("Detected invalid hash '" + hash + "' for file '" + libPath + "'. Expected '" + sha1 + "'. Skipping..."); + try { + Files.delete(libPath); + return; + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + } + + this.jarMap.put(name, file); + } + + private String getLibraryHash(File file) { + try { + final MessageDigest md = MessageDigest.getInstance("SHA-1"); + final byte[] data = Files.readAllBytes(file.toPath()); + final byte[] b = md.digest(data); + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < b.length; i++) { + if ((0xff & b[i]) < 0x10) { + buffer.append("0" + Integer.toHexString((0xFF & b[i]))); + } else { + buffer.append(Integer.toHexString(0xFF & b[i])); + } + } + return buffer.toString(); + } catch (Throwable t) { + t.printStackTrace(); + } + return null; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/GDCatalogType.java b/bukkit/src/main/java/com/griefdefender/GDCatalogType.java new file mode 100644 index 0000000..0b0cf88 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDCatalogType.java @@ -0,0 +1,76 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.StringJoiner; + +import com.griefdefender.api.CatalogType; + +public abstract class GDCatalogType implements CatalogType { + + private final String id; + + public GDCatalogType(String id) { + this.id = checkNotNull(id, "id"); + } + + @Override + public final String getId() { + return this.id; + } + + @Override + public String getName() { + return getId(); + } + + @Override + public final int hashCode() { + return this.id.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CatalogType other = (CatalogType) obj; + return getId().equals(other.getId()); + } + + @Override + public String toString() { + return new StringJoiner(", ", GDCatalogType.class.getSimpleName() + "[", "]") + .add("id=" + getId()) + .add("name=" + getName()) + .toString(); + } + +} diff --git a/bukkit/src/main/java/com/griefdefender/GDCore.java b/bukkit/src/main/java/com/griefdefender/GDCore.java new file mode 100644 index 0000000..38ca95d --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDCore.java @@ -0,0 +1,85 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import com.google.common.collect.ImmutableList; +import com.google.inject.Singleton; +import com.griefdefender.api.Core; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimBlockSystem; +import com.griefdefender.api.claim.ClaimManager; +import com.griefdefender.api.data.PlayerData; +import com.griefdefender.api.permission.flag.Flag; +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Singleton +public class GDCore implements Core { + + @Override + public boolean isEnabled(UUID worldUniqueId) { + return GriefDefenderPlugin.getInstance().claimsEnabledForWorld(worldUniqueId); + } + + @Override + public ClaimBlockSystem getClaimBlockSystem() { + return GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.claimBlockSystem; + } + + @Override + public boolean isEconomyModeEnabled() { + return GriefDefenderPlugin.getInstance().isEconomyModeEnabled(); + } + + @Override + public boolean isProtectionModuleEnabled(Flag flag) { + return GriefDefenderPlugin.getGlobalConfig().getConfig().modules.isProtectionModuleEnabled(flag.toString()); + } + + @Override + public ClaimManager getClaimManager(UUID worldUniqueId) { + return GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(worldUniqueId); + } + + @Override + public Optional getPlayerData(UUID worldUniqueId, UUID playerUniqueId) { + return Optional.ofNullable(GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(worldUniqueId, playerUniqueId)); + } + + @Override + public List getAllPlayerClaims(UUID playerUniqueId) { + List claimList = new ArrayList<>(); + for (World world : Bukkit.getServer().getWorlds()) { + claimList.addAll(this.getClaimManager(world.getUID()).getPlayerClaims(playerUniqueId)); + } + + return ImmutableList.copyOf(claimList); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/GDDebugData.java b/bukkit/src/main/java/com/griefdefender/GDDebugData.java new file mode 100644 index 0000000..222dca9 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDDebugData.java @@ -0,0 +1,206 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.griefdefender.api.Tristate; +import com.griefdefender.util.HttpClient; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.format.TextColor; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +public class GDDebugData { + private static final String BYTEBIN_ENDPOINT = "https://bytebin.lucko.me/post"; + private static final String DEBUG_VIEWER_URL = "https://griefprevention.github.io/debug/?"; + private static final MediaType PLAIN_TYPE = MediaType.parse("text/plain; charset=utf-8"); + + private static final int MAX_LINES = 5000; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + private static final Component GD_TEXT = TextComponent.builder("").append("[", TextColor.WHITE).append("GD", TextColor.AQUA).append("] ", TextColor.WHITE).build(); + + private final CommandSender source; + private final List header; + private final List records; + private final long startTime = System.currentTimeMillis(); + private boolean verbose; + private OfflinePlayer target; + + public GDDebugData(CommandSender source, OfflinePlayer target, boolean verbose) { + this.source = source; + this.target = target; + this.verbose = verbose; + this.records = new ArrayList<>(); + this.header = new ArrayList<>(); + this.header.add("# GriefDefender Debug Log"); + this.header.add("#### This file was automatically generated by [GriefDefender](https://github.com/MinecraftPortCentral/GriefDefender) "); + this.header.add(""); + this.header.add("### Metadata"); + this.header.add("| Key | Value |"); + this.header.add("|-----|-------|"); + this.header.add("| GD Version | " + GriefDefenderPlugin.IMPLEMENTATION_VERSION + "|"); + this.header.add("| Bukkit Version | " + Bukkit.getVersion() + "|"); + final Plugin lpContainer = Bukkit.getPluginManager().getPlugin("luckperms"); + if (lpContainer != null) { + final String version = lpContainer.getDescription().getVersion(); + if (version != null) { + this.header.add("| LuckPerms Version | " + version); + } + } + this.header.add("| User | " + (this.target == null ? "ALL" : this.target.getName()) + "|"); + this.header.add("| Record start | " + DATE_FORMAT.format(new Date(this.startTime)) + "|"); + } + + public void addRecord(String flag, String trust, String source, String target, String location, String user, String permission, Tristate result) { + if (this.records.size() < MAX_LINES) { + this.records.add("| " + flag + " | " + trust + " | " + source + " | " + target + " | " + location + " | " + user + " | " + permission + " | " + result + " | "); + } else { + TextAdapter.sendComponent(this.source, TextComponent.builder("").append("MAX DEBUG LIMIT REACHED!").append("\n") + .append("Pasting output...", TextColor.GREEN).build()); + this.pasteRecords(); + this.records.clear(); + GriefDefenderPlugin.debugActive = false; + TextAdapter.sendComponent(this.source, TextComponent.builder("").append(GD_TEXT).append("Debug ", TextColor.GRAY).append("OFF", TextColor.RED).build()); + } + } + + public CommandSender getSource() { + return this.source; + } + + public OfflinePlayer getTarget() { + return this.target; + } + + public boolean isRecording() { + return !this.verbose; + } + + public void setTarget(OfflinePlayer user) { + this.target = user; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public void pasteRecords() { + if (this.records.isEmpty()) { + TextAdapter.sendComponent(this.source, TextComponent.of("No debug records to paste!", TextColor.RED)); + return; + } + + final long endTime = System.currentTimeMillis(); + List debugOutput = new ArrayList<>(this.header); + debugOutput.add("| Record end | " + DATE_FORMAT.format(new Date(endTime)) + "|"); + long elapsed = (endTime - startTime) / 1000L; + debugOutput.add("| Time elapsed | " + elapsed + " seconds" + "|"); + debugOutput.add(""); + debugOutput.add("### Output") ; + debugOutput.add("| Flag | Trust | Source | Target | Location | User | Permission | Result |"); + debugOutput.add("|------|-------|--------|--------|----------|------|------------|--------|"); + + debugOutput.addAll(this.records); + + String content = String.join("\n", debugOutput); + + String pasteId; + try { + pasteId = postContent(content); + } catch (Exception e) { + TextAdapter.sendComponent(this.source, TextComponent.builder("").append("Error uploading content : ", TextColor.RED).append(e.getMessage(), TextColor.WHITE).build()); + return; + } + + String url = DEBUG_VIEWER_URL + pasteId; + + URL jUrl; + try { + jUrl = new URL(url); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + TextAdapter.sendComponent(this.source, TextComponent.builder("").append("Paste success! : " + url, TextColor.GREEN) + .clickEvent(ClickEvent.openUrl(jUrl.toString())).build()); + } + + private static String postContent(String content) throws IOException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + try (GZIPOutputStream writer = new GZIPOutputStream(byteOut)) { + writer.write(content.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + + RequestBody body = RequestBody.create(PLAIN_TYPE, byteOut.toByteArray()); + + Request.Builder requestBuilder = new Request.Builder() + .url(BYTEBIN_ENDPOINT) + .header("Content-Encoding", "gzip") + .post(body); + + Request request = requestBuilder.build(); + try (Response response = HttpClient.makeCall(request)) { + try (ResponseBody responseBody = response.body()) { + if (responseBody == null) { + throw new RuntimeException("No response"); + } + + try (InputStream inputStream = responseBody.byteStream()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + JsonObject object = new Gson().fromJson(reader, JsonObject.class); + return object.get("key").getAsString(); + } + } + } + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/GDEventManager.java b/bukkit/src/main/java/com/griefdefender/GDEventManager.java new file mode 100644 index 0000000..5bd326c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDEventManager.java @@ -0,0 +1,67 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import com.google.inject.Singleton; +import com.griefdefender.api.event.Event; +import com.griefdefender.api.event.EventManager; +import net.kyori.event.EventBus; +import net.kyori.event.SimpleEventBus; +import net.kyori.event.method.MethodSubscriptionAdapter; +import net.kyori.event.method.SimpleMethodSubscriptionAdapter; +import net.kyori.event.method.asm.ASMEventExecutorFactory; + +@Singleton +public class GDEventManager implements EventManager { + + private final EventBus bus; + private final MethodSubscriptionAdapter adapter; + + public GDEventManager() { + this.bus = new SimpleEventBus(Event.class); + this.adapter = new SimpleMethodSubscriptionAdapter<>(bus, new ASMEventExecutorFactory<>()); + } + + @Override + public EventBus getBus() { + return this.bus; + } + + @Override + public void post(Event event) { + this.bus.post(event); + } + + @Override + public void register(Object listener) { + this.adapter.register(listener); + } + + @Override + public void unregister(Object listener) { + this.adapter.unregister(listener); + } + +} diff --git a/bukkit/src/main/java/com/griefdefender/GDPlayerData.java b/bukkit/src/main/java/com/griefdefender/GDPlayerData.java new file mode 100644 index 0000000..25c4117 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDPlayerData.java @@ -0,0 +1,601 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.ShovelType; +import com.griefdefender.api.claim.ShovelTypes; +import com.griefdefender.api.data.PlayerData; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.configuration.GriefDefenderConfig; +import com.griefdefender.configuration.PlayerStorageData; +import com.griefdefender.internal.block.BlockSnapshot; +import com.griefdefender.internal.block.BlockTransaction; +import com.griefdefender.internal.util.NMSUtil; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.provider.VaultProvider; +import com.griefdefender.util.PermissionUtil; +import me.lucko.luckperms.api.context.MutableContextSet; +import net.kyori.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitTask; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class GDPlayerData implements PlayerData { + + public UUID playerID; + public UUID worldUniqueId; + private WeakReference playerSubject; + private Set claimList; + private PlayerStorageData playerStorage; + public Location lastAfkCheckLocation; + public Location lastShovelLocation; + public Location endShovelLocation; + public Location lastValidInspectLocation; + public ShovelType shovelMode = ShovelTypes.BASIC; + + public GDClaim claimResizing; + public GDClaim claimSubdividing; + + public List visualBlocks; + public UUID visualClaimId; + public BukkitTask visualRevertTask; + private final VaultProvider vaultProvider = GriefDefenderPlugin.getInstance().getVaultProvider(); + + public boolean ignoreClaims = false; + + public boolean debugClaimPermissions = false; + public WeakReference lastClaim = new WeakReference<>(null); + + public boolean inTown = false; + public boolean townChat = false; + + // Always ignore active contexts by default + // This prevents protection issues when other plugins call getActiveContext + public boolean ignoreActiveContexts = true; + + public boolean lastInteractResult = false; + public int lastTickCounter = 0; + public UUID lastInteractClaim = GriefDefenderPlugin.PUBLIC_UUID; + + // collide event cache + public int lastCollideEntityId = 0; + public boolean lastCollideEntityResult = false; + + private String playerName; + + // cached global option values + public int minClaimLevel; + private Integer optionClaimCreateMode; + private Integer optionMaxAccruedBlocks; + + // cached permission values + public boolean canManageAdminClaims = false; + public boolean canManageWilderness = false; + public boolean ignoreBorderCheck = false; + public boolean ignoreAdminClaims = false; + public boolean ignoreBasicClaims = false; + public boolean ignoreTowns = false; + public boolean ignoreWilderness = false; + + public boolean dataInitialized = false; + public boolean showVisualFillers = true; + private boolean checkedDimensionHeight = false; + + public GDPlayerData(UUID worldUniqueId, UUID playerUniqueId, PlayerStorageData playerStorage, GriefDefenderConfig activeConfig, Set claims) { + this.worldUniqueId = worldUniqueId; + this.playerID = playerUniqueId; + this.playerStorage = playerStorage; + this.claimList = claims; + this.refreshPlayerOptions(); + } + + // Run async + public void refreshPlayerOptions() { + //final GriefDefenderConfig activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId); + GriefDefenderPlugin.getInstance().executor.execute(() -> { + if (this.playerSubject == null || this.playerSubject.get() == null) { + GDPermissionUser subject = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); + this.playerSubject = new WeakReference<>(subject); + } + final GDPermissionUser subject = this.playerSubject.get(); + final MutableContextSet activeContexts = PermissionUtil.getInstance().getActiveContexts(subject); + // permissions + this.ignoreBorderCheck = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_BORDER_CHECK, activeContexts).asBoolean(); + this.ignoreAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_ADMIN, activeContexts).asBoolean(); + this.ignoreTowns = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_TOWN, activeContexts).asBoolean(); + this.ignoreWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_WILDERNESS, activeContexts).asBoolean(); + this.ignoreBasicClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_BASIC, activeContexts).asBoolean(); + this.canManageAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.COMMAND_ADMIN_CLAIMS, activeContexts).asBoolean(); + this.canManageWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_WILDERNESS, activeContexts).asBoolean(); + this.playerID = subject.getUniqueId(); + /*if (this.optionMaxClaimLevel > 255 || this.optionMaxClaimLevel <= 0 || this.optionMaxClaimLevel < this.optionMinClaimLevel) { + this.optionMaxClaimLevel = 255; + } + if (this.optionMinClaimLevel < 0 || this.optionMinClaimLevel >= 255 || this.optionMinClaimLevel > this.optionMaxClaimLevel) { + this.optionMinClaimLevel = 0; + }*/ + this.dataInitialized = true; + this.checkedDimensionHeight = false; + }); + } + + public String getPlayerName() { + if (this.playerName == null) { + GDPermissionUser user = this.playerSubject.get(); + if (user == null) { + user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); + } + if (user != null) { + this.playerName = user.getFriendlyName(); + } + if (this.playerName == null) { + this.playerName = "[unknown]"; + } + } + + return this.playerName; + } + + public void revertActiveVisual(Player player) { + if (this.visualRevertTask != null) { + this.visualRevertTask.cancel(); + this.visualRevertTask = null; + } + + if (this.visualClaimId != null) { + GDClaim claim = (GDClaim) GriefDefenderPlugin.getInstance().dataStore.getClaim(this.worldUniqueId, this.visualClaimId); + if (claim != null) { + claim.playersWatching.remove(this.playerID); + } + } + this.visualClaimId = null; + if (this.visualBlocks == null || !player.getWorld().equals(this.visualBlocks.get(0).getFinal().getLocation().getWorld())) { + return; + } + + for (int i = 0; i < this.visualBlocks.size(); i++) { + BlockSnapshot snapshot = this.visualBlocks.get(i).getOriginal(); + NMSUtil.getInstance().sendBlockChange(player, snapshot); + } + } + + @Override + public int getBlocksAccruedPerHour() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.BLOCKS_ACCRUED_PER_HOUR, this).intValue(); + } + + @Override + public int getChestClaimExpiration() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.CHEST_EXPIRATION, this).intValue(); + } + + @Override + public int getCreateClaimLimit(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.CREATE_LIMIT, type, this).intValue(); + } + + @Override + public int getInitialClaimBlocks() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.INITIAL_BLOCKS, this).intValue(); + } + + @Override + public int getRemainingClaimBlocks() { + final int initialClaimBlocks = GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.INITIAL_BLOCKS, this).intValue(); + int remainingBlocks = initialClaimBlocks + this.getAccruedClaimBlocks() + this.getBonusClaimBlocks(); + if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + if (!this.vaultProvider.getApi().hasAccount(this.getSubject().getOfflinePlayer())) { + return 0; + } + + final double currentFunds = this.vaultProvider.getApi().getBalance(this.getSubject().getOfflinePlayer()); + final Double economyBlockCost = GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.ECONOMY_BLOCK_COST, this); + remainingBlocks = (int) Math.round((currentFunds / economyBlockCost)); + } else { + for (Claim claim : this.claimList) { + if (claim.isSubdivision()) { + continue; + } + + GDClaim gpClaim = (GDClaim) claim; + if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) { + remainingBlocks -= claim.getClaimBlocks(); + } + } + } + + return remainingBlocks; + } + + public int getTotalClaimsCost() { + int totalCost = 0; + for (Claim claim : this.claimList) { + if (claim.isSubdivision()) { + continue; + } + + final GDClaim gpClaim = (GDClaim) claim; + if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) { + totalCost += claim.getClaimBlocks(); + } + } + + return totalCost; + } + + public double getRemainingChunks() { + final double remainingChunks = this.getRemainingClaimBlocks() / 65536.0; + return Math.round(remainingChunks * 100.0)/100.0; + } + + @Override + public int getAccruedClaimBlocks() { + return this.playerStorage.getConfig().getAccruedClaimBlocks(); + } + + public boolean addAccruedClaimBlocks(int newAccruedClaimBlocks) { + int currentTotal = this.getAccruedClaimBlocks(); + if ((currentTotal + newAccruedClaimBlocks) > this.getMaxAccruedClaimBlocks()) { + return false; + } + + this.playerStorage.getConfig().setAccruedClaimBlocks(currentTotal + newAccruedClaimBlocks); + return true; + } + + public boolean setAccruedClaimBlocks(int newAccruedClaimBlocks) { + if (newAccruedClaimBlocks > this.getMaxAccruedClaimBlocks()) { + return false; + } + + this.playerStorage.getConfig().setAccruedClaimBlocks(newAccruedClaimBlocks); + return true; + } + + public int getBonusClaimBlocks() { + return this.playerStorage.getConfig().getBonusClaimBlocks(); + } + + public void setBonusClaimBlocks(int bonusClaimBlocks) { + this.playerStorage.getConfig().setBonusClaimBlocks(bonusClaimBlocks); + } + + public int getClaimCreateMode() { + if (this.optionClaimCreateMode == null) { + int mode = GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.CREATE_MODE, this).intValue(); + // default to 0 if invalid + if (mode != 0 && mode != 1) { + mode = 0; + } + this.optionClaimCreateMode = mode; + } + + return this.optionClaimCreateMode; + } + + public void setClaimCreateMode(int mode) { + // default to 0 if invalid + if (mode != 0 && mode != 1) { + mode = 0; + } + this.optionClaimCreateMode = mode; + } + + public boolean canCreateClaim(Player player) { + return canCreateClaim(player, false); + } + + public boolean canCreateClaim(Player player, boolean sendMessage) { + final int createMode = this.getClaimCreateMode(); + if (this.shovelMode == ShovelTypes.BASIC) { + if (createMode == 0 && !player.hasPermission(GDPermissions.CLAIM_CREATE_BASIC)) { + if (sendMessage) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionClaimCreate.toText()); + } + return false; + } + if (createMode == 1 && !player.hasPermission(GDPermissions.CLAIM_CUBOID_BASIC)) { + if (sendMessage) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionCuboid.toText()); + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimCuboidDisabled.toText()); + } + return false; + } + } else if (this.shovelMode == ShovelTypes.SUBDIVISION) { + if (createMode == 0 && !player.hasPermission(GDPermissions.CLAIM_CREATE_SUBDIVISION)) { + if (sendMessage) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionClaimCreate.toText()); + } + return false; + } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_SUBDIVISION)) { + if (sendMessage) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionCuboid.toText()); + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimCuboidDisabled.toText()); + } + return false; + } + } else if (this.shovelMode == ShovelTypes.ADMIN) { + if (createMode == 0 && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) { + return false; + } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_ADMIN)) { + return false; + } + } else if (this.shovelMode == ShovelTypes.TOWN) { + if (createMode == 0 && !player.hasPermission(GDPermissions.CLAIM_CREATE_TOWN)) { + return false; + } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_TOWN)) { + return false; + } + } + + return true; + } + + public void saveAllData() { + this.playerStorage.save(); + } + + public PlayerStorageData getStorageData() { + return this.playerStorage; + } + + public Set getClaims() { + return ImmutableSet.copyOf(this.claimList); + } + + public Set getInternalClaims() { + return this.claimList; + } + + public int getClaimTypeCount(ClaimType type) { + int count = 0; + for (Claim claim : this.claimList) { + if (claim.getType() == type) { + count++; + } + } + return count; + } + + public void setLastCollideEntityData(int entityId, boolean result) { + this.lastCollideEntityId = entityId; + this.lastCollideEntityResult = result; + } + + public void setLastInteractData(GDClaim claim) { + this.lastInteractResult = true; + this.lastInteractClaim = claim.getUniqueId(); + this.lastTickCounter = NMSUtil.getInstance().getRunningServerTicks(); + } + + public boolean checkLastInteraction(GDClaim claim, GDPermissionUser user) { + if (this.lastInteractResult && user != null && (NMSUtil.getInstance().getRunningServerTicks() - this.lastTickCounter) <= 2) { + if (claim.getUniqueId().equals(this.lastInteractClaim) || claim.isWilderness()) { + return true; + } + } + + return false; + } + + public void setIgnoreClaims(boolean flag) { + this.ignoreClaims = flag; + } + + @Override + public boolean canIgnoreClaim(Claim claim) { + if (claim == null || this.ignoreClaims == false) { + return false; + } + + if (claim.isAdminClaim()) { + return this.ignoreAdminClaims; + } else if (claim.isWilderness()) { + return this.ignoreWilderness; + } else if (claim.isTown()) { + return this.ignoreTowns; + } + return this.ignoreBasicClaims; + } + + public boolean canManageOption(Player player, GDClaim claim, boolean isGroup) { + if (claim.allowEdit(player) != null) { + return false; + } + + if (claim.isWilderness()) { + return player.hasPermission(GDPermissions.MANAGE_WILDERNESS); + } + if (isGroup) { + if (claim.isTown() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_GROUP_TOWN)) { + return true; + } + if (claim.isAdminClaim() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_GROUP_ADMIN)) { + return true; + } + if (claim.isBasicClaim() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_GROUP_BASIC)) { + return true; + } + if (claim.isSubdivision() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_GROUP_SUBDIVISION)) { + return true; + } + } else { + if (claim.isTown() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_PLAYER_TOWN)) { + return true; + } + if (claim.isAdminClaim() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_PLAYER_ADMIN)) { + return true; + } + if (claim.isBasicClaim() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_PLAYER_BASIC)) { + return true; + } + if (claim.isSubdivision() && player.hasPermission(GDPermissions.COMMAND_OPTIONS_PLAYER_SUBDIVISION)) { + return true; + } + } + + return false; + } + + @Override + public int getMaxAccruedClaimBlocks() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.MAX_ACCRUED_BLOCKS, this).intValue(); + } + + @Override + public double getAbandonedReturnRatio(ClaimType type) { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.ABANDON_RETURN_RATIO, this); + } + + @Override + public int getMaxClaimX(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MAX_SIZE_X, type, this).intValue(); + } + + @Override + public int getMaxClaimY(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MAX_SIZE_Y, type, this).intValue(); + } + + @Override + public int getMaxClaimZ(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MAX_SIZE_Z, type, this).intValue(); + } + + @Override + public int getMinClaimX(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MIN_SIZE_X, type, this).intValue(); + } + + @Override + public int getMinClaimY(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MIN_SIZE_Y, type, this).intValue(); + } + + @Override + public int getMinClaimZ(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.MIN_SIZE_Z, type, this).intValue(); + } + + @Override + public int getMaxClaimLevel() { + int maxClaimLevel = GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.MAX_LEVEL, this).intValue(); + if (!this.checkedDimensionHeight) { + final World world = Bukkit.getServer().getWorld(this.worldUniqueId); + if (world != null) { + final int buildHeight = world.getMaxHeight() - 1; + if (buildHeight < maxClaimLevel) { + maxClaimLevel = buildHeight; + } + } + this.checkedDimensionHeight = true; + } + return maxClaimLevel; + } + + @Override + public int getMinClaimLevel() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.MIN_LEVEL, this).intValue(); + } + + @Override + public double getEconomyClaimBlockCost() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.ECONOMY_BLOCK_COST, this); + } + + @Override + public double getEconomyClaimBlockReturn() { + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(this.getSubject(), Options.ECONOMY_BLOCK_SELL_RETURN, this); + } + + @Override + public double getTaxRate(ClaimType type) { + return GDPermissionManager.getInstance().getInternalOptionValue(this.getSubject(), Options.TAX_RATE, type, this); + } + + @Override + public String getSubjectId() { + return this.getSubject().getIdentifier(); + } + + public GDPermissionUser getSubject() { + this.playerSubject = null; + if (this.playerSubject == null || this.playerSubject.get() == null) { + GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); + this.playerSubject = new WeakReference<>(user); + } + + return this.playerSubject.get(); + } + + public void sendTaxExpireMessage(Player player, GDClaim claim) { + final double taxRate = GDPermissionManager.getInstance().getInternalOptionValue(player, Options.TAX_RATE, claim, this); + final double taxOwed = claim.getClaimBlocks() * taxRate; + final double remainingDays = GDPermissionManager.getInstance().getInternalOptionValue(player, Options.TAX_EXPIRATION_DAYS_KEEP, claim, this).intValue(); + final Component message = GriefDefenderPlugin.getInstance().messageData.taxClaimExpired + .apply(ImmutableMap.of( + "remaining_days", remainingDays, + "tax_owed", taxOwed)).build(); + GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message); + } + + public double getTotalTax() { + double totalTax = 0; + final GDPermissionUser subject = this.getSubject(); + for (Claim claim : this.getInternalClaims()) { + double playerTaxRate = GDPermissionManager.getInstance().getInternalOptionValue(subject, Options.TAX_RATE, claim, this); + totalTax += (claim.getClaimBlocks() / 256) * playerTaxRate; + } + + return totalTax; + } + + public void onDisconnect() { + this.visualBlocks = null; + this.lastInteractClaim = null; + this.claimResizing = null; + this.claimSubdividing = null; + if (this.visualRevertTask != null) { + this.visualRevertTask.cancel(); + this.visualRevertTask = null; + } + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/GDRelocator.java b/bukkit/src/main/java/com/griefdefender/GDRelocator.java new file mode 100644 index 0000000..3f1ddc5 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDRelocator.java @@ -0,0 +1,79 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.griefdefender.util.BootstrapUtil; + +import me.lucko.jarrelocator.JarRelocator; +import me.lucko.jarrelocator.Relocation; + +public class GDRelocator { + + private static GDRelocator instance; + private List rules; + + public static GDRelocator getInstance() { + if (instance == null) { + instance = new GDRelocator(); + } + return instance; + } + + public GDRelocator() { + this.rules = new ArrayList<>(); + for (String name : GDBootstrap.getInstance().getRelocateList()) { + final String[] parts = name.split(":"); + final String key = parts[0]; + final String relocated = parts[1]; + this.rules.add(new Relocation(key, "com.griefdefender.lib." + relocated)); + } + } + + public void relocateJars(Map jarMap) { + for (Map.Entry mapEntry : jarMap.entrySet()) { + final String name = mapEntry.getKey(); + final File input = mapEntry.getValue(); + final File output = Paths.get(input.getParentFile().getPath()).resolve(input.getName().replace(".jar", "") + "-shaded.jar").toFile(); + if (!output.exists()) { + // Relocate + JarRelocator relocator = new JarRelocator(input, output, this.rules); + + try { + relocator.run(); + } catch (IOException e) { + throw new RuntimeException("Unable to relocate", e); + } + } + BootstrapUtil.addUrlToClassLoader(name, output); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/GDTimings.java b/bukkit/src/main/java/com/griefdefender/GDTimings.java new file mode 100644 index 0000000..74297ba --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDTimings.java @@ -0,0 +1,79 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import co.aikar.timings.lib.MCTiming; + +public class GDTimings { + + public static final MCTiming BLOCK_BREAK_EVENT = GriefDefenderPlugin.timing("onBlockBreak"); + public static final MCTiming BLOCK_COLLIDE_EVENT = GriefDefenderPlugin.timing("onBlockCollide"); + public static final MCTiming BLOCK_NOTIFY_EVENT = GriefDefenderPlugin.timing("onBlockNotify"); + public static final MCTiming BLOCK_PLACE_EVENT = GriefDefenderPlugin.timing("onBlockPlace"); + public static final MCTiming BLOCK_POST_EVENT = GriefDefenderPlugin.timing("onBlockPost"); + public static final MCTiming BLOCK_PRE_EVENT = GriefDefenderPlugin.timing("onBlockPre"); + public static final MCTiming ENTITY_EXPLOSION_PRE_EVENT = GriefDefenderPlugin.timing("onEntityExplosionPre"); + public static final MCTiming ENTITY_EXPLOSION_DETONATE_EVENT = GriefDefenderPlugin.timing("onEntityExplosionDetonate"); + public static final MCTiming ENTITY_ATTACK_EVENT = GriefDefenderPlugin.timing("onEntityAttack"); + public static final MCTiming ENTITY_COLLIDE_EVENT = GriefDefenderPlugin.timing("onEntityCollide"); + public static final MCTiming ENTITY_DAMAGE_EVENT = GriefDefenderPlugin.timing("onEntityDamage"); + public static final MCTiming ENTITY_DAMAGE_MONITOR_EVENT = GriefDefenderPlugin.timing("onEntityDamageMonitor"); + public static final MCTiming ENTITY_DEATH_EVENT = GriefDefenderPlugin.timing("onEntityDeath"); + public static final MCTiming ENTITY_DROP_ITEM_DEATH_EVENT = GriefDefenderPlugin.timing("onEntityDropDeathItem"); + public static final MCTiming ENTITY_MOUNT_EVENT = GriefDefenderPlugin.timing("onEntityMount"); + public static final MCTiming ENTITY_MOVE_EVENT = GriefDefenderPlugin.timing("onEntityMove"); + public static final MCTiming ENTITY_SPAWN_PRE_EVENT = GriefDefenderPlugin.timing("onEntitySpawnPre"); + public static final MCTiming ENTITY_SPAWN_EVENT = GriefDefenderPlugin.timing("onEntitySpawn"); + public static final MCTiming ENTITY_TELEPORT_EVENT = GriefDefenderPlugin.timing("onEntityTeleport"); + public static final MCTiming PLAYER_CHANGE_HELD_ITEM_EVENT = GriefDefenderPlugin.timing("onPlayerChangeHeldItem"); + public static final MCTiming PLAYER_CHAT_EVENT = GriefDefenderPlugin.timing("onPlayerChat"); + public static final MCTiming PLAYER_COMMAND_EVENT = GriefDefenderPlugin.timing("onPlayerCommand"); + public static final MCTiming PLAYER_DEATH_EVENT = GriefDefenderPlugin.timing("onPlayerDeath"); + public static final MCTiming PLAYER_DISPENSE_ITEM_EVENT = GriefDefenderPlugin.timing("onPlayerDispenseItem"); + public static final MCTiming PLAYER_LOGIN_EVENT = GriefDefenderPlugin.timing("onPlayerLogin"); + public static final MCTiming PLAYER_HANDLE_SHOVEL_ACTION = GriefDefenderPlugin.timing("onPlayerHandleShovelAction"); + public static final MCTiming PLAYER_INTERACT_BLOCK_PRIMARY_EVENT = GriefDefenderPlugin.timing("onPlayerInteractBlockPrimary"); + public static final MCTiming PLAYER_INTERACT_BLOCK_SECONDARY_EVENT = GriefDefenderPlugin.timing("onPlayerInteractBlockSecondary"); + public static final MCTiming PLAYER_INTERACT_ENTITY_PRIMARY_EVENT = GriefDefenderPlugin.timing("onPlayerInteractEntityPrimary"); + public static final MCTiming PLAYER_INTERACT_ENTITY_SECONDARY_EVENT = GriefDefenderPlugin.timing("onPlayerInteractEntitySecondary"); + public static final MCTiming PLAYER_INTERACT_INVENTORY_CLICK_EVENT = GriefDefenderPlugin.timing("onPlayerInteractInventoryClick"); + public static final MCTiming PLAYER_INTERACT_INVENTORY_CLOSE_EVENT = GriefDefenderPlugin.timing("onPlayerInteractInventoryClose"); + public static final MCTiming PLAYER_INTERACT_INVENTORY_OPEN_EVENT = GriefDefenderPlugin.timing("onPlayerInteractInventoryOpen"); + public static final MCTiming PLAYER_INVESTIGATE_CLAIM = GriefDefenderPlugin.timing("onPlayerInvestigateClaim"); + public static final MCTiming PLAYER_JOIN_EVENT = GriefDefenderPlugin.timing("onPlayerJoin"); + public static final MCTiming PLAYER_KICK_EVENT = GriefDefenderPlugin.timing("onPlayerKick"); + public static final MCTiming PLAYER_PICKUP_ITEM_EVENT = GriefDefenderPlugin.timing("onPlayerPickupItem"); + public static final MCTiming PLAYER_QUIT_EVENT = GriefDefenderPlugin.timing("onPlayerQuit"); + public static final MCTiming PLAYER_RESPAWN_EVENT = GriefDefenderPlugin.timing("onPlayerRespawn"); + public static final MCTiming PLAYER_USE_ITEM_EVENT = GriefDefenderPlugin.timing("onPlayerUseItem"); + public static final MCTiming SIGN_CHANGE_EVENT = GriefDefenderPlugin.timing("onSignChange"); + public static final MCTiming PROJECTILE_IMPACT_BLOCK_EVENT = GriefDefenderPlugin.timing("onProjectileImpactBlock"); + public static final MCTiming PROJECTILE_IMPACT_ENTITY_EVENT = GriefDefenderPlugin.timing("onProjectileImpactEntity"); + public static final MCTiming EXPLOSION_EVENT = GriefDefenderPlugin.timing("onExplosion"); + public static final MCTiming CLAIM_GETCLAIM = GriefDefenderPlugin.timing("getClaimAt"); + public static final MCTiming WORLD_LOAD_EVENT = GriefDefenderPlugin.timing("onWorldSave"); + public static final MCTiming WORLD_SAVE_EVENT = GriefDefenderPlugin.timing("onWorldSave"); + public static final MCTiming WORLD_UNLOAD_EVENT = GriefDefenderPlugin.timing("onWorldSave"); +} diff --git a/bukkit/src/main/java/com/griefdefender/GDVersion.java b/bukkit/src/main/java/com/griefdefender/GDVersion.java new file mode 100644 index 0000000..14dc426 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GDVersion.java @@ -0,0 +1,44 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import com.google.inject.Singleton; +import com.griefdefender.api.Version; + +@Singleton +public class GDVersion implements Version { + + public static final double API_VERSION = 0.91; + + @Override + public double getApiVersion() { + return API_VERSION; + } + + @Override + public String getImplementationVersion() { + return GriefDefenderPlugin.IMPLEMENTATION_VERSION; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/GriefDefenderPlugin.java b/bukkit/src/main/java/com/griefdefender/GriefDefenderPlugin.java new file mode 100644 index 0000000..17b6ae9 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/GriefDefenderPlugin.java @@ -0,0 +1,931 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.PaperCommandManager; +import co.aikar.commands.RegisteredCommand; +import co.aikar.commands.RootCommand; +import co.aikar.timings.lib.MCTiming; +import co.aikar.timings.lib.TimingManager; + +import com.google.common.reflect.TypeToken; +import com.google.inject.Guice; +import com.google.inject.Stage; +import com.griefdefender.api.Tristate; +import com.griefdefender.api.claim.ClaimBlockSystem; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.permission.flag.Flag; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.claim.GDClaimManager; +import com.griefdefender.command.CommandAdjustBonusClaimBlocks; +import com.griefdefender.command.CommandCallback; +import com.griefdefender.command.CommandClaimAbandon; +import com.griefdefender.command.CommandClaimAbandonAll; +import com.griefdefender.command.CommandClaimAbandonTop; +import com.griefdefender.command.CommandClaimAdmin; +import com.griefdefender.command.CommandClaimBank; +import com.griefdefender.command.CommandClaimBasic; +import com.griefdefender.command.CommandClaimBuy; +import com.griefdefender.command.CommandClaimBuyBlocks; +import com.griefdefender.command.CommandClaimClear; +import com.griefdefender.command.CommandClaimCuboid; +import com.griefdefender.command.CommandClaimDelete; +import com.griefdefender.command.CommandClaimDeleteAll; +import com.griefdefender.command.CommandClaimDeleteAllAdmin; +import com.griefdefender.command.CommandClaimDeleteTop; +import com.griefdefender.command.CommandClaimFarewell; +import com.griefdefender.command.CommandClaimFlag; +import com.griefdefender.command.CommandClaimFlagDebug; +import com.griefdefender.command.CommandClaimFlagGroup; +import com.griefdefender.command.CommandClaimFlagPlayer; +import com.griefdefender.command.CommandClaimGreeting; +import com.griefdefender.command.CommandClaimIgnore; +import com.griefdefender.command.CommandClaimInfo; +import com.griefdefender.command.CommandClaimInherit; +import com.griefdefender.command.CommandClaimList; +import com.griefdefender.command.CommandClaimName; +import com.griefdefender.command.CommandClaimOption; +import com.griefdefender.command.CommandClaimPermissionGroup; +import com.griefdefender.command.CommandClaimPermissionPlayer; +import com.griefdefender.command.CommandClaimSchematic; +import com.griefdefender.command.CommandClaimSell; +import com.griefdefender.command.CommandClaimSellBlocks; +import com.griefdefender.command.CommandClaimSetSpawn; +import com.griefdefender.command.CommandClaimSpawn; +import com.griefdefender.command.CommandClaimSubdivide; +import com.griefdefender.command.CommandClaimTown; +import com.griefdefender.command.CommandClaimTransfer; +import com.griefdefender.command.CommandClaimWorldEdit; +import com.griefdefender.command.CommandDebug; +import com.griefdefender.command.CommandGDReload; +import com.griefdefender.command.CommandGDVersion; +import com.griefdefender.command.CommandHelp; +import com.griefdefender.command.CommandPagination; +import com.griefdefender.command.CommandPlayerInfo; +import com.griefdefender.command.CommandRestoreClaim; +import com.griefdefender.command.CommandRestoreNature; +import com.griefdefender.command.CommandSetAccruedClaimBlocks; +import com.griefdefender.command.CommandTownChat; +import com.griefdefender.command.CommandTownTag; +import com.griefdefender.command.CommandTrustGroup; +import com.griefdefender.command.CommandTrustGroupAll; +import com.griefdefender.command.CommandTrustList; +import com.griefdefender.command.CommandTrustPlayer; +import com.griefdefender.command.CommandTrustPlayerAll; +import com.griefdefender.command.CommandUntrustGroup; +import com.griefdefender.command.CommandUntrustGroupAll; +import com.griefdefender.command.CommandUntrustPlayer; +import com.griefdefender.command.CommandUntrustPlayerAll; +import com.griefdefender.configuration.ClaimTypeSerializer; +import com.griefdefender.configuration.GriefDefenderConfig; +import com.griefdefender.configuration.MessageDataConfig; +import com.griefdefender.configuration.MessageStorage; +import com.griefdefender.configuration.category.BlacklistCategory; +import com.griefdefender.configuration.type.ConfigBase; +import com.griefdefender.configuration.type.GlobalConfig; +import com.griefdefender.inject.GriefDefenderImplModule; +import com.griefdefender.internal.material.GDMaterials; +import com.griefdefender.internal.provider.WorldEditProvider; +import com.griefdefender.internal.provider.WorldGuardProvider; +import com.griefdefender.internal.registry.BlockTypeRegistryModule; +import com.griefdefender.internal.registry.EntityTypeRegistryModule; +import com.griefdefender.internal.registry.ItemTypeRegistryModule; +import com.griefdefender.internal.util.NMSUtil; +import com.griefdefender.internal.util.VecHelper; +import com.griefdefender.listener.BlockEventHandler; +import com.griefdefender.listener.BlockEventTracker; +import com.griefdefender.listener.CommandEventHandler; +import com.griefdefender.listener.EntityEventHandler; +import com.griefdefender.listener.PlayerEventHandler; +import com.griefdefender.listener.WorldEventHandler; +import com.griefdefender.permission.GDFlags; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.provider.LuckPermsProvider; +import com.griefdefender.provider.VaultProvider; +import com.griefdefender.registry.ClaimTypeRegistryModule; +import com.griefdefender.registry.FlagRegistryModule; +import com.griefdefender.registry.OptionRegistryModule; +import com.griefdefender.registry.ResultTypeRegistryModule; +import com.griefdefender.registry.ShovelTypeRegistryModule; +import com.griefdefender.registry.TrustTypeRegistryModule; +import com.griefdefender.storage.BaseStorage; +import com.griefdefender.storage.FileStorage; +import com.griefdefender.task.ClaimBlockTask; +import com.griefdefender.task.ClaimCleanupTask; +import com.griefdefender.text.ComponentConfigSerializer; +import com.griefdefender.text.TextTemplate; +import com.griefdefender.text.TextTemplateConfigSerializer; + +import me.lucko.luckperms.api.LuckPermsApi; +import me.lucko.luckperms.api.User; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.event.HoverEvent; +import net.kyori.text.format.TextColor; +import net.kyori.text.serializer.plain.PlainComponentSerializer; +import ninja.leaping.configurate.objectmapping.serialize.TypeSerializers; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.LocaleUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Monster; +import org.bukkit.event.Event; +import org.bukkit.plugin.RegisteredServiceProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +public class GriefDefenderPlugin { + + private static GriefDefenderPlugin instance; + public static final String MOD_ID = "GriefDefender"; + public static final String API_VERSION = GriefDefenderPlugin.class.getPackage().getSpecificationVersion(); + public static final String IMPLEMENTATION_NAME = GriefDefenderPlugin.class.getPackage().getImplementationTitle(); + public static final String IMPLEMENTATION_VERSION = GriefDefenderPlugin.class.getPackage().getImplementationVersion(); + public static String VERSION = "1.0.0"; + private Path configPath = Paths.get(".", "plugins", "GriefDefender"); + public MessageStorage messageStorage; + public MessageDataConfig messageData; + public Map worldGeneratorRandoms = new HashMap<>(); + public static ClaimBlockSystem CLAIM_BLOCK_SYSTEM; + + public static final String CONFIG_HEADER = "1.0.0\n" + + "# If you need help with the configuration or have any issues related to GriefDefender,\n" + + "# create a ticket on https://github.com/bloodmc/GriefDefender/issues.\n" + + "# Note: If you have not purchased GriefDefender, please consider doing so to get \n" + + "# exclusive access to Discord for prompt support.\n"; + + // GP Public user info + public static final UUID PUBLIC_UUID = UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77"); + public static final UUID WORLD_USER_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000"); + public static final UUID ADMIN_USER_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111"); + public static GDPermissionUser PUBLIC_USER; + public static GDPermissionUser WORLD_USER; + public static final String PUBLIC_NAME = "[GDPublic]"; + public static final String WORLD_USER_NAME = "[GDWorld]"; + + public static GDPermissionHolder DEFAULT_HOLDER; + private PaperCommandManager commandManager; + private static TimingManager timingManager; + + public BaseStorage dataStore; + + private LuckPermsProvider luckPermsProvider; + private WorldEditProvider worldEditProvider; + private WorldGuardProvider worldGuardProvider; + private VaultProvider vaultProvider; + + public Executor executor; + + private boolean isEconomyModeEnabled = false; + + public Material modificationTool; + public Material investigationTool; + public int maxInspectionDistance = 100; + + public static boolean debugLogging = false; + public static boolean debugActive = false; + private Map debugUserMap = new HashMap<>(); + public static final Component GD_TEXT = TextComponent.builder("").append("[").append("GD", TextColor.AQUA).append("] ").build(); + public static final List ID_MAP = new ArrayList<>(); + public static List helpComponents = new ArrayList<>(); + + public static GriefDefenderPlugin getInstance() { + if (instance == null) { + instance = new GriefDefenderPlugin(); + } + return instance; + } + + public Path getConfigPath() { + return this.configPath; + } + + public static void addEventLogEntry(Event event, Location location, String sourceId, String targetId, GDPermissionHolder permissionSubject, String permission, String trust, Tristate result) { + final String eventName = event.getClass().getSimpleName().replace('$', '.').replace(".Impl", ""); + final String eventLocation = location == null ? "none" : VecHelper.toVector3i(location).toString(); + for (GDDebugData debugEntry : GriefDefenderPlugin.getInstance().getDebugUserMap().values()) { + final CommandSender debugSource = debugEntry.getSource(); + final OfflinePlayer debugUser = debugEntry.getTarget(); + if (debugUser != null) { + if (permissionSubject == null) { + continue; + } + // Check event source user + if (!permissionSubject.getFriendlyName().equals(debugUser.getName())) { + continue; + } + } + + String messageUser = permissionSubject.getFriendlyName(); + if (permissionSubject instanceof User) { + messageUser = ((User) permissionSubject).getName(); + } + + // record + if (debugEntry.isRecording()) { + permission = permission.replace("griefdefender.flag.", ""); + String messageFlag = permission; + final Flag flag = FlagRegistryModule.getInstance().getById(permission).orElse(null); + if (flag != null) { + messageFlag = flag.toString(); + } + String messageSource = sourceId == null ? "none" : sourceId; + String messageTarget = targetId == null ? "none" : targetId; + if (messageTarget.endsWith(".0")) { + messageTarget = messageTarget.substring(0, messageTarget.length() - 2); + } + if (trust == null) { + trust = "none"; + } + // Strip minecraft id on bukkit + String[] parts = messageSource.split(":"); + if (parts.length > 1) { + messageSource = parts[1]; + } + parts = messageTarget.split(":"); + if (parts.length > 1) { + messageTarget = parts[1]; + } + debugEntry.addRecord(messageFlag, trust, messageSource, messageTarget, eventLocation, messageUser, permission, result); + continue; + } + + final Component textEvent = TextComponent.builder("") + .append(GD_TEXT) + .append("Event: ", TextColor.GRAY) + .append(eventName == null ? TextComponent.of("Plugin").color(TextColor.GRAY) : TextComponent.of(eventName).color(TextColor.GRAY)) + .append("\n").build(); + final Component textCause = TextComponent.builder("") + .append(GD_TEXT) + .append("Cause: ", TextColor.GRAY) + .append(sourceId, TextColor.LIGHT_PURPLE) + .append("\n").build(); + final Component textLocation = TextComponent.builder("") + .append(GD_TEXT) + .append("Location: ", TextColor.GRAY) + .append(eventLocation == null ? "NONE" : eventLocation).build(); + final Component textUser = TextComponent.builder("") + .append("User: ", TextColor.GRAY) + .append(messageUser, TextColor.GOLD) + .append("\n").build(); + final Component textLocationAndUser = TextComponent.builder("") + .append(textLocation) + .append(" ") + .append(textUser).build(); + Component textContext = null; + Component textPermission = null; + if (targetId != null) { + textContext = TextComponent.builder("") + .append(GD_TEXT) + .append("Target: ", TextColor.GRAY) + .append(GDPermissionManager.getInstance().getPermissionIdentifier(targetId), TextColor.YELLOW) + .append("\n").build(); + } + if (permission != null) { + textPermission = TextComponent.builder("") + .append(GD_TEXT) + .append("Permission: ", TextColor.GRAY) + .append(permission, TextColor.RED) + .append("\n").build(); + } + TextComponent.Builder textBuilder = TextComponent.builder("").append(textEvent); + if (textContext != null) { + textBuilder.append(textContext); + } else { + textBuilder.append(textCause); + } + if (textPermission != null) { + textBuilder.append(textPermission); + } + textBuilder.append(textLocationAndUser); + TextAdapter.sendComponent(debugSource, textBuilder.build()); + } + } + + public void onEnable() { + this.getLogger().info("GriefDefender boot start."); + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPermsApi.class); + if (provider == null) { + this.getLogger().severe("Unable to initialize plugin. GriefDefender requires LuckPerms to be installed." + + "\nSee https://github.com/lucko/LuckPerms/wiki/Migration on how to migrate from other permission plugins."); + return; + } + + this.luckPermsProvider = new LuckPermsProvider(); + instance = this; + timingManager = TimingManager.of(GDBootstrap.getInstance()); + DEFAULT_HOLDER = new GDPermissionHolder(this.luckPermsProvider.getApi().getGroup("default")); + PUBLIC_USER = new GDPermissionUser(PUBLIC_UUID, PUBLIC_NAME); + WORLD_USER = new GDPermissionUser(WORLD_USER_UUID, WORLD_USER_NAME); + Guice.createInjector(Stage.PRODUCTION, new GriefDefenderImplModule()); + ClaimTypeRegistryModule.getInstance().registerDefaults(); + ShovelTypeRegistryModule.getInstance().registerDefaults(); + TrustTypeRegistryModule.getInstance().registerDefaults(); + FlagRegistryModule.getInstance().registerDefaults(); + OptionRegistryModule.getInstance().registerDefaults(); + ResultTypeRegistryModule.getInstance().registerDefaults(); + EntityTypeRegistryModule.getInstance().registerDefaults(); + BlockTypeRegistryModule.getInstance().registerDefaults(); + ItemTypeRegistryModule.getInstance().registerDefaults(); + + this.loadConfig(); + + this.executor = Executors.newFixedThreadPool(GriefDefenderPlugin.getGlobalConfig().getConfig().thread.numExecutorThreads); + + if (Bukkit.getPluginManager().getPlugin("Vault") != null) { + this.vaultProvider = new VaultProvider(); + this.getLogger().info("Detected Vault. Checking for compatible economy plugin..."); + if (this.vaultProvider.getApi() != null) { + this.getLogger().info("Found economy plugin '" + this.vaultProvider.getApi().getName() + "'. GriefDefender economy integration is now enabled."); + } else { + this.getLogger().info("Could not locate a compatible economy plugin for Vault. Please check with your server administrator."); + } + if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + this.getLogger().info("Economy mode enabled!. Claimblocks will be disabled..."); + } + } else if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + this.getLogger().severe("No economy plugin found! Unable to initialize economy plugin."); + return; + } + + if (Bukkit.getPluginManager().getPlugin("WorldEdit") != null || Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null) { + this.worldEditProvider = new WorldEditProvider(); + } + + if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { + this.worldGuardProvider = new WorldGuardProvider(); + } + + if (this.dataStore == null) { + try { + this.dataStore = new FileStorage(); + this.dataStore.initialize(); + } catch (Exception e) { + this.getLogger().info("Unable to initialize file storage."); + this.getLogger().info(e.getMessage()); + e.printStackTrace(); + return; + } + } + + //this.registerBaseCommands(); + Bukkit.getPluginManager().registerEvents(new BlockEventHandler(dataStore), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new BlockEventTracker(), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new CommandEventHandler(dataStore), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new PlayerEventHandler(dataStore), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new EntityEventHandler(dataStore), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new WorldEventHandler(), GDBootstrap.getInstance()); + Bukkit.getPluginManager().registerEvents(new NMSUtil(), GDBootstrap.getInstance()); + + /*PUBLIC_USER = Sponge.getServiceManager().provide(UserStorageService.class).get() + .getOrCreate(GameProfile.of(GriefDefenderPlugin.PUBLIC_UUID, GriefDefenderPlugin.PUBLIC_NAME)); + WORLD_USER = Sponge.getServiceManager().provide(UserStorageService.class).get() + .getOrCreate(GameProfile.of(GriefDefenderPlugin.WORLD_USER_UUID, GriefDefenderPlugin.WORLD_USER_NAME));*/ + + // run cleanup task + int cleanupTaskInterval = GriefDefenderPlugin.getGlobalConfig().getConfig().claim.expirationCleanupInterval; + if (cleanupTaskInterval > 0) { + new ClaimCleanupTask(cleanupTaskInterval); + } + + + /*if (this.permissionService == null) { + this.getLogger().severe("Unable to initialize plugin. GriefDefender requires a permissions plugin such as LuckPerms."); + return; + }*/ + + final boolean resetMigration = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations; + final boolean resetClaimData = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks; + final int migration2dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate; + final int migration3dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate; + boolean migrate = false; + if (resetMigration || resetClaimData || (migration2dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA) + || (migration3dRate > -1 && GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME)) { + migrate = true; + } + + if (migrate) { + List playerDataList = new ArrayList<>(); + if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { + final GDClaimManager claimWorldManager = this.dataStore.getClaimWorldManager(Bukkit.getServer().getWorlds().get(0).getUID()); + claimWorldManager.resetPlayerData(); + playerDataList = new ArrayList<>(claimWorldManager.getPlayerDataMap().values()); + for (GDPlayerData playerData : playerDataList) { + if (Bukkit.getServer().getPlayer(playerData.playerID) != null && playerData.getClaims().isEmpty()) { + playerData.onDisconnect(); + claimWorldManager.removePlayer(playerData.playerID); + } + } + } + if (!BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { + for (World world : Bukkit.getServer().getWorlds()) { + final GDClaimManager claimWorldManager = this.dataStore.getClaimWorldManager(world.getUID()); + playerDataList = new ArrayList<>(claimWorldManager.getPlayerDataMap().values()); + for (GDPlayerData playerData : playerDataList) { + if (Bukkit.getServer().getPlayer(playerData.playerID) != null && playerData.getClaims().isEmpty()) { + playerData.onDisconnect(); + claimWorldManager.removePlayer(playerData.playerID); + } + } + } + } + GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations = false; + GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks = false; + GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate = -1; + GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate = -1; + GriefDefenderPlugin.getGlobalConfig().save(); + } + + new ClaimBlockTask(); + registerBaseCommands(); + this.getLogger().info("Loaded successfully."); + } + + public void onDisable() { + // Spigot disables plugins before calling world save on shutdown so we need to manually save here + for (World world : Bukkit.getServer().getWorlds()) { + if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUID())) { + continue; + } + + GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUID()); + if (claimWorldManager == null) { + continue; + } + + claimWorldManager.save(); + claimWorldManager.playerIndexStorage.savePlayerDatData(); + } + } + + public void registerBaseCommands() { + PaperCommandManager manager = new PaperCommandManager(GDBootstrap.getInstance()); + this.commandManager = manager; + manager.getCommandReplacements().addReplacement("griefdefender", "gd|griefdefender"); + manager.registerCommand(new CommandAdjustBonusClaimBlocks()); + manager.registerCommand(new CommandCallback()); + manager.registerCommand(new CommandClaimAbandon()); + manager.registerCommand(new CommandClaimAbandonAll()); + manager.registerCommand(new CommandClaimAbandonTop()); + manager.registerCommand(new CommandClaimAdmin()); + manager.registerCommand(new CommandClaimBank()); + manager.registerCommand(new CommandClaimBasic()); + manager.registerCommand(new CommandClaimBuy()); + manager.registerCommand(new CommandClaimBuyBlocks()); + manager.registerCommand(new CommandClaimClear()); + manager.registerCommand(new CommandClaimCuboid()); + manager.registerCommand(new CommandClaimDelete()); + manager.registerCommand(new CommandClaimDeleteAll()); + manager.registerCommand(new CommandClaimDeleteAllAdmin()); + manager.registerCommand(new CommandClaimDeleteTop()); + manager.registerCommand(new CommandClaimFarewell()); + manager.registerCommand(new CommandClaimFlag()); + manager.registerCommand(new CommandClaimFlagDebug()); + manager.registerCommand(new CommandClaimFlagGroup()); + manager.registerCommand(new CommandClaimFlagPlayer()); + manager.registerCommand(new CommandClaimGreeting()); + manager.registerCommand(new CommandClaimIgnore()); + manager.registerCommand(new CommandClaimInfo()); + manager.registerCommand(new CommandClaimInherit()); + manager.registerCommand(new CommandClaimList()); + manager.registerCommand(new CommandClaimName()); + manager.registerCommand(new CommandClaimOption()); + manager.registerCommand(new CommandClaimPermissionGroup()); + manager.registerCommand(new CommandClaimPermissionPlayer()); + manager.registerCommand(new CommandClaimSchematic()); + manager.registerCommand(new CommandClaimSell()); + manager.registerCommand(new CommandClaimSellBlocks()); + manager.registerCommand(new CommandClaimSetSpawn()); + manager.registerCommand(new CommandClaimSpawn()); + manager.registerCommand(new CommandClaimSubdivide()); + manager.registerCommand(new CommandClaimTown()); + manager.registerCommand(new CommandClaimTransfer()); + manager.registerCommand(new CommandClaimWorldEdit()); + manager.registerCommand(new CommandDebug()); + manager.registerCommand(new CommandGDReload()); + manager.registerCommand(new CommandGDVersion()); + manager.registerCommand(new CommandHelp()); + manager.registerCommand(new CommandPagination()); + manager.registerCommand(new CommandPlayerInfo()); + manager.registerCommand(new CommandRestoreClaim()); + manager.registerCommand(new CommandRestoreNature()); + manager.registerCommand(new CommandSetAccruedClaimBlocks()); + manager.registerCommand(new CommandTownChat()); + manager.registerCommand(new CommandTownTag()); + manager.registerCommand(new CommandTrustGroup()); + manager.registerCommand(new CommandTrustPlayer()); + manager.registerCommand(new CommandTrustGroupAll()); + manager.registerCommand(new CommandTrustPlayerAll()); + manager.registerCommand(new CommandUntrustGroup()); + manager.registerCommand(new CommandUntrustPlayer()); + manager.registerCommand(new CommandUntrustGroupAll()); + manager.registerCommand(new CommandUntrustPlayerAll()); + manager.registerCommand(new CommandTrustList()); + manager.enableUnstableAPI("help"); + + final Map helpMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + // Generate help text + RootCommand rootCommand = getCommandManager().getRootCommand("gd"); + for (BaseCommand child : rootCommand.getChildren()) { + for (RegisteredCommand registeredCommand : child.getRegisteredCommands()) { + if (helpMap.get(registeredCommand.getPrefSubCommand()) != null) { + continue; + } + TextComponent permissionText = TextComponent.builder("") + .append("Permission: ", TextColor.GOLD) + .append(registeredCommand.getRequiredPermissions() == null ? "None" : String.join(",", registeredCommand.getRequiredPermissions()), TextColor.GRAY) + .build(); + + TextComponent argumentsText = TextComponent.builder("") + //.append("Arguments: ", TextColor.AQUA) + .append(registeredCommand.getSyntaxText() == null ? "Arguments: None" : registeredCommand.getSyntaxText(), TextColor.GREEN) + .build(); + + final TextComponent hoverText = TextComponent.builder("") + .append("Command: ", TextColor.AQUA) + .append(registeredCommand.getPrefSubCommand() + "\n", TextColor.GREEN) + .append("Description: ", TextColor.AQUA) + .append(registeredCommand.getHelpText() + "\n", TextColor.GREEN) + .append("Arguments: ", TextColor.AQUA) + .append(argumentsText) + .append("\n") + .append(permissionText) + .build(); + + final TextComponent commandText = TextComponent.builder("") + .append("/gd " + registeredCommand.getPrefSubCommand(), TextColor.GREEN) + .hoverEvent(HoverEvent.showText(hoverText)) + .clickEvent(ClickEvent.suggestCommand("/gd " + registeredCommand.getPrefSubCommand())) + .build(); + helpMap.put(registeredCommand.getPrefSubCommand(), commandText); + } + } + helpComponents = new ArrayList<>(helpMap.values()); + + NMSUtil.getInstance().populateTabComplete(); + ID_MAP.add("any"); + ID_MAP.add("ambient"); + ID_MAP.add("animal"); + ID_MAP.add("aquatic"); + ID_MAP.add("monster"); + + // commands + ID_MAP.add("griefdefender:cf"); + ID_MAP.add("griefdefender:cfg"); + ID_MAP.add("griefdefender:cfp"); + ID_MAP.add("griefdefender:cog"); + ID_MAP.add("griefdefender:cop"); + ID_MAP.add("griefdefender:cpg"); + ID_MAP.add("griefdefender:cpp"); + ID_MAP.add("griefdefender:claimflag"); + ID_MAP.add("griefdefender:claimflaggroup"); + ID_MAP.add("griefdefender:claimflagplayer"); + //ID_MAP.add("unknown"); + } + + public PaperCommandManager getCommandManager() { + return this.commandManager; + } + + public void loadConfig() { + this.getLogger().info("Loading configuration..."); + try { + TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(Component.class), new ComponentConfigSerializer()); + TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(TextTemplate.class), new TextTemplateConfigSerializer()); + TypeSerializers.getDefaultSerializers().registerType(TypeToken.of(ClaimType.class), new ClaimTypeSerializer()); + + if (Files.notExists(BaseStorage.dataLayerFolderPath)) { + Files.createDirectories(BaseStorage.dataLayerFolderPath); + } + + Path rootConfigPath = this.getConfigPath().resolve("worlds"); + BaseStorage.globalConfig = new GriefDefenderConfig<>(GlobalConfig.class, this.getConfigPath().resolve("global.conf"), null); + BaseStorage.globalConfig.getConfig().permissionCategory.refreshFlags(); + String localeString = BaseStorage.globalConfig.getConfig().message.locale; + try { + LocaleUtils.toLocale(localeString); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + this.getLogger().severe("Could not validate the locale '" + localeString + "'. Defaulting to 'en_US'..."); + localeString = "en_US"; + } + final Path localePath = this.getConfigPath().resolve("lang").resolve(localeString + ".conf"); + if (!localePath.toFile().exists()) { + // Check for a default locale asset and copy to lang folder + try { + final InputStream in = getClass().getResourceAsStream("/assets/lang/" + localeString + ".conf"); + FileUtils.copyInputStreamToFile(in, localePath.toFile()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + messageStorage = new MessageStorage(localePath); + messageData = messageStorage.getConfig(); + BaseStorage.USE_GLOBAL_PLAYER_STORAGE = BaseStorage.globalConfig.getConfig().playerdata.useGlobalPlayerDataStorage; + GDFlags.populateFlagStatus(); + PermissionHolderCache.getInstance().getOrCreatePermissionCache(GriefDefenderPlugin.DEFAULT_HOLDER).invalidateAll(); + CLAIM_BLOCK_SYSTEM = BaseStorage.globalConfig.getConfig().playerdata.claimBlockSystem; + final Material modTool = Material.getMaterial(BaseStorage.globalConfig.getConfig().claim.modificationTool); + final Material invTool = Material.getMaterial(BaseStorage.globalConfig.getConfig().claim.investigationTool); + this.modificationTool = modTool == null ? GDMaterials.GOLDEN_SHOVEL : modTool; + this.investigationTool = invTool == null? Material.STICK : invTool; + this.maxInspectionDistance = BaseStorage.globalConfig.getConfig().general.maxClaimInspectionDistance; + if (this.dataStore != null) { + for (World world : Bukkit.getServer().getWorlds()) { + final String dimType = world.getEnvironment().name().toLowerCase(); + final Path dimPath = rootConfigPath.resolve(dimType); + if (Files.notExists(dimPath.resolve(world.getName()))) { + try { + Files.createDirectories(rootConfigPath.resolve(dimType).resolve(world.getName())); + } catch (IOException e) { + e.printStackTrace(); + } + } + + GriefDefenderConfig dimConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve("dimension.conf"), BaseStorage.globalConfig); + GriefDefenderConfig worldConfig = new GriefDefenderConfig<>(ConfigBase.class, dimPath.resolve(world.getName()).resolve("world.conf"), dimConfig); + + BaseStorage.dimensionConfigMap.put(world.getUID(), dimConfig); + BaseStorage.worldConfigMap.put(world.getUID(), worldConfig); + + // refresh player data + final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUID()); + for (GDPlayerData playerData : claimManager.getPlayerDataMap().values()) { + if (playerData.playerID.equals(WORLD_USER_UUID) || playerData.playerID.equals(ADMIN_USER_UUID) || playerData.playerID.equals(PUBLIC_UUID)) { + continue; + } + playerData.refreshPlayerOptions(); + } + + if (GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.classicMigrator) { + GriefDefenderPlugin.getGlobalConfig().getConfig().migrator.classicMigrator = false; + GriefDefenderPlugin.getGlobalConfig().save(); + } + if (this.worldEditProvider != null) { + this.getLogger().info("Loading schematics for world " + world.getName() + "..."); + this.worldEditProvider.loadSchematics(world); + } + } + // refresh default permissions + this.dataStore.setDefaultGlobalPermissions(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void sendClaimDenyMessage(GDClaim claim, CommandSender source, Component message) { + if (claim.getData() != null && !claim.getData().allowDenyMessages()) { + return; + } + + sendMessage(source, message); + } + + public static void sendMessage(CommandSender src, String messageKey, TextTemplate template, Map params) { + Component message = null; + //try { + message = template.apply(params).build(); + /*} catch (TextTemplateArgumentException e) { + // fix message data + GriefDefenderPlugin.getInstance().messageStorage.resetMessageData(messageKey); + }*/ + if (message != null) { + sendMessage(src, message); + } + } + + public static void sendMessage(CommandSender source, Component message) { + if (message == TextComponent.empty() || message == null) { + return; + } + + if (source == null) { + GriefDefenderPlugin.getInstance().getLogger().warning(PlainComponentSerializer.INSTANCE.serialize(message)); + } else { + TextAdapter.sendComponent(source, message); + } + } + + public static GriefDefenderConfig getActiveConfig(World world) { + return getActiveConfig(world.getUID()); + } + + public static GriefDefenderConfig getActiveConfig(UUID worldUniqueId) { + GriefDefenderConfig config = BaseStorage.worldConfigMap.get(worldUniqueId); + if (config != null) { + return config; + } + + config = BaseStorage.dimensionConfigMap.get(worldUniqueId); + if (config != null) { + return config; + } + + return BaseStorage.globalConfig; + } + + public static GriefDefenderConfig getGlobalConfig() { + return BaseStorage.globalConfig; + } + + public boolean claimsEnabledForWorld(UUID worldUniqueId) { + return GriefDefenderPlugin.getActiveConfig(worldUniqueId).getConfig().claim.claimsEnabled != 0; + } + + public int getSeaLevel(World world) { + return world.getSeaLevel(); + } + + public Map getDebugUserMap() { + return this.debugUserMap; + } + + public static boolean isEntityProtected(Entity entity) { + // ignore monsters + if (entity instanceof Monster) { + return false; + } + + return true; + } + + public static GDPermissionUser getOrCreateUser(UUID uuid) { + if (uuid == null) { + return null; + } + + if (uuid == PUBLIC_UUID) { + return PUBLIC_USER; + } + if (uuid == WORLD_USER_UUID) { + return WORLD_USER; + } + + // check the cache + return PermissionHolderCache.getInstance().getOrCreateUser(uuid); + } + + public static boolean isSourceIdBlacklisted(String flag, Object source, UUID worldUniqueId) { + final List flagList = GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.flagIdBlacklist.get(flag); + final boolean checkFlag = flagList != null && !flagList.isEmpty(); + final boolean checkGlobal = !GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.globalSourceBlacklist.isEmpty(); + if (!checkFlag && !checkGlobal) { + return false; + } + + final GriefDefenderConfig activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId); + final String id = GDPermissionManager.getInstance().getPermissionIdentifier(source); + final String idNoMeta = GDPermissionManager.getInstance().getIdentifierWithoutMeta(id); + + // Check global + if (checkGlobal) { + final BlacklistCategory blacklistCategory = activeConfig.getConfig().blacklist; + final List globalSourceBlacklist = blacklistCategory.getGlobalSourceBlacklist(); + if (globalSourceBlacklist == null) { + return false; + } + for (String str : globalSourceBlacklist) { + if (FilenameUtils.wildcardMatch(id, str)) { + return true; + } + if (FilenameUtils.wildcardMatch(idNoMeta, str)) { + return true; + } + } + } + // Check flag + if (checkFlag) { + for (String str : flagList) { + if (FilenameUtils.wildcardMatch(id, str)) { + return true; + } + if (FilenameUtils.wildcardMatch(idNoMeta, str)) { + return true; + } + } + } + + return false; + } + + public static boolean isTargetIdBlacklisted(String flag, Object target, UUID worldUniqueId) { + final List flagList = GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.flagIdBlacklist.get(flag); + final boolean checkFlag = flagList != null && !flagList.isEmpty(); + final boolean checkGlobal = !GriefDefenderPlugin.getGlobalConfig().getConfig().blacklist.globalTargetBlacklist.isEmpty(); + if (!checkFlag && !checkGlobal) { + return false; + } + + final GriefDefenderConfig activeConfig = GriefDefenderPlugin.getActiveConfig(worldUniqueId); + final String id = GDPermissionManager.getInstance().getPermissionIdentifier(target); + final String idNoMeta = GDPermissionManager.getInstance().getIdentifierWithoutMeta(id); + + // Check global + if (checkGlobal) { + final BlacklistCategory blacklistCategory = activeConfig.getConfig().blacklist; + final List globalTargetBlacklist = blacklistCategory.getGlobalTargetBlacklist(); + if (globalTargetBlacklist == null) { + return false; + } + for (String str : globalTargetBlacklist) { + if (FilenameUtils.wildcardMatch(id, str)) { + return true; + } + if (FilenameUtils.wildcardMatch(idNoMeta, str)) { + return true; + } + } + } + // Check flag + if (checkFlag) { + for (String str : flagList) { + if (FilenameUtils.wildcardMatch(id, str)) { + return true; + } + if (FilenameUtils.wildcardMatch(idNoMeta, str)) { + return true; + } + } + } + + return false; + } + + public boolean isEconomyModeEnabled() { + return this.isEconomyModeEnabled; + } + + public LuckPermsProvider getLuckPermsProvider() { + return this.luckPermsProvider; + } + + public WorldEditProvider getWorldEditProvider() { + return this.worldEditProvider; + } + + public WorldGuardProvider getWorldGuardProvider() { + return this.worldGuardProvider; + } + + public VaultProvider getVaultProvider() { + return this.vaultProvider; + } + + public Logger getLogger() { + return GDBootstrap.getInstance().getLogger(); + } + + public static MCTiming timing(String name) { + return timingManager.of(name); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/cache/PermissionHolderCache.java b/bukkit/src/main/java/com/griefdefender/cache/PermissionHolderCache.java new file mode 100644 index 0000000..0f9fb02 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/cache/PermissionHolderCache.java @@ -0,0 +1,181 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.cache; + +import com.github.benmanes.caffeine.cache.AsyncCache; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.griefdefender.api.Tristate; +import com.griefdefender.permission.GDPermissionGroup; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.util.PermissionUtil; +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.User; +import org.bukkit.OfflinePlayer; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +public class PermissionHolderCache { + + private static PermissionHolderCache instance; + private final AsyncCache holderCache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES) + .buildAsync(); + private final ConcurrentHashMap> permissionCache = new ConcurrentHashMap<>(); + + public GDPermissionUser getOrCreateUser(OfflinePlayer player) { + if (player == null) { + return null; + } + + return getOrCreateUser(player.getUniqueId()); + } + + public GDPermissionUser getOrCreateUser(UUID uuid) { + GDPermissionUser user = null; + CompletableFuture future = holderCache.getIfPresent(uuid.toString()); + if (future != null) { + try { + user = (GDPermissionUser) future.get(); + if (user != null) { + return user; + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + user = new GDPermissionUser(uuid); + final GDPermissionHolder holder = user; + future = CompletableFuture.supplyAsync(() -> holder); + holderCache.put(user.getIdentifier(), future); + return user; + } + + public GDPermissionUser getOrCreateUser(String username) { + final User luckPermsUser = PermissionUtil.getInstance().getUserSubject(username); + if (luckPermsUser != null) { + return this.getOrCreateUser(luckPermsUser.getUuid()); + } + return null; + } + + public GDPermissionGroup getOrCreateGroup(String groupName) { + GDPermissionGroup permissionHolder = null; + CompletableFuture future = holderCache.getIfPresent(groupName); + if (future != null) { + try { + permissionHolder = (GDPermissionGroup) future.get(); + if (permissionHolder != null) { + return permissionHolder; + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + final Group luckPermsGroup = PermissionUtil.getInstance().getGroupSubject(groupName); + if (luckPermsGroup == null) { + return null; + } + + permissionHolder = new GDPermissionGroup(luckPermsGroup); + final GDPermissionGroup holder = permissionHolder; + future = CompletableFuture.supplyAsync(() -> holder); + holderCache.put(groupName, future); + return holder; + } + + public GDPermissionGroup getOrCreateGroup(Group group) { + GDPermissionGroup permissionHolder = null; + CompletableFuture future = holderCache.getIfPresent(group.getName()); + if (future != null) { + try { + permissionHolder = (GDPermissionGroup) future.get(); + if (permissionHolder != null) { + return permissionHolder; + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + permissionHolder = new GDPermissionGroup(group); + final GDPermissionGroup holder = permissionHolder; + future = CompletableFuture.supplyAsync(() -> holder); + holderCache.put(group.getName(), future); + return holder; + } + + public GDPermissionHolder getOrCreateHolder(String identifier) { + GDPermissionHolder permissionHolder = null; + CompletableFuture future = holderCache.getIfPresent(identifier); + if (future != null) { + try { + permissionHolder = future.get(); + if (permissionHolder != null) { + return permissionHolder; + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + permissionHolder = new GDPermissionHolder(identifier); + final GDPermissionHolder holder = permissionHolder; + future = CompletableFuture.supplyAsync(() -> holder); + holderCache.put(identifier, future); + return holder; + } + + public Cache getOrCreatePermissionCache(GDPermissionHolder holder) { + Cache cache = this.permissionCache.get(holder); + if (cache == null) { + cache = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build(); + this.permissionCache.put(holder, cache); + } + return cache; + } + + public void invalidateAllPermissionCache() { + for (Cache cache : this.permissionCache.values()) { + cache.invalidateAll(); + } + } + + static { + instance = new PermissionHolderCache(); + } + + public static PermissionHolderCache getInstance() { + return instance; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java b/bukkit/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java new file mode 100644 index 0000000..d76c610 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/ClaimContextCalculator.java @@ -0,0 +1,87 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import me.lucko.luckperms.api.PermissionHolder; +import me.lucko.luckperms.api.context.ContextCalculator; + +public abstract class ClaimContextCalculator implements ContextCalculator { + + /*@Override + public void accumulateContexts(PermissionHolder calculable, Set accumulator) { + if (calculable.getCommandSource().isPresent() && calculable.getCommandSource().get() instanceof Player) { + Player player = (Player) calculable.getCommandSource().get(); + GPPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(player.getWorld(), player.getUniqueId()); + if (playerData == null) { + return; + } + if (playerData.ignoreActiveContexts) { + playerData.ignoreActiveContexts = false; + return; + } + + GPClaim sourceClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + if (sourceClaim != null) { + if (playerData == null || playerData.canIgnoreClaim(sourceClaim)) { + return; + } + + if (sourceClaim.parent != null && sourceClaim.getData().doesInheritParent()) { + accumulator.add(sourceClaim.parent.getContext()); + } else { + accumulator.add(sourceClaim.getContext()); + } + } + } + + } + + @Override + public boolean matches(Context context, Subject subject) { + if (context.equals("gd_claim")) { + if (subject.getCommandSource().isPresent() && subject.getCommandSource().get() instanceof Player) { + Player player = (Player) subject.getCommandSource().get(); + GPPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(player.getWorld(), player.getUniqueId()); + if (playerData == null) { + return false; + } + + GPClaim playerClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + if (playerClaim != null && playerClaim.id.equals(UUID.fromString(context.getValue()))) { + return true; + } + } + } + + return false; + } + + @Override + public @NonNull MutableContextSet giveApplicableContext(@NonNull PermissionHolder arg0, + @NonNull MutableContextSet arg1) { + // TODO Auto-generated method stub + return null; + }*/ +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDClaim.java b/bukkit/src/main/java/com/griefdefender/claim/GDClaim.java new file mode 100644 index 0000000..db5bc55 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDClaim.java @@ -0,0 +1,2984 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.flowpowered.math.vector.Vector3i; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.Tristate; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimBlockSystem; +import com.griefdefender.api.claim.ClaimContexts; +import com.griefdefender.api.claim.ClaimManager; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.api.claim.ClaimResultType; +import com.griefdefender.api.claim.ClaimSchematic; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.api.claim.ShovelTypes; +import com.griefdefender.api.claim.TrustType; +import com.griefdefender.api.claim.TrustTypes; +import com.griefdefender.api.data.ClaimData; +import com.griefdefender.api.event.EventCause; +import com.griefdefender.api.permission.Context; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.configuration.ClaimDataConfig; +import com.griefdefender.configuration.ClaimStorageData; +import com.griefdefender.configuration.IClaimData; +import com.griefdefender.configuration.MessageStorage; +import com.griefdefender.configuration.TownDataConfig; +import com.griefdefender.configuration.TownStorageData; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDChangeClaimEvent; +import com.griefdefender.event.GDCreateClaimEvent; +import com.griefdefender.event.GDDeleteClaimEvent; +import com.griefdefender.event.GDGroupTrustClaimEvent; +import com.griefdefender.event.GDSaveClaimEvent; +import com.griefdefender.event.GDTransferClaimEvent; +import com.griefdefender.event.GDUserTrustClaimEvent; +import com.griefdefender.internal.provider.WorldGuardProvider; +import com.griefdefender.internal.util.BlockUtil; +import com.griefdefender.internal.util.VecHelper; +import com.griefdefender.internal.visual.ClaimVisual; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.provider.VaultProvider; +import com.griefdefender.registry.TrustTypeRegistryModule; +import com.griefdefender.storage.BaseStorage; +import com.griefdefender.util.PermissionUtil; +import com.griefdefender.visual.ClaimVisualType; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nullable; + +public class GDClaim implements Claim { + + public static final BaseStorage DATASTORE = GriefDefenderPlugin.getInstance().dataStore; + // Note: 2D cuboids will ignore the upper Y value while 3D cuboids do not + public Vector3i lesserBoundaryCorner; + public Vector3i greaterBoundaryCorner; + private World world; + private ClaimType type = ClaimTypes.BASIC; + private Set chunkHashes; + private final int hashCode; + private final GDClaimManager worldClaimManager; + private final Claim wildernessClaim; + private final VaultProvider vaultProvider = GriefDefenderPlugin.getInstance().getVaultProvider(); + + // Permission Context + private final Context context; + private final Context overrideClaimContext; + + private UUID id = null; + private UUID ownerUniqueId; + + public boolean cuboid = false; + + protected ClaimStorageData claimStorage; + protected IClaimData claimData; + + public GDClaim parent = null; + public Set children = new HashSet<>(); + public ClaimVisual claimVisual; + public List playersWatching = new ArrayList<>(); + public Map schematics = new HashMap<>(); + + private GDPlayerData ownerPlayerData; + private static final int MAX_AREA = GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME ? 2560000 : 10000; + + public GDClaim(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid) { + this(world, point1, point2, type, ownerUniqueId, cuboid, null); + } + + public GDClaim(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid, GDClaim parent) { + int minx = Math.min(point1.getX(), point2.getX()); + int miny = Math.min(point1.getY(), point2.getY()); + int minz = Math.min(point1.getZ(), point2.getZ()); + int maxx = Math.max(point1.getX(), point2.getX()); + int maxy = Math.max(point1.getY(), point2.getY()); + int maxz = Math.max(point1.getZ(), point2.getZ()); + + this.world = world; + this.lesserBoundaryCorner = new Vector3i(minx, miny, minz); + this.greaterBoundaryCorner = new Vector3i(maxx, maxy, maxz); + if (ownerUniqueId != null) { + this.ownerUniqueId = ownerUniqueId; + this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world, this.ownerUniqueId); + } + this.type = type; + this.id = UUID.randomUUID(); + this.context = new Context("gd_claim", this.id.toString()); + this.overrideClaimContext = new Context("gd_claim_override", this.id.toString()); + this.cuboid = cuboid; + this.parent = parent; + this.hashCode = this.id.hashCode(); + this.worldClaimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + if (this.type == ClaimTypes.WILDERNESS) { + this.wildernessClaim = this; + } else { + this.wildernessClaim = this.worldClaimManager.getWildernessClaim(); + } + } + + // Used for visualizations + public GDClaim(World world, Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, ClaimType type, boolean cuboid) { + this(world, lesserBoundaryCorner, greaterBoundaryCorner, UUID.randomUUID(), type, null, cuboid); + } + + // Used at server startup + public GDClaim(World world, Vector3i lesserBoundaryCorner, Vector3i greaterBoundaryCorner, UUID claimId, ClaimType type, UUID ownerUniqueId, boolean cuboid) { + this.id = claimId; + this.overrideClaimContext = new Context("gd_claim_override", this.id.toString()); + this.lesserBoundaryCorner = lesserBoundaryCorner; + this.greaterBoundaryCorner = greaterBoundaryCorner; + this.world = world; + if (ownerUniqueId != null) { + this.ownerUniqueId = ownerUniqueId; + this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world, this.ownerUniqueId); + } + this.type = type; + this.cuboid = cuboid; + this.context = new Context("gd_claim", this.id.toString()); + this.hashCode = this.id.hashCode(); + this.worldClaimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + if (this.type == ClaimTypes.WILDERNESS) { + this.wildernessClaim = this; + } else { + this.wildernessClaim = this.worldClaimManager.getWildernessClaim(); + } + } + + public void initializeClaimData(GDClaim parent) { + Path claimDataFolderPath = null; + // check if main world + if (parent != null) { + claimDataFolderPath = parent.getClaimStorage().filePath.getParent().resolve(this.type.getName().toLowerCase()); + } else { + claimDataFolderPath = BaseStorage.worldConfigMap.get(this.world.getUID()).getPath().getParent().resolve("ClaimData").resolve(this.type.getName().toLowerCase()); + } + try { + if (Files.notExists(claimDataFolderPath)) { + Files.createDirectories(claimDataFolderPath); + } + } catch (IOException e) { + e.printStackTrace(); + } + File claimFile = new File(claimDataFolderPath + File.separator + this.id); + if (this.isTown()) { + this.claimStorage = new TownStorageData(claimFile.toPath(), this.world.getUID(), this.ownerUniqueId, this.cuboid); + } else { + this.claimStorage = new ClaimStorageData(claimFile.toPath(), this.world.getUID(), this.ownerUniqueId, this.type, this.cuboid); + } + this.claimData = this.claimStorage.getConfig(); + this.parent = parent; + + this.updateClaimStorageData(); + } + + public ClaimType getType() { + return this.type; + } + + public void setType(ClaimType type) { + this.type = type; + this.claimData.setType(type); + } + + public ClaimVisual getVisualizer() { + if (this.claimVisual == null) { + this.claimVisual = new ClaimVisual(this, ClaimVisual.getClaimVisualType(this)); + } + return this.claimVisual; + } + + public GDPlayerData getOwnerPlayerData() { + if (this.ownerPlayerData == null && this.ownerUniqueId != null) { + this.ownerPlayerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world.getUID(), this.ownerUniqueId); + } + + return this.ownerPlayerData; + } + + public UUID getOwnerUniqueId() { + if (this.isAdminClaim()) { + return GriefDefenderPlugin.ADMIN_USER_UUID; + } + if (this.ownerUniqueId == null) { + if (this.parent != null) { + return this.parent.getOwnerUniqueId(); + } + + return GriefDefenderPlugin.ADMIN_USER_UUID; + } + + return this.ownerUniqueId; + } + + public void setOwnerUniqueId(UUID uniqueId) { + this.ownerUniqueId = uniqueId; + } + + public boolean isAdminClaim() { + return this.type == ClaimTypes.ADMIN; + } + + @Override + public boolean isCuboid() { + if (this.claimData != null) { + return this.claimData.isCuboid(); + } + + return this.cuboid; + } + + @Override + public boolean isInTown() { + if (this.isTown()) { + return true; + } + + GDClaim parent = this.parent; + while (parent != null) { + if (parent.isTown()) { + return true; + } + parent = parent.parent; + } + + return false; + } + + @Override + public Optional getTown() { + return Optional.ofNullable(this.getTownClaim()); + } + + @Nullable + public GDClaim getTownClaim() { + if (this.isTown()) { + return this; + } + + if (this.parent == null) { + return null; + } + + GDClaim parent = this.parent; + while (parent != null) { + if (parent.isTown()) { + return parent; + } + parent = parent.parent; + } + + return null; + } + + @Override + public UUID getUniqueId() { + return this.id; + } + + public Optional getName() { + if (this.claimData == null) { + return Optional.empty(); + } + return this.claimData.getName(); + } + + public Component getFriendlyNameType() { + return this.getFriendlyNameType(false); + } + + public Component getFriendlyNameType(boolean upper) { + if (this.type == ClaimTypes.ADMIN) { + if (upper) { + return TextComponent.of(this.type.getName(), TextColor.RED); + } + return TextComponent.of("Admin", TextColor.RED); + } + + if (this.type == ClaimTypes.BASIC) { + if (upper) { + return TextComponent.of(this.type.getName(), TextColor.YELLOW); + } + return TextComponent.of("Basic", TextColor.YELLOW); + } + + if (this.type == ClaimTypes.SUBDIVISION) { + if (upper) { + return TextComponent.of(this.type.getName(), TextColor.AQUA); + } + return TextComponent.of("Subdivision", TextColor.AQUA); + } + + if (upper) { + return TextComponent.of(this.type.getName(), TextColor.GREEN); + } + return TextComponent.of("Town", TextColor.GREEN); + } + + @Override + public int getClaimBlocks() { + if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) { + return this.getVolume(); + } + + return this.getArea(); + } + + @Override + public int getArea() { + final int claimWidth = this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1; + final int claimLength = this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1; + + return claimWidth * claimLength; + } + + @Override + public int getVolume() { + final int claimWidth = this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1; + final int claimLength = this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1; + final int claimHeight = this.greaterBoundaryCorner.getY() - this.lesserBoundaryCorner.getY() + 1; + + return claimWidth * claimLength * claimHeight; + } + + @Override + public int getWidth() { + return this.greaterBoundaryCorner.getX() - this.lesserBoundaryCorner.getX() + 1; + } + + @Override + public int getHeight() { + return this.greaterBoundaryCorner.getZ() - this.lesserBoundaryCorner.getZ() + 1; + } + + public Component allowEdit(Player player) { + final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player); + if (user != null) { + return allowEdit(user); + } + + return TextComponent.of(""); + } + + public Component allowEdit(GDPermissionUser holder) { + return allowEdit(holder, false); + } + + public Component allowEdit(GDPermissionUser holder, boolean forced) { + if (this.isUserTrusted(holder, TrustTypes.MANAGER, null, forced)) { + return null; + } + + if (PermissionUtil.getInstance().holderHasPermission(holder, GDPermissions.COMMAND_DELETE_CLAIMS)) { + return null; + } + + if (this.parent != null && this.getData().doesInheritParent()) { + return this.parent.allowEdit(holder); + } + + final Component message = GriefDefenderPlugin.getInstance().messageData.claimOwnerOnly + .apply(ImmutableMap.of( + "owner", this.getOwnerName() + )).build(); + return message; + } + + public Component allowGrantPermission(Player player) { + if(this.allowEdit(player) == null) { + return null; + } + + for(int i = 0; i < this.claimData.getManagers().size(); i++) { + UUID managerID = this.claimData.getManagers().get(i); + if(player.getUniqueId().equals(managerID)) { + return null; + } + } + + if(this.parent != null && this.getData().doesInheritParent()) { + return this.parent.allowGrantPermission(player); + } + + final Component reason = GriefDefenderPlugin.getInstance().messageData.permissionTrust + .apply(ImmutableMap.of( + "owner", this.getOwnerName() + )).build(); + return reason; + } + + @Override + public Vector3i getLesserBoundaryCorner() { + return this.lesserBoundaryCorner.clone(); + } + + @Override + public Vector3i getGreaterBoundaryCorner() { + return this.greaterBoundaryCorner.clone(); + } + + @Override + public Component getOwnerName() { + if (this.isAdminClaim() || this.isWilderness()) { + return GriefDefenderPlugin.getInstance().messageData.ownerAdmin.toText(); + } + + if (this.getOwnerPlayerData() == null) { + return TextComponent.of("[unknown]"); + } + + return TextComponent.of(this.getOwnerPlayerData().getPlayerName()); + } + + @Override + public boolean contains(Vector3i pos, boolean excludeChildren) { + return this.contains(pos.getX(), pos.getY(), pos.getZ(), excludeChildren, null, false); + } + + + public boolean contains(Vector3i pos, GDPlayerData playerData, boolean useBorderBlockRadius) { + return this.contains(pos.getX(), pos.getY(), pos.getZ(), false, playerData, useBorderBlockRadius); + } + + public boolean contains(int x, int y, int z, boolean excludeChildren, GDPlayerData playerData, boolean useBorderBlockRadius) { + int borderBlockRadius = 0; + if (useBorderBlockRadius && (playerData != null && !playerData.ignoreBorderCheck)) { + final int borderRadiusConfig = GriefDefenderPlugin.getActiveConfig(this.world.getUID()).getConfig().claim.borderBlockRadius; + if (borderRadiusConfig > 0 && !this.isUserTrusted(playerData.getSubject(), TrustTypes.BUILDER)) { + borderBlockRadius = borderRadiusConfig; + } + } + + boolean inClaim = ( + y >= (this.lesserBoundaryCorner.getY() - borderBlockRadius)) && + y < (this.greaterBoundaryCorner.getY() + 1 + borderBlockRadius) && + x >= (this.lesserBoundaryCorner.getX() - borderBlockRadius) && + x < (this.greaterBoundaryCorner.getX() + 1 + borderBlockRadius) && + z >= (this.lesserBoundaryCorner.getZ() - borderBlockRadius) && + z < (this.greaterBoundaryCorner.getZ() + 1 + borderBlockRadius); + + if (!inClaim) { + return false; + } + + if (!excludeChildren && this.parent != null && (this.getData() == null || (this.getData() != null && this.getData().doesInheritParent()))) { + return this.parent.contains(x, y, z, false, null, false); + } + + return true; + } + + public boolean isClaimOnBorder(GDClaim claim) { + if (claim.cuboid) { + return false; + } + + boolean result = claim.lesserBoundaryCorner.getX() == this.lesserBoundaryCorner.getX() || + claim.greaterBoundaryCorner.getX() == this.greaterBoundaryCorner.getX() || + claim.lesserBoundaryCorner.getZ() == this.lesserBoundaryCorner.getZ() || + claim.greaterBoundaryCorner.getZ() == this.greaterBoundaryCorner.getZ(); + if (claim.cuboid) { + result = claim.lesserBoundaryCorner.getY() == this.lesserBoundaryCorner.getY() || + claim.greaterBoundaryCorner.getY() == this.greaterBoundaryCorner.getY(); + } + return result; + } + + @Override + public boolean overlaps(Claim other) { + GDClaim otherClaim = (GDClaim) other; + if (this.id == otherClaim.id) { + return false; + } + + // Handle claims entirely within a town + if (this.isTown() && !otherClaim.isTown() && otherClaim.isInside(this)) { + return false; + } + + //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(), false)) { + return true; + } + + return this.isBandingAcross(otherClaim); + } + + //Checks if claim bands across another claim, either horizontally or vertically + public boolean isBandingAcross(GDClaim otherClaim) { + final boolean isClaimInside = otherClaim.isInside(this); + if (isClaimInside) { + return false; + } + + final int smallX = otherClaim.getLesserBoundaryCorner().getX(); + final int smallY = otherClaim.getLesserBoundaryCorner().getY(); + final int smallZ = otherClaim.getLesserBoundaryCorner().getZ(); + final int bigX = otherClaim.getGreaterBoundaryCorner().getX(); + final int bigY = otherClaim.getGreaterBoundaryCorner().getY(); + final int bigZ = otherClaim.getGreaterBoundaryCorner().getZ(); + + if(this.contains(otherClaim.lesserBoundaryCorner, false)) { + return true; + } + if(this.contains(otherClaim.greaterBoundaryCorner, false)) { + return true; + } + if(this.contains(new Vector3i(smallX, 0, bigZ), false)) { + return true; + } + if(this.contains(new Vector3i(bigX, 0, smallZ), false)) { + return true; + } + + boolean inArea = false; + if(this.getLesserBoundaryCorner().getZ() <= bigZ && + this.getLesserBoundaryCorner().getZ() >= smallZ && + this.getLesserBoundaryCorner().getX() < smallX && + this.getGreaterBoundaryCorner().getX() > bigX) + inArea = true; + + if( this.getGreaterBoundaryCorner().getZ() <= bigZ && + this.getGreaterBoundaryCorner().getZ() >= smallZ && + this.getLesserBoundaryCorner().getX() < smallX && + this.getGreaterBoundaryCorner().getX() > bigX ) + inArea = true; + + if( this.getLesserBoundaryCorner().getX() <= bigX && + this.getLesserBoundaryCorner().getX() >= smallX && + this.getLesserBoundaryCorner().getZ() < smallZ && + this.getGreaterBoundaryCorner().getZ() > bigZ ) + inArea = true; + + if( this.getGreaterBoundaryCorner().getX() <= bigX && + this.getGreaterBoundaryCorner().getX() >= smallX && + this.getLesserBoundaryCorner().getZ() < smallZ && + this.getGreaterBoundaryCorner().getZ() > bigZ ) + inArea = true; + + if (inArea) { + // check height + if ((this.lesserBoundaryCorner.getY() >= smallY && + this.lesserBoundaryCorner.getY() <= bigY) || + (this.greaterBoundaryCorner.getY() <= smallY && + this.greaterBoundaryCorner.getY() >= smallY)) { + return true; + } + + return false; + } + + return false; + } + + @Override + public boolean isInside(Claim claim) { + final GDClaim otherClaim = (GDClaim) claim; + if(!otherClaim.contains(this.lesserBoundaryCorner)) { + return false; + } + if(!otherClaim.contains(this.greaterBoundaryCorner)) { + return false; + } + + if(!otherClaim.contains(new Vector3i(this.lesserBoundaryCorner.getX(), this.lesserBoundaryCorner.getY(), this.greaterBoundaryCorner.getZ()))) { + return false; + } + if(!otherClaim.contains(new Vector3i(this.greaterBoundaryCorner.getX(), this.greaterBoundaryCorner.getY(), this.lesserBoundaryCorner.getZ()))) { + return false; + } + + return true; + } + + @Override + public ArrayList getChunkPositions() { + ArrayList chunkPositions = new ArrayList(); + final Set chunkHashes = this.getChunkHashes(true); + for (Long hash : chunkHashes) { + //chunkPositions.add(ChunkPos.) + } + return chunkPositions; + } + + public ArrayList getChunks() { + ArrayList chunks = new ArrayList(); + + Chunk lesserChunk = this.world + .getChunkAt(this.getLesserBoundaryCorner().getX() >> 4, this.getLesserBoundaryCorner().getZ() >> 4); + Chunk greaterChunk = this.world + .getChunkAt(this.getGreaterBoundaryCorner().getX() >> 4, this.getGreaterBoundaryCorner().getZ() >> 4); + + if (lesserChunk != null && greaterChunk != null) { + for (int x = lesserChunk.getX(); x <= greaterChunk.getX(); x++) { + for (int z = lesserChunk.getZ(); z <= greaterChunk.getZ(); z++) { + Chunk chunk = world.getChunkAt(x, z); + if (chunk != null) { + chunks.add(chunk); + } + } + } + } + + return chunks; + } + + public boolean canIgnoreHeight() { + if (this.isCuboid()) { + return false; + } + + if (this.ownerPlayerData != null && (this.getOwnerMinClaimLevel() > 0 || this.getOwnerMaxClaimLevel() < 255)) { + return false; + } + + return true; + } + + public double getOwnerEconomyBlockCost() { + return this.getOwnerEconomyBlockCost(this.ownerPlayerData); + } + + public double getOwnerEconomyBlockCost(GDPlayerData playerData) { + final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject(); + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(subject, Options.ECONOMY_BLOCK_COST, playerData).intValue(); + } + + public int getOwnerMinClaimLevel() { + return this.getOwnerMinClaimLevel(this.ownerPlayerData); + } + + public int getOwnerMinClaimLevel(GDPlayerData playerData) { + final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject(); + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(subject, Options.MIN_LEVEL, playerData).intValue(); + } + + public int getOwnerMaxClaimLevel() { + return this.getOwnerMaxClaimLevel(this.ownerPlayerData); + } + + public int getOwnerMaxClaimLevel(GDPlayerData playerData) { + final GDPermissionHolder subject = playerData == null ? GriefDefenderPlugin.DEFAULT_HOLDER : playerData.getSubject(); + return GDPermissionManager.getInstance().getGlobalInternalOptionValue(subject, Options.MAX_LEVEL, playerData).intValue(); + } + + @Override + public Set getChunkHashes() { + return this.getChunkHashes(true); + } + + public Set getChunkHashes(boolean refresh) { + if (this.chunkHashes == null || refresh) { + this.chunkHashes = new HashSet(); + int smallX = this.lesserBoundaryCorner.getX() >> 4; + int smallZ = this.lesserBoundaryCorner.getZ() >> 4; + int largeX = this.greaterBoundaryCorner.getX() >> 4; + int largeZ = this.greaterBoundaryCorner.getZ() >> 4; + + for (int x = smallX; x <= largeX; x++) { + for (int z = smallZ; z <= largeZ; z++) { + this.chunkHashes.add(BlockUtil.getInstance().asLong(x, z)); + } + } + } + + return this.chunkHashes; + } + + @Override + public ClaimData getData() { + return (ClaimData) this.claimData; + } + + public IClaimData getInternalClaimData() { + return this.claimData; + } + + @Nullable + public TownDataConfig getTownData() { + if (!(this.claimData instanceof TownDataConfig)) { + return null; + } + + return (TownDataConfig) this.claimData; + } + + public ClaimStorageData getClaimStorage() { + return this.claimStorage; + } + + public void setClaimData(IClaimData data) { + this.claimData = data; + } + + public void setClaimStorage(ClaimStorageData storage) { + this.claimStorage = storage; + } + + public void updateClaimStorageData() { + if (!this.isAdminClaim()) { + this.claimStorage.getConfig().setOwnerUniqueId(this.getOwnerUniqueId()); + } + this.claimStorage.getConfig().setWorldUniqueId(this.world.getUID()); + this.claimData.setCuboid(this.cuboid); + this.claimData.setType(this.type); + this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner)); + this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner)); + // Will save next world save + this.claimData.setRequiresSave(true); + } + + public void save() { + for (Claim child : this.children) { + GDClaim childClaim = (GDClaim) child; + if (childClaim.getInternalClaimData().requiresSave()) { + childClaim.save(); + } + } + GDSaveClaimEvent.Pre preEvent = new GDSaveClaimEvent.Pre(this); + GriefDefender.getEventManager().post(preEvent); + if (this.getInternalClaimData().requiresSave()) { + this.updateClaimStorageData(); + this.getClaimStorage().save(); + this.getInternalClaimData().setRequiresSave(false); + } + GDSaveClaimEvent.Post postEvent = new GDSaveClaimEvent.Post(this); + GriefDefender.getEventManager().post(postEvent); + } + + public boolean isPvpEnabled() { + Tristate value = this.claimData.getPvpOverride(); + if (value != Tristate.UNDEFINED) { + return value.asBoolean(); + } + + return this.world.getPVP(); + } + + public void setPvpOverride(Tristate value) { + this.claimData.setPvpOverride(value); + this.getClaimStorage().save(); + } + + @Override + public ClaimResult transferOwner(UUID newOwnerID) { + if (this.isWilderness()) { + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, TextComponent.builder("").append("The wilderness cannot be transferred.", TextColor.RED).build()); + } + + if (this.isAdminClaim()) { + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, TextComponent.builder("").append("Admin claims cannot be transferred.", TextColor.RED).build()); + } + + GDPlayerData ownerData = DATASTORE.getOrCreatePlayerData(this.world, this.getOwnerUniqueId()); + // determine new owner + GDPlayerData newOwnerData = DATASTORE.getOrCreatePlayerData(this.world, newOwnerID); + + if (this.isBasicClaim() && this.claimData.requiresClaimBlocks() && !GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + int remainingClaimBlocks = newOwnerData.getRemainingClaimBlocks(); + if (remainingClaimBlocks < 0 || (this.getClaimBlocks() > remainingClaimBlocks)) { + return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS); + } + } + + // Check limits + final Player currentOwner = ownerData.getSubject() instanceof Player ? (Player) ownerData.getSubject() : null; + final Double createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(newOwnerData.getSubject(), Options.CREATE_LIMIT, this, newOwnerData); + if (createClaimLimit != null && createClaimLimit > 0 && (newOwnerData.getInternalClaims().size() + 1) > createClaimLimit.intValue()) { + if (currentOwner != null) { + GriefDefenderPlugin.sendMessage(currentOwner, GriefDefenderPlugin.getInstance().messageData.claimTransferExceedsLimit.toText()); + } + return new GDClaimResult(this, ClaimResultType.EXCEEDS_MAX_CLAIM_LIMIT, GriefDefenderPlugin.getInstance().messageData.claimTransferExceedsLimit.toText()); + } + + // transfer + GDTransferClaimEvent event = new GDTransferClaimEvent(this, this.getOwnerUniqueId(), newOwnerID); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + if (this.isAdminClaim()) { + // convert to basic + this.type = ClaimTypes.BASIC; + this.getVisualizer().setType(ClaimVisualType.CLAIM); + this.claimData.setType(ClaimTypes.BASIC); + } + + this.ownerUniqueId = event.getNewOwner(); + if (!this.getOwnerUniqueId().equals(newOwnerID)) { + newOwnerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.world, this.getOwnerUniqueId()); + } + + this.claimData.setOwnerUniqueId(newOwnerID); + if (this.isBasicClaim()) { + ownerData.getInternalClaims().remove(this); + newOwnerData.getInternalClaims().add(this); + } + + this.ownerPlayerData = newOwnerData; + this.getClaimStorage().save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + public ClaimResult doesClaimOverlap() { + if (this.parent != null) { + final GDClaim parentClaim = (GDClaim) this.parent; + // 1 - Make sure new claim is inside parent + if (!this.isInside(parentClaim)) { + return new GDClaimResult(parentClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + + // 2 - Check parent children + for (Claim child : parentClaim.children) { + final GDClaim childClaim = (GDClaim) child; + if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + final Set chunkHashes = this.getChunkHashes(true); + + // Since there is no parent we need to check all claims stored in chunk hashes + for (Long chunkHash : chunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk == null || claimsInChunk.size() == 0) { + continue; + } + for (Claim child : claimsInChunk) { + final GDClaim gpChild = (GDClaim) child; + // First check if newly resized claim is crossing another + if (this.isBandingAcross(gpChild) || gpChild.isBandingAcross(this)) { + return new GDClaimResult(child, ClaimResultType.OVERLAPPING_CLAIM); + } + } + } + + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + // Scans area for any overlaps and migrates children to a newly created or resized claim + public ClaimResult checkArea(boolean resize) { + final List claimsInArea = new ArrayList<>(); + claimsInArea.add(this); + + if (this.parent != null) { + return checkAreaParent(claimsInArea, resize); + } + + final List claimsToMigrate = new ArrayList<>(); + // First check children + for (Claim child : this.children) { + final GDClaim childClaim = (GDClaim) child; + if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + if (childClaim.isInside(this)) { + if (!this.isAdminClaim()) { + if (this.type.equals(childClaim.type) || childClaim.isAdminClaim()) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + } else { + // child is no longer within parent + // if resizing, migrate the child claim out + if (resize) { + claimsToMigrate.add(childClaim); + } else { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + } + + if (!claimsToMigrate.isEmpty()) { + ((GDClaim) this.wildernessClaim).migrateClaims(claimsToMigrate); + } + + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + final Set chunkHashes = this.getChunkHashes(true); + + // Since there is no parent we need to check all claims stored in chunk hashes + for (Long chunkHash : chunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk == null || claimsInChunk.size() == 0) { + continue; + } + for (Claim chunkClaim : claimsInChunk) { + final GDClaim gpChunkClaim = (GDClaim) chunkClaim; + if (gpChunkClaim.equals(this) || claimsInArea.contains(gpChunkClaim)) { + continue; + } + if (this.isAdminClaim() && gpChunkClaim.isAdminClaim() && gpChunkClaim.parent != null && gpChunkClaim.parent.equals(this)) { + continue; + } + + // First check if new claim is crossing another + if (this.isBandingAcross(gpChunkClaim) || gpChunkClaim.isBandingAcross(this)) { + return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + if (gpChunkClaim.isInside(this)) { + if (!this.isAdminClaim()) { + if (this.type.equals(gpChunkClaim.type) || gpChunkClaim.isAdminClaim()) { + return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + if (!this.canEnclose(gpChunkClaim)) { + return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + if (!this.isSubdivision()) { + claimsInArea.add(gpChunkClaim); + } + } else if (this.isInside(gpChunkClaim)) { + // Fix WorldEdit issue + // Make sure to check if chunk claim can enclose newly created claim + if (!gpChunkClaim.canEnclose(this)) { + return new GDClaimResult(gpChunkClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + } + } + + return new GDClaimResult(claimsInArea, ClaimResultType.SUCCESS); + } + + public ClaimResult checkAreaParent(List claimsInArea, boolean resize) { + if (this.isClaimOnBorder(this.parent)) { + return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM); + } + final GDClaim parentClaim = (GDClaim) this.parent; + // 1 - Make sure new claim is inside parent + if (!this.isInside(parentClaim)) { + return new GDClaimResult(parentClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + + // 2 - Check parent children + for (Claim child : parentClaim.children) { + final GDClaim childClaim = (GDClaim) child; + if (this.equals(child)) { + continue; + } + if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + + if (childClaim.isInside(this)) { + if (!this.isAdminClaim()) { + if (this.type.equals(childClaim.type) || childClaim.isAdminClaim()) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + } + if (!this.isSubdivision()) { + claimsInArea.add(childClaim); + } + } + // ignore claims not inside + } + + if (resize) { + // Make sure children are still within their parent + final List claimsToMigrate = new ArrayList<>(); + for (Claim child : this.children) { + GDClaim childClaim = (GDClaim) child; + if (this.isBandingAcross(childClaim) || childClaim.isBandingAcross(this)) { + return new GDClaimResult(childClaim, ClaimResultType.OVERLAPPING_CLAIM); + } + if (!childClaim.isInside(this)) { + if (this.parent != null) { + claimsToMigrate.add(childClaim); + } else { + childClaim.parent = null; + this.children.remove(childClaim); + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + claimWorldManager.addClaim(childClaim, true); + } + } + } + if (!claimsToMigrate.isEmpty()) { + this.parent.migrateClaims(claimsToMigrate); + } + } + return new GDClaimResult(claimsInArea, ClaimResultType.SUCCESS); + } + + public boolean canEnclose(Claim claim) { + if (claim.isWilderness()) { + return false; + } + if (this.isAdminClaim()) { + // admin claims can enclose any type + return true; + } + if (this.isSubdivision()) { + return false; + } + if (this.isBasicClaim()) { + if (!claim.isSubdivision()) { + return false; + } + return true; + } + if (this.isTown()) { + if (claim.isAdminClaim()) { + return false; + } + return true; + } + return true; + } + + // Checks to see if the passed in claim is a parent of this claim + @Override + public boolean isParent(Claim claim) { + if (this.parent == null) { + return false; + } + + GDClaim parent = this.parent; + while (parent != null) { + if (parent.getUniqueId().equals(claim.getUniqueId())) { + return true; + } + parent = parent.parent; + } + + return false; + } + + @Override + public ClaimResult resize(int x1, int x2, int y1, int y2, int z1, int z2) { + int minx = Math.min(x1, x2); + int miny = Math.min(y1, y2); + int minz = Math.min(z1, z2); + int maxx = Math.max(x1, x2); + int maxy = Math.max(y1, y2); + int maxz = Math.max(z1, z2); + + final Object root = GDCauseStackManager.getInstance().getCurrentCause().root(); + final Player player = root instanceof Player ? (Player) root : null; + if (this.cuboid) { + return resizeCuboid(player, minx, miny, minz, maxx, maxy, maxz); + } + + Location startCorner = null; + Location endCorner = null; + GDPlayerData playerData = null; + if (player != null) { + playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, player.getUniqueId()); + } else if (!this.isAdminClaim() && this.ownerUniqueId != null) { + playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, this.ownerUniqueId); + } + + if (playerData == null) { + startCorner = new Location(this.world, minx, miny, minz); + endCorner = new Location(this.world, maxx, maxy, maxz); + } else { + startCorner = playerData.lastShovelLocation; + endCorner = playerData.endShovelLocation; + } + + // Auto-adjust Y levels for 2D claims + if (playerData != null) { + miny = this.getOwnerMinClaimLevel(); + } + if (playerData != null) { + maxy = this.getOwnerMaxClaimLevel(); + } + Vector3i currentLesserCorner = this.getLesserBoundaryCorner(); + Vector3i currentGreaterCorner = this.getGreaterBoundaryCorner(); + Vector3i newLesserCorner = new Vector3i(minx, miny, minz); + Vector3i newGreaterCorner = new Vector3i(maxx, maxy, maxz); + + GDChangeClaimEvent.Resize event = new GDChangeClaimEvent.Resize(this, startCorner, endCorner); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED); + } + + // check player has enough claim blocks + if ((this.isBasicClaim() || this.isTown()) && this.claimData.requiresClaimBlocks()) { + final int newCost = BlockUtil.getInstance().getClaimBlockCost(this.world, newLesserCorner, newGreaterCorner, this.cuboid); + final int currentCost = BlockUtil.getInstance().getClaimBlockCost(this.world, currentLesserCorner, currentGreaterCorner, this.cuboid); + if (GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode && this.vaultProvider != null) { + if (!this.vaultProvider.getApi().hasAccount(player)) { + return new GDClaimResult(ClaimResultType.ECONOMY_ACCOUNT_NOT_FOUND); + } + + final double requiredFunds = Math.abs((newCost - currentCost) * this.getOwnerEconomyBlockCost()); + if (newCost > currentCost) { + final double currentFunds = this.vaultProvider.getApi().getBalance(player); + final EconomyResponse result = this.vaultProvider.getApi().withdrawPlayer(player, requiredFunds); + if (!result.transactionSuccess()) { + Component message = null; + if (player != null) { + message = GriefDefenderPlugin.getInstance().messageData.economyNotEnoughFunds + .apply(ImmutableMap.of( + "balance", this.vaultProvider.getApi().getBalance(player), + "cost", requiredFunds)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + return new GDClaimResult(ClaimResultType.ECONOMY_NOT_ENOUGH_FUNDS, message); + } + } else { + final EconomyResponse result = this.vaultProvider.getApi().depositPlayer(player, requiredFunds); + } + } else if (newCost > currentCost) { + final int remainingClaimBlocks = this.ownerPlayerData.getRemainingClaimBlocks() - (newCost - currentCost); + if (remainingClaimBlocks < 0) { + if (player != null) { + if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) { + final double claimableChunks = Math.abs(remainingClaimBlocks / 65536.0); + final Map params = ImmutableMap.of( + "chunks", Math.round(claimableChunks * 100.0)/100.0, + "blocks", Math.abs(remainingClaimBlocks)); + GriefDefenderPlugin.sendMessage(player, MessageStorage.CLAIM_SIZE_NEED_BLOCKS_3D, GriefDefenderPlugin.getInstance().messageData.claimSizeNeedBlocks3d, params); + } else { + final Map params = ImmutableMap.of( + "blocks", Math.abs(remainingClaimBlocks)); + GriefDefenderPlugin.sendMessage(player, MessageStorage.CLAIM_SIZE_NEED_BLOCKS_2D, GriefDefenderPlugin.getInstance().messageData.claimSizeNeedBlocks2d, params); + } + } + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + this.lesserBoundaryCorner = currentLesserCorner; + this.greaterBoundaryCorner = currentGreaterCorner; + return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS); + } + } + } + + this.lesserBoundaryCorner = newLesserCorner; + this.greaterBoundaryCorner = newGreaterCorner; + + // checkArea refreshes the current chunk hashes so it is important + // to make a copy before making the call + final Set currentChunkHashes = new HashSet<>(this.chunkHashes); + + final ClaimResult result = this.checkArea(true); + if (!result.successful()) { + this.lesserBoundaryCorner = currentLesserCorner; + this.greaterBoundaryCorner = currentGreaterCorner; + return result; + } + + if (this.type != ClaimTypes.ADMIN && this.type != ClaimTypes.WILDERNESS) { + ClaimResult claimResult = checkSizeLimits(player, playerData, newLesserCorner, newGreaterCorner); + if (!claimResult.successful()) { + this.lesserBoundaryCorner = currentLesserCorner; + this.greaterBoundaryCorner = currentGreaterCorner; + return claimResult; + } + } + + // This needs to be adjusted before we check for overlaps + this.lesserBoundaryCorner = newLesserCorner; + this.greaterBoundaryCorner = newGreaterCorner; + GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + + // resize validated, remove invalid chunkHashes + if (this.parent == null) { + for (Long chunkHash : currentChunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk != null && claimsInChunk.size() > 0) { + claimsInChunk.remove(this); + } + } + + final Set newChunkHashes = this.getChunkHashes(true); + // add new chunk hashes + for (Long chunkHash : newChunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk == null) { + claimsInChunk = new HashSet<>(); + claimWorldManager.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk); + } + + claimsInChunk.add(this); + } + } + + this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner)); + this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner)); + this.claimData.setRequiresSave(true); + this.getClaimStorage().save(); + + if (result.getClaims().size() > 1) { + this.migrateClaims(new ArrayList<>(result.getClaims())); + } + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + public ClaimResult resizeCuboid(Player player, int smallX, int smallY, int smallZ, int bigX, int bigY, int bigZ) { + // make sure resize doesn't cross paths + if (smallX >= bigX || smallY >= bigY || smallZ >= bigZ) { + return new GDClaimResult(this, ClaimResultType.OVERLAPPING_CLAIM); + } + + Location startCorner = null; + Location endCorner = null; + GDPlayerData playerData = null; + + if (player != null) { + playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, player.getUniqueId()); + } else if (!this.isAdminClaim() && this.ownerUniqueId != null) { + playerData = GriefDefenderPlugin.getInstance().dataStore.getPlayerData(this.world, this.ownerUniqueId); + } + + if (playerData == null) { + startCorner = new Location(this.world, smallX, smallY, smallZ); + endCorner = new Location(this.world, bigX, bigY, bigZ); + } else { + startCorner = playerData.lastShovelLocation; + endCorner = playerData.endShovelLocation; + } + + GDChangeClaimEvent.Resize event = new GDChangeClaimEvent.Resize(this, startCorner, endCorner); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(this, ClaimResultType.CLAIM_EVENT_CANCELLED); + } + + final int minClaimLevel = this.getOwnerMinClaimLevel(); + if (playerData != null && playerData.shovelMode != ShovelTypes.ADMIN && smallY < minClaimLevel) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimBelowLevel + .apply(ImmutableMap.of( + "claim-level", minClaimLevel)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return new GDClaimResult(ClaimResultType.BELOW_MIN_LEVEL); + } + final int maxClaimLevel = this.getOwnerMaxClaimLevel(); + if (playerData != null && playerData.shovelMode != ShovelTypes.ADMIN && bigY > maxClaimLevel) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimAboveLevel + .apply(ImmutableMap.of( + "claim-level", maxClaimLevel)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return new GDClaimResult(ClaimResultType.ABOVE_MAX_LEVEL); + } + // check if child extends past parent limits + if (this.parent != null) { + if (smallX < this.parent.getLesserBoundaryCorner().getX() || + smallY < this.parent.getLesserBoundaryCorner().getY() || + smallZ < this.parent.getLesserBoundaryCorner().getZ()) { + return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM); + } + if (bigX > this.parent.getGreaterBoundaryCorner().getX() || + (this.parent.isCuboid() && bigY > this.parent.getGreaterBoundaryCorner().getY()) || + bigZ > this.parent.getGreaterBoundaryCorner().getZ()) { + return new GDClaimResult(this.parent, ClaimResultType.OVERLAPPING_CLAIM); + } + } + + Vector3i currentLesserCorner = this.lesserBoundaryCorner; + Vector3i currentGreaterCorner = this.greaterBoundaryCorner; + Vector3i newLesserCorner = new Vector3i(smallX, smallY, smallZ); + Vector3i newGreaterCorner = new Vector3i(bigX, bigY, bigZ); + this.lesserBoundaryCorner = newLesserCorner; + this.greaterBoundaryCorner = newGreaterCorner; + + // checkArea refreshes the current chunk hashes so it is important + // to make a copy before making the call + final Set currentChunkHashes = new HashSet<>(this.chunkHashes); + + final ClaimResult result = this.checkArea(true); + if (!result.successful()) { + this.lesserBoundaryCorner = currentLesserCorner; + this.greaterBoundaryCorner = currentGreaterCorner; + return result; + } + + if (this.type != ClaimTypes.ADMIN && this.type != ClaimTypes.WILDERNESS) { + ClaimResult claimResult = checkSizeLimits(player, playerData, newLesserCorner, newGreaterCorner); + if (!claimResult.successful()) { + this.lesserBoundaryCorner = currentLesserCorner; + this.greaterBoundaryCorner = currentGreaterCorner; + return claimResult; + } + } + + this.lesserBoundaryCorner = newLesserCorner; + this.greaterBoundaryCorner = newGreaterCorner; + // resize validated, remove invalid chunkHashes + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + if (this.parent == null) { + for (Long chunkHash : currentChunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk != null && claimsInChunk.size() > 0) { + claimsInChunk.remove(this); + } + } + + final Set newChunkHashes = this.getChunkHashes(true); + // add new chunk hashes + for (Long chunkHash : newChunkHashes) { + Set claimsInChunk = claimWorldManager.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk == null) { + claimsInChunk = new HashSet<>(); + claimWorldManager.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk); + } + + claimsInChunk.add(this); + } + } + + this.claimData.setLesserBoundaryCorner(BlockUtil.getInstance().posToString(this.lesserBoundaryCorner)); + this.claimData.setGreaterBoundaryCorner(BlockUtil.getInstance().posToString(this.greaterBoundaryCorner)); + this.claimData.setRequiresSave(true); + this.getClaimStorage().save(); + if (result.getClaims().size() > 1) { + this.migrateClaims(new ArrayList<>(result.getClaims())); + } + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + private ClaimResult checkSizeLimits(Player player, GDPlayerData playerData, Vector3i lesserCorner, Vector3i greaterCorner) { + if (playerData == null) { + return new GDClaimResult(ClaimResultType.SUCCESS); + } + + final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateUser(player.getUniqueId()); + final int minClaimX = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MIN_SIZE_X, this, playerData).intValue(); + final int minClaimY = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MIN_SIZE_Y, this, playerData).intValue(); + final int minClaimZ = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MIN_SIZE_Z, this, playerData).intValue(); + final int maxClaimX = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MAX_SIZE_X, this, playerData).intValue(); + final int maxClaimY = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MAX_SIZE_Y, this, playerData).intValue(); + final int maxClaimZ = GDPermissionManager.getInstance().getInternalOptionValue(holder, Options.MAX_SIZE_Z, this, playerData).intValue(); + + // Handle single block selection + if ((this.isCuboid() && greaterCorner.equals(lesserCorner)) || (!this.isCuboid() && greaterCorner.getX() == lesserCorner.getX() && greaterCorner.getZ() == lesserCorner.getZ())) { + if (playerData.claimResizing != null) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimResizeSameLocation.toText(); + GriefDefenderPlugin.sendMessage(player, message); + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + // TODO: Add new result type for this + return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message); + } + if (playerData.claimSubdividing == null) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimCreateOnlySubdivision.toText(); + GriefDefenderPlugin.sendMessage(player, message); + playerData.lastShovelLocation = null; + // TODO: Add new result type for this + return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message); + } + } + Component message = null; + if (maxClaimX > 0) { + int size = Math.abs(greaterCorner.getX() - lesserCorner.getX()) + 1; + if (size > maxClaimX) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxX + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimX, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + minClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxX + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimX, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + minClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_X, message); + } + } + if (this.cuboid && maxClaimY > 0) { + int size = Math.abs(greaterCorner.getY() - lesserCorner.getY()) + 1; + if (size > maxClaimY) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxY + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimY, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + minClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxY + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimY, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + minClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_Y, message); + } + } + if (maxClaimZ > 0) { + int size = Math.abs(greaterCorner.getZ() - lesserCorner.getZ()) + 1; + if (size > maxClaimZ) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxZ + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimZ, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + minClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMaxZ + .apply(ImmutableMap.of( + "size", size, + "max-size", maxClaimZ, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + minClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.EXCEEDS_MAX_SIZE_Z, message); + } + } + if (minClaimX > 0) { + int size = Math.abs(greaterCorner.getX() - lesserCorner.getX()) + 1; + if (size < minClaimX) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinX + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimX, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + maxClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinX + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimX, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_X, message); + } + } + if (this.cuboid && minClaimY > 0) { + int size = Math.abs(greaterCorner.getY() - lesserCorner.getY()) + 1; + if (size < minClaimY) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinY + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimY, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + maxClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinY + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimY, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_Y, message); + } + } + if (minClaimZ > 0) { + int size = Math.abs(greaterCorner.getZ() - lesserCorner.getZ()) + 1; + if (size < minClaimZ) { + if (player != null) { + if (this.isCuboid()) { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinZ + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimZ, + "min-area", minClaimX + "x" + minClaimY + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimY + "x" + maxClaimZ)).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeMinZ + .apply(ImmutableMap.of( + "size", size, + "min-size", minClaimZ, + "min-area", minClaimX + "x" + minClaimZ, + "max-area", maxClaimX + "x" + maxClaimZ)).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(ClaimResultType.BELOW_MIN_SIZE_Z, message); + } + } + + return new GDClaimResult(ClaimResultType.SUCCESS); + } + + public void unload() { + // clear any references + this.world = null; + if (this.ownerPlayerData != null) { + this.ownerPlayerData.getInternalClaims().remove(this); + } + } + + @Override + public Claim getWilderness() { + return this.wildernessClaim; + } + + @Override + public ClaimManager getClaimManager() { + return (ClaimManager) this.worldClaimManager; + } + + @Override + public Context getContext() { + return this.context; + } + + public Context getInheritContext() { + if (this.parent == null || !this.getData().doesInheritParent()) { + return this.context; + } + + return this.parent.getInheritContext(); + } + + public boolean hasAdminParent() { + if (this.parent == null || this.isAdminClaim()) { + return false; + } + + if (this.parent.isAdminClaim()) { + return true; + } + + return this.parent.hasAdminParent(); + } + + @Override + public boolean extend(int newDepth) { + return false; + } + + @Override + public Optional getParent() { + return Optional.ofNullable(this.parent); + } + + @Override + public UUID getWorldUniqueId() { + return this.world.getUID(); + } + + public World getWorld() { + return this.world; + } + + public void setWorld(World world) { + this.world = world; + } + /*@Override + public List getEntities() { + Collection worldEntityList = Bukkit.getServer().getWorld(this.world.getUID()).getEntities(); + List entityList = new ArrayList<>(); + for (Entity entity : worldEntityList) { + if (!(entity.isDead() && this.contains(VecHelper.toVector3i(entity.getLocation())))) { + entityList.add(entity); + } + } + + return entityList; + }*/ + + @Override + public List getPlayers() { + Collection worldPlayerList = Bukkit.getServer().getWorld(this.world.getUID()).getPlayers(); + List playerList = new ArrayList<>(); + for (Player player : worldPlayerList) { + if (!player.isDead() && this.contains(VecHelper.toVector3i(player.getLocation()))) { + playerList.add(player.getUniqueId()); + } + } + + return playerList; + } + + @Override + public Set getChildren(boolean recursive) { + if (recursive) { + Set claimList = new HashSet<>(this.children); + List subChildren = new ArrayList<>(); + for (Claim child : claimList) { + GDClaim childClaim = (GDClaim) child; + if (!childClaim.children.isEmpty()) { + subChildren.addAll(childClaim.getChildren(true)); + } + } + claimList.addAll(subChildren); + return claimList; + } + return ImmutableSet.copyOf(this.children); + } + + @Override + public List getParents(boolean recursive) { + List parents = new ArrayList<>(); + GDClaim currentClaim = this; + while (currentClaim.parent != null) { + parents.add(currentClaim.parent); + currentClaim = currentClaim.parent; + } + + // Index 0 is highest parent while last index represents direct + Collections.reverse(parents); + return ImmutableList.copyOf(parents); + } + + public List getInheritedParents() { + List parents = new ArrayList<>(); + GDClaim currentClaim = this; + while (currentClaim.parent != null && (currentClaim.getData() == null || currentClaim.getData().doesInheritParent())) { + if (currentClaim.isAdminClaim()) { + if (currentClaim.parent.isAdminClaim()) { + parents.add(currentClaim.parent); + } + } else { + parents.add(currentClaim.parent); + } + currentClaim = currentClaim.parent; + } + + // Index 0 is highest parent while last index represents direct + Collections.reverse(parents); + return ImmutableList.copyOf(parents); + } + + @Override + public ClaimResult deleteChild(Claim child) { + boolean found = false; + for (Claim childClaim : this.children) { + if (childClaim.getUniqueId().equals(child.getUniqueId())) { + found = true; + } + } + + if (!found) { + return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND); + } + + final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + return claimManager.deleteClaim(child, true); + } + + @Override + public ClaimResult deleteChildren() { + return this.deleteChildren(null); + } + + @Override + public ClaimResult deleteChildren(ClaimType claimType) { + List claimList = new ArrayList<>(); + for (Claim child : this.children) { + if (claimType == null || child.getType() == claimType) { + claimList.add(child); + } + } + + if (claimList.isEmpty()) { + return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND); + } + + GDDeleteClaimEvent event = new GDDeleteClaimEvent(claimList); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(claimList, ClaimResultType.CLAIM_EVENT_CANCELLED); + } + + final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + for (Claim child : claimList) { + claimManager.deleteClaimInternal(child, true); + } + + return new GDClaimResult(event.getClaims(), ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult changeType(ClaimType type, Optional ownerUniqueId) { + return changeType(type, ownerUniqueId, null); + } + + public ClaimResult changeType(ClaimType type, Optional ownerUniqueId, CommandSender src) { + if (type == this.type) { + return new GDClaimResult(ClaimResultType.SUCCESS); + } + + GDChangeClaimEvent.Type event = new GDChangeClaimEvent.Type(this, type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.world.getUID()); + final GDPlayerData sourcePlayerData = src != null && src instanceof Player ? claimWorldManager.getOrCreatePlayerData(((Player) src).getUniqueId()) : null; + UUID newOwnerUUID = ownerUniqueId.orElse(this.ownerUniqueId); + final ClaimResult result = this.validateClaimType(type, newOwnerUUID, sourcePlayerData); + if (!result.successful()) { + return result; + } + + if (type == ClaimTypes.ADMIN) { + newOwnerUUID = GriefDefenderPlugin.ADMIN_USER_UUID; + } + + final String fileName = this.getClaimStorage().filePath.getFileName().toString(); + final Path newPath = this.getClaimStorage().folderPath.getParent().resolve(type.getName().toLowerCase()).resolve(fileName); + try { + if (Files.notExists(newPath.getParent())) { + Files.createDirectories(newPath.getParent()); + } + Files.move(this.getClaimStorage().filePath, newPath); + if (type == ClaimTypes.TOWN) { + this.setClaimStorage(new TownStorageData(newPath, this.getWorldUniqueId(), newOwnerUUID, this.cuboid)); + } else { + this.setClaimStorage(new ClaimStorageData(newPath, this.getWorldUniqueId(), (ClaimDataConfig) this.getInternalClaimData())); + } + this.claimData = this.claimStorage.getConfig(); + this.getClaimStorage().save(); + } catch (IOException e) { + e.printStackTrace(); + return new GDClaimResult(ClaimResultType.CLAIM_NOT_FOUND, TextComponent.of(e.getMessage())); + } + + // If switched to admin or new owner, remove from player claim list + if (type == ClaimTypes.ADMIN || !this.ownerUniqueId.equals(newOwnerUUID)) { + final Set currentPlayerClaims = claimWorldManager.getInternalPlayerClaims(this.ownerUniqueId); + if (currentPlayerClaims != null) { + currentPlayerClaims.remove(this); + } + } + if (type != ClaimTypes.ADMIN) { + final Set newPlayerClaims = claimWorldManager.getInternalPlayerClaims(newOwnerUUID); + if (newPlayerClaims != null && !newPlayerClaims.contains(this)) { + newPlayerClaims.add(this); + } + } + + if (!this.isAdminClaim() && this.ownerPlayerData != null) { + final Player player = Bukkit.getServer().getPlayer(this.ownerUniqueId); + if (player != null) { + this.ownerPlayerData.revertActiveVisual(player); + } + } + + // revert visuals for all players watching this claim + List playersWatching = new ArrayList<>(this.playersWatching); + for (UUID playerUniqueId : playersWatching) { + final Player spongePlayer = Bukkit.getServer().getPlayer(playerUniqueId); + final GDPlayerData playerData = claimWorldManager.getOrCreatePlayerData(playerUniqueId); + if (spongePlayer != null) { + playerData.revertActiveVisual(spongePlayer); + } + } + + if (!newOwnerUUID.equals(GriefDefenderPlugin.ADMIN_USER_UUID)) { + this.setOwnerUniqueId(newOwnerUUID); + } + this.setType(type); + this.claimVisual = null; + this.getInternalClaimData().setRequiresSave(true); + this.getClaimStorage().save(); + return new GDClaimResult(ClaimResultType.SUCCESS); + } + + public ClaimResult validateClaimType(ClaimType type, UUID newOwnerUUID, GDPlayerData playerData) { + boolean isAdmin = false; + if (playerData != null && (playerData.canManageAdminClaims || playerData.canIgnoreClaim(this))) { + isAdmin = true; + } + + if (type == ClaimTypes.ADMIN) { + if (!isAdmin) { + final Component message = TextComponent.of("You do not have administrative permissions to change type to ADMIN.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + if (this.parent != null && this.parent.isAdminClaim()) { + final Component message = TextComponent.of("Admin claims cannot have direct admin children claims.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + } else if (type == ClaimTypes.BASIC) { + if (this.isAdminClaim() && newOwnerUUID == null) { + return new GDClaimResult(ClaimResultType.REQUIRES_OWNER, TextComponent.of("Could not convert admin claim to basic. Owner is required.", TextColor.RED)); + } + if (this.parent != null && this.parent.isBasicClaim()) { + final Component message = TextComponent.of("Basic claims cannot have direct basic children claims.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + for (Claim child : this.children) { + if (!child.isSubdivision()) { + final Component message = TextComponent.of("Basic claims can only contain subdivisions.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + } + } else if (type == ClaimTypes.SUBDIVISION) { + if (!this.children.isEmpty()) { + final Component message = TextComponent.of("Subdivisions cannot contain children claims.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + if (this.parent == null) { + final Component message = TextComponent.of("Subdivisions cannot be created in the wilderness.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + if (this.isAdminClaim() && newOwnerUUID == null) { + return new GDClaimResult(ClaimResultType.REQUIRES_OWNER, TextComponent.of("Could not convert admin claim to subdivision. Owner is required.", TextColor.RED)); + } + } else if (type == ClaimTypes.TOWN) { + if (this.parent != null && this.parent.isTown()) { + final Component message = TextComponent.of("Towns cannot contain children towns.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + } else if (type == ClaimTypes.WILDERNESS) { + final Component message = TextComponent.of("You cannot change a claim to WILDERNESS.", TextColor.RED); + return new GDClaimResult(ClaimResultType.WRONG_CLAIM_TYPE, message); + } + + return new GDClaimResult(ClaimResultType.SUCCESS); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || !(o instanceof GDClaim)) { + return false; + } + GDClaim that = (GDClaim) o; + return this.type == that.type && + Objects.equal(this.id, that.id); + } + + @Override + public int hashCode() { + return this.hashCode; + } + + @Override + public List getUserTrusts() { + List trustList = new ArrayList<>(); + trustList.addAll(this.claimData.getAccessors()); + trustList.addAll(this.claimData.getContainers()); + trustList.addAll(this.claimData.getBuilders()); + trustList.addAll(this.claimData.getManagers()); + return ImmutableList.copyOf(trustList); + } + + @Override + public List getUserTrusts(TrustType type) { + return ImmutableList.copyOf(this.getUserTrustList(type)); + } + + public boolean isUserTrusted(Player player, TrustType type) { + final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player); + return isUserTrusted(user, type, null); + } + + public boolean isUserTrusted(GDPermissionUser user, TrustType type) { + return isUserTrusted(user, type, null); + } + + @Override + public boolean isUserTrusted(UUID uuid, TrustType type) { + final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(uuid); + return isUserTrusted(user, type, null); + } + + public boolean isUserTrusted(GDPermissionUser user, TrustType type, Set contexts) { + return isUserTrusted(user, type, contexts, false); + } + + public boolean isUserTrusted(GDPermissionUser user, TrustType type, Set contexts, boolean forced) { + if (user == null) { + return false; + } + + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world, user.getUniqueId()); + if (!playerData.canIgnoreClaim(this) && this.getInternalClaimData().isExpired()) { + return false; + } + if (forced || !playerData.debugClaimPermissions) { + if (user.getUniqueId().equals(this.getOwnerUniqueId())) { + return true; + } + if (this.isAdminClaim() && playerData.canManageAdminClaims) { + return true; + } + if (this.isWilderness() && playerData.canManageWilderness) { + return true; + } + if (playerData.canIgnoreClaim(this)) { + return true; + } + } + + if (type == null) { + return true; + } + if (this.isPublicTrusted(type)) { + return true; + } + + if (type == TrustTypes.ACCESSOR) { + if (this.claimData.getAccessors().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getBuilders().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getContainers().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getManagers().contains(user.getUniqueId())) { + return true; + } + } else if (type == TrustTypes.BUILDER) { + if (this.claimData.getBuilders().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getManagers().contains(user.getUniqueId())) { + return true; + } + } else if (type == TrustTypes.CONTAINER) { + if (this.claimData.getContainers().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getBuilders().contains(user.getUniqueId())) { + return true; + } + if (this.claimData.getManagers().contains(user.getUniqueId())) { + return true; + } + } else if (type == TrustTypes.MANAGER) { + if (this.claimData.getManagers().contains(user.getUniqueId())) { + return true; + } + } + + if (contexts == null) { + contexts = new HashSet<>(); + contexts.add(this.getContext()); + } + + if (PermissionUtil.getInstance().getPermissionValue(this, user, GDPermissions.getTrustPermission(type), contexts) == Tristate.TRUE) { + return true; + } + + // Only check parent if this claim inherits + if (this.parent != null && this.getData().doesInheritParent()) { + return this.parent.isUserTrusted(user, type, contexts); + } + + return false; + } + + private boolean isPublicTrusted(TrustType type) { + if (type == TrustTypes.ACCESSOR) { + if (this.claimData.getAccessors().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getContainers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + } else if (type == TrustTypes.BUILDER) { + if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + } else if (type == TrustTypes.CONTAINER) { + if (this.claimData.getContainers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getBuilders().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + } else if (type == TrustTypes.MANAGER) { + if (this.claimData.getManagers().contains(GriefDefenderPlugin.PUBLIC_UUID)) { + return true; + } + } + + return false; + } + + @Override + public boolean isGroupTrusted(String name, TrustType type) { + if (name == null) { + return false; + } + + if (!PermissionUtil.getInstance().hasGroupSubject(name)) { + return false; + } + + final GDPermissionHolder holder = PermissionHolderCache.getInstance().getOrCreateHolder(name); + Set contexts = new HashSet<>(); + contexts.add(this.getContext()); + + return PermissionUtil.getInstance().getPermissionValue(this, holder, GDPermissions.getTrustPermission(type), contexts) == Tristate.TRUE; + } + + @Override + public ClaimResult addUserTrust(UUID uuid, TrustType type) { + GDUserTrustClaimEvent.Add event = new GDUserTrustClaimEvent.Add(this, ImmutableList.of(uuid), type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + List userList = this.getUserTrustList(type); + if (!userList.contains(uuid)) { + userList.add(uuid); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult addUserTrusts(List uuids, TrustType type) { + GDUserTrustClaimEvent.Add event = new GDUserTrustClaimEvent.Add(this, uuids, type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + for (UUID uuid : uuids) { + List userList = this.getUserTrustList(type); + if (!userList.contains(uuid)) { + userList.add(uuid); + } + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeUserTrust(UUID uuid, TrustType type) { + GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, ImmutableList.of(uuid), type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + if (type == TrustTypes.NONE) { + final ClaimResult result = this.removeAllTrustsFromUser(uuid); + this.claimData.setRequiresSave(true); + this.claimData.save(); + return result; + } + + this.getUserTrustList(type).remove(uuid); + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeUserTrusts(List uuids, TrustType type) { + GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, uuids, type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + if (type == TrustTypes.NONE) { + for (UUID uuid : uuids) { + this.removeAllTrustsFromUser(uuid); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + List userList = this.getUserTrustList(type); + for (UUID uuid : uuids) { + if (userList.contains(uuid)) { + userList.remove(uuid); + } + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult addGroupTrust(String group, TrustType type) { + GDGroupTrustClaimEvent.Add event = new GDGroupTrustClaimEvent.Add(this, ImmutableList.of(group), type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + List groupList = this.getGroupTrustList(type); + if (!groupList.contains(group)) { + groupList.add(group); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult addGroupTrusts(List groups, TrustType type) { + GDGroupTrustClaimEvent.Add event = new GDGroupTrustClaimEvent.Add(this, groups, type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + for (String group : groups) { + List groupList = this.getGroupTrustList(type); + if (!groupList.contains(group)) { + groupList.add(group); + } + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeGroupTrust(String group, TrustType type) { + GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, ImmutableList.of(group), type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + if (type == TrustTypes.NONE) { + final ClaimResult result = this.removeAllTrustsFromGroup(group); + this.claimData.setRequiresSave(true); + this.claimData.save(); + return result; + } + + this.getGroupTrustList(type).remove(group); + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeGroupTrusts(List groups, TrustType type) { + GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, groups, type); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + if (type == TrustTypes.NONE) { + for (String group : groups) { + this.removeAllTrustsFromGroup(group); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + List groupList = this.getGroupTrustList(type); + for (String group : groups) { + if (groupList.contains(group)) { + groupList.remove(group); + } + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeAllTrusts() { + List userTrustList = this.getUserTrusts(); + GDUserTrustClaimEvent.Remove userEvent = new GDUserTrustClaimEvent.Remove(this, userTrustList, TrustTypes.NONE); + GriefDefender.getEventManager().post(userEvent); + if (userEvent.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, userEvent.getMessage().orElse(null)); + } + + List groupTrustList = this.getGroupTrusts(); + GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, groupTrustList, TrustTypes.NONE); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getUserTrustList(type).clear(); + } + + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getGroupTrustList(type).clear(); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeAllUserTrusts() { + List trustList = this.getUserTrusts(); + GDUserTrustClaimEvent.Remove event = new GDUserTrustClaimEvent.Remove(this, trustList, TrustTypes.NONE); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getUserTrustList(type).clear(); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + @Override + public ClaimResult removeAllGroupTrusts() { + List trustList = this.getGroupTrusts(); + GDGroupTrustClaimEvent.Remove event = new GDGroupTrustClaimEvent.Remove(this, trustList, TrustTypes.NONE); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getGroupTrustList(type).clear(); + } + + this.claimData.setRequiresSave(true); + this.claimData.save(); + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + public ClaimResult removeAllTrustsFromUser(UUID userUniqueId) { + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getUserTrustList(type).remove(userUniqueId); + } + + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + public ClaimResult removeAllTrustsFromGroup(String group) { + for (TrustType type : TrustTypeRegistryModule.getInstance().getAll()) { + this.getGroupTrustList(type).remove(group); + } + + return new GDClaimResult(this, ClaimResultType.SUCCESS); + } + + public List getUserTrustList(TrustType type) { + if (type == TrustTypes.NONE) { + return new ArrayList<>(); + } + if (type == TrustTypes.ACCESSOR) { + return this.claimData.getAccessors(); + } + if (type == TrustTypes.CONTAINER) { + return this.claimData.getContainers(); + } + if (type == TrustTypes.BUILDER) { + return this.claimData.getBuilders(); + } + return this.claimData.getManagers(); + } + + public List getParentUserTrustList(TrustType type) { + List userList = new ArrayList<>(); + for (Claim claim : this.getInheritedParents()) { + GDClaim parentClaim = (GDClaim) claim; + userList.addAll(parentClaim.getUserTrusts(type)); + } + return userList; + } + + public List getParentGroupTrustList(TrustType type) { + List trustList = new ArrayList<>(); + for (Claim claim : this.getInheritedParents()) { + GDClaim parentClaim = (GDClaim) claim; + trustList.addAll(parentClaim.getGroupTrusts(type)); + } + return trustList; + } + + public List getUserTrustList(TrustType type, boolean includeParents) { + List trustList = new ArrayList<>(); + if (type == TrustTypes.ACCESSOR) { + trustList.addAll(this.claimData.getAccessors()); + } else if (type == TrustTypes.CONTAINER) { + trustList.addAll(this.claimData.getContainers()); + } else if (type == TrustTypes.BUILDER) { + trustList.addAll(this.claimData.getBuilders()); + } else { + trustList.addAll(this.claimData.getManagers()); + } + + if (includeParents) { + List parentList = getParentUserTrustList(type); + for (UUID uuid : parentList) { + if (!trustList.contains(uuid)) { + trustList.add(uuid); + } + } + } + + return trustList; + } + + public List getGroupTrustList(TrustType type) { + return this.getGroupTrustList(type, false); + } + + public List getGroupTrustList(TrustType type, boolean includeParents) { + List trustList = new ArrayList<>(); + if (type == TrustTypes.ACCESSOR) { + trustList.addAll(this.claimData.getAccessorGroups()); + } else if (type == TrustTypes.CONTAINER) { + trustList.addAll(this.claimData.getContainerGroups()); + } else if (type == TrustTypes.BUILDER) { + trustList.addAll(this.claimData.getBuilderGroups()); + } else { + trustList.addAll(this.claimData.getManagerGroups()); + } + + if (includeParents) { + List parentList = getParentGroupTrustList(type); + for (String groupId : parentList) { + if (!trustList.contains(groupId)) { + trustList.add(groupId); + } + } + } + + return trustList; + } + + @Override + public List getGroupTrusts() { + List groups = new ArrayList<>(); + groups.addAll(this.getInternalClaimData().getAccessorGroups()); + groups.addAll(this.getInternalClaimData().getBuilderGroups()); + groups.addAll(this.getInternalClaimData().getContainerGroups()); + groups.addAll(this.getInternalClaimData().getManagerGroups()); + return ImmutableList.copyOf(groups); + } + + @Override + public List getGroupTrusts(TrustType type) { + return ImmutableList.copyOf(this.getGroupTrustList(type)); + } + + public Optional getEconomyAccountId() { + if (this.vaultProvider == null || this.vaultProvider.getApi() == null || !this.vaultProvider.getApi().hasBankSupport() || this.isAdminClaim() || this.isSubdivision() || !GriefDefenderPlugin.getGlobalConfig().getConfig().claim.bankTaxSystem) { + return Optional.empty(); + } + + if (this.vaultProvider.getApi().getBanks().contains(this.id.toString())) { + return Optional.of(this.id); + } + + if (this.vaultProvider != null) { + this.vaultProvider.getApi().createBank(this.claimStorage.filePath.getFileName().toString(), this.ownerPlayerData.getSubject().getOfflinePlayer()); + return Optional.ofNullable(this.id); + } + return Optional.empty(); + } + + public static class ClaimBuilder implements Builder { + + private UUID ownerUniqueId; + private ClaimType type = ClaimTypes.BASIC; + private boolean cuboid = false; + private boolean requiresClaimBlocks = true; + private boolean denyMessages = true; + private boolean expire = true; + private boolean resizable = true; + private boolean inherit = true; + private boolean overrides = true; + private boolean createLimitRestrictions = true; + private boolean levelRestrictions = true; + private boolean sizeRestrictions = true; + private UUID worldUniqueId; + private Vector3i point1; + private Vector3i point2; + private Vector3i spawnPos; + private Component greeting; + private Component farewell; + private Claim parent; + + public ClaimBuilder() { + + } + + @Override + public Builder bounds(Vector3i point1, Vector3i point2) { + this.point1 = point1; + this.point2 = point2; + return this; + } + + @Override + public Builder cuboid(boolean cuboid) { + this.cuboid = cuboid; + return this; + } + + @Override + public Builder owner(UUID ownerUniqueId) { + this.ownerUniqueId = ownerUniqueId; + return this; + } + + @Override + public Builder parent(Claim parentClaim) { + this.parent = parentClaim; + return this; + } + + @Override + public Builder type(ClaimType type) { + this.type = type; + return this; + } + + @Override + public Builder world(UUID worldUniqueId) { + this.worldUniqueId = worldUniqueId; + return this; + } + + @Override + public Builder createLimitRestrictions(boolean checkCreateLimit) { + this.createLimitRestrictions = checkCreateLimit; + return this; + } + + @Override + public Builder levelRestrictions(boolean checkLevel) { + this.levelRestrictions = checkLevel; + return this; + } + + @Override + public Builder sizeRestrictions(boolean checkSize) { + this.sizeRestrictions = checkSize; + return this; + } + + @Override + public Builder requireClaimBlocks(boolean requiresClaimBlocks) { + this.requiresClaimBlocks = requiresClaimBlocks; + return this; + } + + @Override + public Builder denyMessages(boolean allowDenyMessages) { + this.denyMessages = allowDenyMessages; + return this; + } + + @Override + public Builder expire(boolean allowExpire) { + this.expire = allowExpire; + return this; + } + + @Override + public Builder inherit(boolean inherit) { + this.inherit = inherit; + return this; + } + + @Override + public Builder resizable(boolean allowResize) { + this.resizable = allowResize; + return this; + } + + @Override + public Builder overrides(boolean allowOverrides) { + this.overrides = allowOverrides; + return this; + } + + @Override + public Builder farewell(Component farewell) { + this.farewell = farewell; + return this; + } + + @Override + public Builder greeting(Component greeting) { + this.greeting = greeting; + return this; + } + + @Override + public Builder spawnPos(Vector3i spawnPos) { + this.spawnPos = spawnPos; + return this; + } + + @Override + public Builder reset() { + this.ownerUniqueId = null; + this.type = ClaimTypes.BASIC; + this.cuboid = false; + this.worldUniqueId = null; + this.point1 = null; + this.point2 = null; + this.parent = null; + return this; + } + + @Override + public ClaimResult build() { + checkNotNull(this.type); + checkNotNull(this.worldUniqueId); + checkNotNull(this.point1); + checkNotNull(this.point2); + + final World world = Bukkit.getServer().getWorld(this.worldUniqueId); + if (world == null) { + return new GDClaimResult(ClaimResultType.WORLD_NOT_FOUND); + } + + if (this.type == ClaimTypes.SUBDIVISION) { + checkNotNull(this.parent); + } + + if (this.type == ClaimTypes.ADMIN || this.type == ClaimTypes.WILDERNESS) { + this.sizeRestrictions = false; + this.levelRestrictions = false; + } + + GDClaim claim = null; + if (this.type == ClaimTypes.TOWN) { + claim = new GDTown(world, this.point1, this.point2, this.type, this.ownerUniqueId, this.cuboid); + } else { + claim = new GDClaim(world, this.point1, this.point2, this.type, this.ownerUniqueId, this.cuboid); + } + claim.parent = (GDClaim) this.parent; + Player player = null; + final EventCause cause = GDCauseStackManager.getInstance().getCurrentCause(); + if (cause.root() instanceof Player) { + player = (Player) cause.root(); + } + GDPlayerData playerData = null; + double requiredFunds = 0; + + if (this.ownerUniqueId != null) { + if (playerData == null) { + playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(this.worldUniqueId, this.ownerUniqueId); + } + + final WorldGuardProvider worldGuardProvider = GriefDefenderPlugin.getInstance().getWorldGuardProvider(); + if (worldGuardProvider != null) { + if (player != null) { + if (!worldGuardProvider.allowClaimCreate(claim, player)) { + return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED); + } + } else { + final GDPermissionUser user = playerData.getSubject(); + if (user != null && !worldGuardProvider.allowClaimCreate(claim, user.getOnlinePlayer())) { + return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED); + } + } + } + + if (this.levelRestrictions) { + final int minClaimLevel = claim.getOwnerMinClaimLevel(); + if (claim.getLesserBoundaryCorner().getY() < minClaimLevel) { + Component message = null; + if (player != null) { + message = GriefDefenderPlugin.getInstance().messageData.claimBelowLevel + .apply(ImmutableMap.of( + "claim-level", minClaimLevel)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(claim, ClaimResultType.BELOW_MIN_LEVEL, message); + } + final int maxClaimLevel = claim.getOwnerMaxClaimLevel(); + if (claim.getGreaterBoundaryCorner().getY() > maxClaimLevel) { + Component message = null; + if (player != null) { + message = GriefDefenderPlugin.getInstance().messageData.claimAboveLevel + .apply(ImmutableMap.of( + "claim-level", maxClaimLevel)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(claim, ClaimResultType.ABOVE_MAX_LEVEL, message); + } + } + + if (this.sizeRestrictions) { + ClaimResult claimResult = claim.checkSizeLimits(player, playerData, this.point1, this.point2); + if (!claimResult.successful()) { + return claimResult; + } + } + + final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(this.ownerUniqueId); + if (this.createLimitRestrictions && !PermissionUtil.getInstance().holderHasPermission(user, GDPermissions.OVERRIDE_CLAIM_LIMIT)) { + final Double createClaimLimit = GDPermissionManager.getInstance().getInternalOptionValue(user, Options.CREATE_LIMIT, claim, playerData); + if (createClaimLimit != null && createClaimLimit > 0 && (playerData.getClaimTypeCount(claim.getType()) + 1) > createClaimLimit.intValue()) { + if (player != null) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimCreateFailedLimit + .apply(ImmutableMap.of( + "limit", createClaimLimit, + "type", claim.getType().getName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + return new GDClaimResult(claim, ClaimResultType.EXCEEDS_MAX_CLAIM_LIMIT, GriefDefenderPlugin.getInstance().messageData.claimCreateFailedLimit.toText()); + } + } + + // check player has enough claim blocks + if ((claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks) { + final int claimCost = BlockUtil.getInstance().getClaimBlockCost(world, claim.lesserBoundaryCorner, claim.greaterBoundaryCorner, claim.cuboid); + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + final OfflinePlayer vaultPlayer = playerData.getSubject().getOnlinePlayer() != null ? playerData.getSubject().getOnlinePlayer() : playerData.getSubject().getOfflinePlayer(); + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(vaultPlayer)) { + return new GDClaimResult(claim, ClaimResultType.ECONOMY_ACCOUNT_NOT_FOUND); + } + + requiredFunds = claimCost * claim.getOwnerEconomyBlockCost(); + final double currentFunds = economy.getBalance(vaultPlayer); + if (currentFunds < requiredFunds) { + Component message = null; + if (player != null) { + message = GriefDefenderPlugin.getInstance().messageData.economyNotEnoughFunds + .apply(ImmutableMap.of( + "balance", currentFunds, + "cost", requiredFunds)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + return new GDClaimResult(claim, ClaimResultType.ECONOMY_NOT_ENOUGH_FUNDS, message); + } + + final EconomyResponse result = economy.withdrawPlayer(vaultPlayer, requiredFunds); + if (!result.transactionSuccess()) { + Component message = null; + if (player != null) { + message = GriefDefenderPlugin.getInstance().messageData.economyWithdrawError + .apply(ImmutableMap.of( + "reason", result.errorMessage)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + return new GDClaimResult(claim, ClaimResultType.ECONOMY_WITHDRAW_FAIL, message); + } + } else { + final int remainingClaimBlocks = playerData.getRemainingClaimBlocks() - claimCost; + if (remainingClaimBlocks < 0) { + Component message = null; + if (player != null) { + if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME) { + final double claimableChunks = Math.abs(remainingClaimBlocks / 65536.0); + message = GriefDefenderPlugin.getInstance().messageData.claimSizeNeedBlocks3d + .apply(ImmutableMap.of( + "chunks", Math.round(claimableChunks * 100.0)/100.0, + "blocks", Math.abs(remainingClaimBlocks))).build(); + } else { + message = GriefDefenderPlugin.getInstance().messageData.claimSizeNeedBlocks2d + .apply(ImmutableMap.of("blocks", Math.abs(remainingClaimBlocks))).build(); + } + GriefDefenderPlugin.sendMessage(player, message); + } + playerData.lastShovelLocation = null; + playerData.claimResizing = null; + return new GDClaimResult(ClaimResultType.INSUFFICIENT_CLAIM_BLOCKS, message); + } + } + } + + if (!GriefDefenderPlugin.getInstance().isEconomyModeEnabled() && claim.isTown() && player != null) { + final double townCost = GriefDefenderPlugin.getGlobalConfig().getConfig().town.cost; + if (townCost > 0) { + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(player)) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyUserNotFound + .apply(ImmutableMap.of( + "user", claim.getOwnerName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + return new GDClaimResult(claim, ClaimResultType.NOT_ENOUGH_FUNDS, message); + } + final double balance = economy.getBalance(player); + if (balance < townCost) { + final Component message = GriefDefenderPlugin.getInstance().messageData.townCreateNotEnoughFunds + .apply(ImmutableMap.of( + "create_cost", townCost, + "balance", balance, + "amount_needed", townCost - balance)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return new GDClaimResult(claim, ClaimResultType.NOT_ENOUGH_FUNDS, message); + } + economy.withdrawPlayer(player, townCost); + } + } + } + + final ClaimResult claimResult = claim.checkArea(false); + if (!claimResult.successful()) { + if (player != null && (claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + GriefDefenderPlugin.getInstance().getVaultProvider().getApi().depositPlayer(player, requiredFunds); + } + return claimResult; + } + + GDCreateClaimEvent.Pre event = new GDCreateClaimEvent.Pre(claim); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + final Component message = event.getMessage().orElse(null); + if (message != null && player != null) { + GriefDefenderPlugin.sendMessage(player, message); + } + if (player != null && (claim.isBasicClaim() || claim.isTown()) && this.requiresClaimBlocks && GriefDefenderPlugin.getGlobalConfig().getConfig().economy.economyMode) { + GriefDefenderPlugin.getInstance().getVaultProvider().getApi().depositPlayer(player, requiredFunds); + } + return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, message); + } + + claim.initializeClaimData((GDClaim) this.parent); + if (this.parent != null) { + if (this.parent.isTown()) { + claim.getData().setInheritParent(true); + } + claim.getData().setParent(this.parent.getUniqueId()); + } + + claim.getData().setExpiration(this.expire); + claim.getData().setDenyMessages(this.denyMessages); + claim.getData().setFlagOverrides(this.overrides); + claim.getData().setInheritParent(this.inherit); + claim.getData().setResizable(this.resizable); + claim.getData().setRequiresClaimBlocks(this.requiresClaimBlocks); + claim.getData().setFarewell(this.farewell); + claim.getData().setGreeting(this.greeting); + claim.getData().setSpawnPos(this.spawnPos); + claim.getData().setSizeRestrictions(this.sizeRestrictions); + final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.worldUniqueId); + claimManager.addClaim(claim, true); + + if (claimResult.getClaims().size() > 1) { + claim.migrateClaims(new ArrayList<>(claimResult.getClaims())); + } + + GDCreateClaimEvent.Post postEvent = new GDCreateClaimEvent.Post(claim); + GriefDefender.getEventManager().post(postEvent); + if (postEvent.cancelled()) { + final Component message = postEvent.getMessage().orElse(null); + if (message != null && player != null) { + GriefDefenderPlugin.sendMessage(player, message); + } + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUID()); + claimWorldManager.deleteClaimInternal(claim, true); + return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, message); + } + return new GDClaimResult(claim, ClaimResultType.SUCCESS); + } + } + + public boolean migrateClaims(List claims) { + GDClaim parentClaim = this; + for (Claim child : claims) { + if (child.equals(this)) { + continue; + } + + moveChildToParent(parentClaim, (GDClaim) child); + } + + return true; + } + + private void moveChildToParent(GDClaim parentClaim, GDClaim childClaim) { + // Remove child from current parent if available + if (childClaim.parent != null && childClaim.parent != parentClaim) { + childClaim.parent.children.remove(childClaim); + } + childClaim.parent = parentClaim; + String fileName = childClaim.getClaimStorage().filePath.getFileName().toString(); + Path newPath = parentClaim.getClaimStorage().folderPath.resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName); + try { + if (Files.notExists(newPath.getParent())) { + Files.createDirectories(newPath.getParent()); + } + Files.move(childClaim.getClaimStorage().filePath, newPath); + if (childClaim.getClaimStorage().folderPath.toFile().listFiles().length == 0) { + Files.delete(childClaim.getClaimStorage().folderPath); + } + childClaim.setClaimStorage(new ClaimStorageData(newPath, this.getWorldUniqueId(), (ClaimDataConfig) childClaim.getInternalClaimData())); + } catch (IOException e) { + e.printStackTrace(); + } + + // Make sure to update new parent in storage + childClaim.getInternalClaimData().setParent(parentClaim.getUniqueId()); + this.worldClaimManager.addClaim(childClaim, true); + for (Claim child : childClaim.children) { + moveChildToParent(childClaim, (GDClaim) child); + } + } + + @Override + public Context getDefaultTypeContext() { + if (this.isAdminClaim()) { + return ClaimContexts.ADMIN_DEFAULT_CONTEXT; + } + if (this.isBasicClaim() || this.isSubdivision()) { + return ClaimContexts.BASIC_DEFAULT_CONTEXT; + } + if (this.isTown()) { + return ClaimContexts.TOWN_DEFAULT_CONTEXT; + } + + return ClaimContexts.WILDERNESS_DEFAULT_CONTEXT; + } + + @Override + public Context getOverrideTypeContext() { + if (this.isAdminClaim()) { + return ClaimContexts.ADMIN_OVERRIDE_CONTEXT; + } + if (this.isBasicClaim() || this.isSubdivision()) { + return ClaimContexts.BASIC_OVERRIDE_CONTEXT; + } + if (this.isTown()) { + return ClaimContexts.TOWN_OVERRIDE_CONTEXT; + } + + return ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT; + } + + @Override + public Context getOverrideClaimContext() { + return this.overrideClaimContext; + } + + @Override + public Map getSchematics() { + return this.schematics; + } + + @Override + public boolean deleteSchematic(String name) { + if (GriefDefenderPlugin.getInstance().getWorldEditProvider() == null) { + return false; + } + + final ClaimSchematic schematic = this.schematics.remove(name); + if (schematic == null) { + return true; + } + final Path schematicPath = GriefDefenderPlugin.getInstance().getWorldEditProvider().getSchematicWorldMap().get(schematic.getClaim().getWorldUniqueId()).resolve(schematic.getClaim().getUniqueId().toString()); + if (!Files.exists(schematicPath)) { + return false; + } + + File outputFile = schematicPath.resolve(schematic.getName() + ".schematic").toFile(); + if (outputFile.delete()) { + return true; + } + return false; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDClaimManager.java b/bukkit/src/main/java/com/griefdefender/claim/GDClaimManager.java new file mode 100644 index 0000000..1720dc0 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDClaimManager.java @@ -0,0 +1,663 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import com.flowpowered.math.vector.Vector3i; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimBlockSystem; +import com.griefdefender.api.claim.ClaimManager; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.api.claim.ClaimResultType; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.configuration.ClaimDataConfig; +import com.griefdefender.configuration.ClaimStorageData; +import com.griefdefender.configuration.GriefDefenderConfig; +import com.griefdefender.configuration.PlayerStorageData; +import com.griefdefender.event.GDDeleteClaimEvent; +import com.griefdefender.internal.tracking.PlayerIndexStorage; +import com.griefdefender.internal.tracking.chunk.GDChunk; +import com.griefdefender.internal.util.BlockUtil; +import com.griefdefender.internal.util.VecHelper; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.storage.BaseStorage; +import com.griefdefender.util.Direction; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.serializer.plain.PlainComponentSerializer; +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nullable; + +public class GDClaimManager implements ClaimManager { + + private static final BaseStorage DATASTORE = GriefDefenderPlugin.getInstance().dataStore; + private UUID worldUniqueId; + private GriefDefenderConfig activeConfig; + + // Player UUID -> player data + private Map playerDataList = Maps.newHashMap(); + // World claim list + private Set worldClaims = new HashSet<>(); + // Claim UUID -> Claim + private Map claimUniqueIdMap = Maps.newHashMap(); + // String -> Claim + private Map> chunksToClaimsMap = new Long2ObjectOpenHashMap<>(4096); + // Entity Index + public PlayerIndexStorage playerIndexStorage; + private Map chunksToGpChunks = new Long2ObjectOpenHashMap<>(4096); + + private GDClaim theWildernessClaim; + + public GDClaimManager(World world) { + this.worldUniqueId = world.getUID(); + this.activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId); + this.playerIndexStorage = new PlayerIndexStorage(world); + } + + public GDPlayerData getOrCreatePlayerData(UUID playerUniqueId) { + GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId); + if (playerData == null) { + return createPlayerData(playerUniqueId); + } else { + return playerData; + } + } + + private GDPlayerData createPlayerData(UUID playerUniqueId) { + Path playerFilePath = null; + if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { + playerFilePath = BaseStorage.globalPlayerDataPath.resolve(playerUniqueId.toString()); + } else { + playerFilePath = BaseStorage.worldConfigMap.get(this.worldUniqueId).getPath().getParent().resolve("PlayerData").resolve(playerUniqueId.toString()); + } + + PlayerStorageData playerStorage = new PlayerStorageData(playerFilePath); + Set claimList = this.createPlayerClaimList(playerUniqueId); + GDPlayerData playerData = new GDPlayerData(this.worldUniqueId, playerUniqueId, playerStorage, this.activeConfig, claimList); + this.getPlayerDataMap().put(playerUniqueId, playerData); + return playerData; + } + + private Set createPlayerClaimList(UUID playerUniqueId) { + Set claimList = new HashSet<>(); + if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { + for (World world : Bukkit.getServer().getWorlds()) { + GDClaimManager claimmanager = DATASTORE.getClaimWorldManager(world.getUID()); + for (Claim claim : claimmanager.worldClaims) { + GDClaim gpClaim = (GDClaim) claim; + if (gpClaim.isAdminClaim()) { + continue; + } + if (gpClaim.parent != null) { + if (gpClaim.parent.getOwnerUniqueId().equals(playerUniqueId)) { + claimList.add(claim); + } + } else { + if (gpClaim.getOwnerUniqueId().equals(playerUniqueId)) { + claimList.add(claim); + } + } + } + } + } else { + for (Claim claim : this.worldClaims) { + GDClaim gpClaim = (GDClaim) claim; + if (gpClaim.isAdminClaim()) { + continue; + } + if (gpClaim.parent != null) { + if (gpClaim.parent.getOwnerUniqueId().equals(playerUniqueId)) { + claimList.add(claim); + } + } else { + if (gpClaim.getOwnerUniqueId().equals(playerUniqueId)) { + claimList.add(claim); + } + } + } + } + + return claimList; + } + + public void removePlayer(UUID playerUniqueId) { + this.getPlayerDataMap().remove(playerUniqueId); + } + + public ClaimResult addClaim(Claim claim) { + GDClaim newClaim = (GDClaim) claim; + // ensure this new claim won't overlap any existing claims + ClaimResult result = newClaim.checkArea(false); + if (!result.successful()) { + return result; + } + + // validate world + if (!this.worldUniqueId.equals(newClaim.getWorld().getUID())) { + World world = Bukkit.getServer().getWorld(this.worldUniqueId); + newClaim.setWorld(world); + } + + // otherwise add this new claim to the data store to make it effective + this.addClaim(newClaim, true); + if (result.getClaims().size() > 1) { + newClaim.migrateClaims(new ArrayList<>(result.getClaims())); + } + return result; + } + + public void addClaim(Claim claimToAdd, boolean writeToStorage) { + GDClaim claim = (GDClaim) claimToAdd; + if (claim.parent == null && this.worldClaims.contains(claimToAdd)) { + return; + } + + if (writeToStorage) { + DATASTORE.writeClaimToStorage(claim); + } + + // We need to keep track of all claims so they can be referenced by children during server startup + this.claimUniqueIdMap.put(claim.getUniqueId(), claim); + + if (claim.isWilderness()) { + this.theWildernessClaim = claim; + return; + } + + if (claim.parent != null) { + claim.parent.children.add(claim); + this.worldClaims.remove(claim); + this.deleteChunkHashes((GDClaim) claim); + if (!claim.isAdminClaim() && (!claim.isInTown() || !claim.getTownClaim().getOwnerUniqueId().equals(claim.getOwnerUniqueId()))) { + final GDPlayerData playerData = this.getPlayerDataMap().get(claim.getOwnerUniqueId()); + Set playerClaims = playerData.getInternalClaims(); + if (!playerClaims.contains(claim)) { + playerClaims.add(claim); + } + } + return; + } + + if (!this.worldClaims.contains(claim)) { + this.worldClaims.add(claim); + } + final UUID ownerId = claim.getOwnerUniqueId(); + final GDPlayerData playerData = this.getPlayerDataMap().get(ownerId); + if (playerData != null) { + Set playerClaims = playerData.getInternalClaims(); + if (!playerClaims.contains(claim)) { + playerClaims.add(claim); + } + } else if (!claim.isAdminClaim()) { + this.createPlayerData(ownerId); + } + + this.updateChunkHashes(claim); + return; + } + + public void updateChunkHashes(GDClaim claim) { + this.deleteChunkHashes(claim); + Set chunkHashes = claim.getChunkHashes(true); + for (Long chunkHash : chunkHashes) { + Set claimsInChunk = this.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk == null) { + claimsInChunk = new HashSet(); + this.getInternalChunksToClaimsMap().put(chunkHash, claimsInChunk); + } + + claimsInChunk.add(claim); + } + } + + // Used when parent claims becomes children + public void removeClaimData(Claim claim) { + this.worldClaims.remove(claim); + this.deleteChunkHashes((GDClaim) claim); + } + + @Override + public ClaimResult deleteClaim(Claim claim, boolean deleteChildren) { + GDDeleteClaimEvent event = new GDDeleteClaimEvent(claim); + GriefDefender.getEventManager().post(event); + if (event.cancelled()) { + return new GDClaimResult(claim, ClaimResultType.CLAIM_EVENT_CANCELLED, event.getMessage().orElse(null)); + } + + this.deleteClaimInternal(claim, deleteChildren); + return new GDClaimResult(claim, ClaimResultType.SUCCESS); + } + + public void deleteClaimInternal(Claim claim, boolean deleteChildren) { + final GDClaim gpClaim = (GDClaim) claim; + Set subClaims = claim.getChildren(false); + for (Claim child : subClaims) { + if (deleteChildren || (gpClaim.parent == null && child.isSubdivision())) { + this.deleteClaimInternal(child, true); + continue; + } + + final GDClaim parentClaim = (GDClaim) claim; + final GDClaim childClaim = (GDClaim) child; + if (parentClaim.parent != null) { + migrateChildToNewParent(parentClaim.parent, childClaim); + } else { + // move child to parent folder + migrateChildToNewParent(null, childClaim); + } + } + + resetPlayerClaimVisuals(claim); + // transfer bank balance to owner + final UUID bankAccount = claim.getEconomyAccountId().orElse(null); + if (bankAccount != null) { + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + final GDPlayerData playerData = ((GDClaim) claim).getOwnerPlayerData(); + if (playerData != null) { + final OfflinePlayer vaultPlayer = playerData.getSubject().getOfflinePlayer(); + if (vaultPlayer != null && !economy.hasAccount(vaultPlayer)) { + final double bankBalance = economy.bankBalance(claim.getUniqueId().toString()).amount; + economy.depositPlayer(vaultPlayer, bankBalance); + } + } + economy.deleteBank(claim.getUniqueId().toString()); + } + this.worldClaims.remove(claim); + this.claimUniqueIdMap.remove(claim.getUniqueId()); + this.deleteChunkHashes((GDClaim) claim); + if (gpClaim.parent != null) { + gpClaim.parent.children.remove(claim); + } + + DATASTORE.deleteClaimFromStorage((GDClaim) claim); + } + + // Migrates children to new parent + private void migrateChildToNewParent(GDClaim parentClaim, GDClaim childClaim) { + childClaim.parent = parentClaim; + String fileName = childClaim.getClaimStorage().filePath.getFileName().toString(); + Path newPath = null; + if (parentClaim == null) { + newPath = childClaim.getClaimStorage().folderPath.getParent().getParent().resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName); + } else { + // Only store in same claim type folder if not admin. + // Admin claims are currently the only type that can hold children of same type within + if (childClaim.getType().equals(parentClaim.getType()) && (!parentClaim.isAdminClaim())) { + newPath = parentClaim.getClaimStorage().folderPath.resolve(fileName); + } else { + newPath = parentClaim.getClaimStorage().folderPath.resolve(childClaim.getType().getName().toLowerCase()).resolve(fileName); + } + } + + try { + if (Files.notExists(newPath.getParent())) { + Files.createDirectories(newPath.getParent()); + } + Files.move(childClaim.getClaimStorage().filePath, newPath); + if (childClaim.getClaimStorage().folderPath.toFile().listFiles().length == 0) { + Files.delete(childClaim.getClaimStorage().folderPath); + } + childClaim.setClaimStorage(new ClaimStorageData(newPath, this.worldUniqueId, (ClaimDataConfig) childClaim.getInternalClaimData())); + } catch (IOException e) { + e.printStackTrace(); + } + + // Make sure to update new parent in storage + final UUID parentUniqueId = parentClaim == null ? null : parentClaim.getUniqueId(); + childClaim.getInternalClaimData().setParent(parentUniqueId); + this.addClaim(childClaim, true); + for (Claim child : childClaim.children) { + migrateChildToNewParent(childClaim, (GDClaim) child); + } + } + + private void resetPlayerClaimVisuals(Claim claim) { + // player may be offline so check is needed + GDPlayerData playerData = this.getPlayerDataMap().get(claim.getOwnerUniqueId()); + if (playerData != null) { + playerData.getInternalClaims().remove(claim); + if (playerData.lastClaim != null) { + playerData.lastClaim.clear(); + } + } + + // revert visuals for all players watching this claim + List playersWatching = new ArrayList<>(((GDClaim) claim).playersWatching); + for (UUID playerUniqueId : playersWatching) { + Player player = Bukkit.getServer().getPlayer(playerUniqueId); + if (player != null) { + playerData = this.getOrCreatePlayerData(playerUniqueId); + playerData.revertActiveVisual(player); + if (playerData.lastClaim != null) { + playerData.lastClaim.clear(); + } + if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) { + GriefDefenderPlugin.getInstance().getWorldEditProvider().revertVisuals(player, playerData, claim.getUniqueId()); + } + } + } + } + + private void deleteChunkHashes(GDClaim claim) { + Set chunkHashes = claim.getChunkHashes(false); + if (chunkHashes == null) { + return; + } + + for (Long chunkHash : chunkHashes) { + Set claimsInChunk = this.getInternalChunksToClaimsMap().get(chunkHash); + if (claimsInChunk != null) { + claimsInChunk.remove(claim); + } + } + } + + @Nullable + public Optional getClaimByUUID(UUID claimUniqueId) { + return Optional.ofNullable(this.claimUniqueIdMap.get(claimUniqueId)); + } + + public Set getInternalPlayerClaims(UUID playerUniqueId) { + final GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId); + if (playerData == null) { + return new HashSet<>(); + } + return playerData.getInternalClaims(); + } + + @Nullable + public Set getPlayerClaims(UUID playerUniqueId) { + final GDPlayerData playerData = this.getPlayerDataMap().get(playerUniqueId); + if (playerData == null) { + return ImmutableSet.of(); + } + return ImmutableSet.copyOf(this.getPlayerDataMap().get(playerUniqueId).getInternalClaims()); + } + + public void createWildernessClaim(World world) { + final Vector3i lesserCorner = new Vector3i(-30000000, 0, -30000000); + final Vector3i greaterCorner = new Vector3i(29999999, 255, 29999999); + // Use world UUID as wilderness claim ID + GDClaim wilderness = new GDClaim(world, lesserCorner, greaterCorner, world.getUID(), ClaimTypes.WILDERNESS, null, false); + wilderness.setOwnerUniqueId(GriefDefenderPlugin.WORLD_USER_UUID); + wilderness.initializeClaimData(null); + wilderness.claimData.save(); + wilderness.claimStorage.save(); + this.theWildernessClaim = wilderness; + this.claimUniqueIdMap.put(wilderness.getUniqueId(), wilderness); + } + + @Override + public GDClaim getWildernessClaim() { + if (this.theWildernessClaim == null) { + World world = Bukkit.getServer().getWorld(this.worldUniqueId); + this.createWildernessClaim(world); + } + return this.theWildernessClaim; + } + + @Override + public Set getWorldClaims() { + return this.worldClaims; + } + + public Map getPlayerDataMap() { + if (BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { + return BaseStorage.GLOBAL_PLAYER_DATA; + } + return this.playerDataList; + } + + @Override + public Map> getChunksToClaimsMap() { + return ImmutableMap.copyOf(this.chunksToClaimsMap); + } + + public Map> getInternalChunksToClaimsMap() { + return this.chunksToClaimsMap; + } + + public void save() { + for (Claim claim : this.worldClaims) { + GDClaim gpClaim = (GDClaim) claim; + gpClaim.save(); + } + this.theWildernessClaim.save(); + + for (GDPlayerData playerData : this.getPlayerDataMap().values()) { + playerData.getStorageData().save(); + } + } + + public void unload() { + this.playerDataList.clear(); + this.worldClaims.clear(); + this.claimUniqueIdMap.clear(); + this.chunksToClaimsMap.clear(); + if (this.theWildernessClaim != null) { + this.theWildernessClaim.unload(); + this.theWildernessClaim = null; + } + this.worldUniqueId = null; + } + + public Claim getClaimAt(Location location, boolean useBorderBlockRadius) { + return this.getClaimAt(VecHelper.toVector3i(location), null, null, useBorderBlockRadius); + } + + public Claim getClaimAtPlayer(Location location, GDPlayerData playerData) { + return this.getClaimAt(VecHelper.toVector3i(location), (GDClaim) playerData.lastClaim.get(), playerData, false); + } + + public Claim getClaimAtPlayer(Location location, GDPlayerData playerData, boolean useBorderBlockRadius) { + return this.getClaimAt(VecHelper.toVector3i(location), (GDClaim) playerData.lastClaim.get(), playerData, useBorderBlockRadius); + } + + @Override + public Claim getClaimAt(Vector3i pos) { + return this.getClaimAt(pos, null, null, false); + } + + public Claim getClaimAt(Vector3i pos, GDClaim cachedClaim, GDPlayerData playerData, boolean useBorderBlockRadius) { + if (cachedClaim != null && !cachedClaim.isWilderness() && cachedClaim.contains(pos, true)) { + return cachedClaim; + } + + Set claimsInChunk = this.getInternalChunksToClaimsMap().get(BlockUtil.getInstance().asLong(pos.getX() >> 4, pos.getZ() >> 4)); + if (useBorderBlockRadius && (playerData != null && !playerData.ignoreBorderCheck)) { + final int borderBlockRadius = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId).getConfig().claim.borderBlockRadius; + // if borderBlockRadius > 0, check surrounding chunks + if (borderBlockRadius > 0) { + for (Direction direction : BlockUtil.getInstance().ORDINAL_SET) { + Vector3i currentPos = pos; + for (int i = 0; i < borderBlockRadius; i++) { // Handle depth + currentPos = BlockUtil.getInstance().getBlockRelative(currentPos, direction); + Set relativeClaims = this.getInternalChunksToClaimsMap().get(BlockUtil.getInstance().asLong(currentPos.getX() >> 4, currentPos.getZ() >> 4)); + if (relativeClaims != null) { + if (claimsInChunk == null) { + claimsInChunk = new HashSet<>(); + } + claimsInChunk.addAll(relativeClaims); + } + } + } + } + } + if (claimsInChunk == null) { + return this.getWildernessClaim(); + } + + for (Claim claim : claimsInChunk) { + GDClaim foundClaim = findClaim((GDClaim) claim, pos, playerData, useBorderBlockRadius); + if (foundClaim != null) { + return foundClaim; + } + } + + // if no claim found, return the world claim + return this.getWildernessClaim(); + } + + private GDClaim findClaim(GDClaim claim, Vector3i pos, GDPlayerData playerData, boolean useBorderBlockRadius) { + if (claim.contains(pos, playerData, useBorderBlockRadius)) { + // when we find a top level claim, if the location is in one of its children, + // return the child claim, not the top level claim + for (Claim childClaim : claim.children) { + GDClaim child = (GDClaim) childClaim; + if (!child.children.isEmpty()) { + GDClaim innerChild = findClaim(child, pos, playerData, useBorderBlockRadius); + if (innerChild != null) { + return innerChild; + } + } + // check if child has children (Town -> Basic -> Subdivision) + if (child.contains(pos, playerData, useBorderBlockRadius)) { + return child; + } + } + return claim; + } + return null; + } + + @Override + public List getClaimsByName(String name) { + List claimList = new ArrayList<>(); + for (Claim worldClaim : this.getWorldClaims()) { + Component claimName = worldClaim.getName().orElse(null); + if (claimName != null && claimName != TextComponent.empty()) { + if (PlainComponentSerializer.INSTANCE.serialize(claimName).equalsIgnoreCase(name)) { + claimList.add(worldClaim); + } + } + // check children + for (Claim child : ((GDClaim) worldClaim).getChildren(true)) { + if (child.getUniqueId().toString().equals(name)) { + claimList.add(child); + } + } + } + return claimList; + } + + public void resetPlayerData() { + // check migration reset + if (GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetMigrations) { + for (GDPlayerData playerData : this.getPlayerDataMap().values()) { + final PlayerStorageData playerStorage = playerData.getStorageData(); + playerStorage.getConfig().setMigratedBlocks(false); + playerStorage.save(); + } + } + // migrate playerdata to new claim block system + final int migration3dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateVolumeRate; + final int migration2dRate = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.migrateAreaRate; + final boolean resetClaimBlockData = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.resetAccruedClaimBlocks; + + if (migration3dRate <= -1 && migration2dRate <= -1 && !resetClaimBlockData) { + return; + } + if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.VOLUME && migration2dRate >= 0) { + return; + } + if (GriefDefenderPlugin.CLAIM_BLOCK_SYSTEM == ClaimBlockSystem.AREA && migration3dRate >= 0) { + return; + } + + for (GDPlayerData playerData : this.getPlayerDataMap().values()) { + final PlayerStorageData playerStorage = playerData.getStorageData(); + final int accruedBlocks = playerStorage.getConfig().getAccruedClaimBlocks(); + int newAccruedBlocks = accruedBlocks; + // first check reset + if (resetClaimBlockData) { + newAccruedBlocks = playerData.getTotalClaimsCost(); + playerStorage.getConfig().setBonusClaimBlocks(0); + } else if (migration3dRate > -1 && !playerStorage.getConfig().hasMigratedBlocks()) { + newAccruedBlocks = accruedBlocks * migration3dRate; + playerStorage.getConfig().setMigratedBlocks(true); + } else if (migration2dRate > -1 && !playerStorage.getConfig().hasMigratedBlocks()) { + newAccruedBlocks = accruedBlocks / migration2dRate; + playerStorage.getConfig().setMigratedBlocks(true); + } + if (newAccruedBlocks < 0) { + newAccruedBlocks = 0; + } + final int maxAccruedBlocks = GDPermissionManager.getInstance().getGlobalInternalOptionValue(playerData.getSubject(), Options.MAX_ACCRUED_BLOCKS, playerData).intValue(); + if (newAccruedBlocks > maxAccruedBlocks) { + newAccruedBlocks = maxAccruedBlocks; + } + playerStorage.getConfig().setAccruedClaimBlocks(newAccruedBlocks); + playerStorage.save(); + } + } + + @Override + public UUID getWorldId() { + return this.worldUniqueId; + } + + public GDChunk getChunk(Chunk chunk) { + GDChunk gpChunk = this.chunksToGpChunks.get(getChunkKey(chunk)); + if (gpChunk == null) { + gpChunk = new GDChunk(chunk); + this.chunksToGpChunks.put(getChunkKey(chunk), gpChunk); + } + return gpChunk; + } + + public void removeChunk(Chunk chunk) { + this.chunksToGpChunks.remove(getChunkKey(chunk)); + } + + private long getChunkKey(Chunk chunk) { + return (long) chunk.getX() & 0xffffffffL | ((long) chunk.getZ() & 0xffffffffL) << 32; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDClaimResult.java b/bukkit/src/main/java/com/griefdefender/claim/GDClaimResult.java new file mode 100644 index 0000000..e1817ce --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDClaimResult.java @@ -0,0 +1,89 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import com.google.common.collect.ImmutableList; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.api.claim.ClaimResultType; +import net.kyori.text.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class GDClaimResult implements ClaimResult { + + private final Component eventMessage; + private final List claims; + private final ClaimResultType resultType; + + public GDClaimResult(ClaimResultType type) { + this(type, null); + } + + public GDClaimResult(ClaimResultType type, Component message) { + this.claims = ImmutableList.of(); + this.resultType = type; + this.eventMessage = message; + } + + public GDClaimResult(Claim claim, ClaimResultType type) { + this(claim, type, null); + } + + public GDClaimResult(Claim claim, ClaimResultType type, Component message) { + List claimList = new ArrayList<>(); + claimList.add(claim); + this.claims = ImmutableList.copyOf(claimList); + this.resultType = type; + this.eventMessage = message; + } + + public GDClaimResult(List claims, ClaimResultType type) { + this(claims, type, null); + } + + public GDClaimResult(List claims, ClaimResultType type, Component message) { + this.claims = ImmutableList.copyOf(claims); + this.resultType = type; + this.eventMessage = message; + } + + @Override + public ClaimResultType getResultType() { + return this.resultType; + } + + @Override + public Optional getMessage() { + return Optional.ofNullable(this.eventMessage); + } + + @Override + public List getClaims() { + return this.claims; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDClaimType.java b/bukkit/src/main/java/com/griefdefender/claim/GDClaimType.java new file mode 100644 index 0000000..fbfc34d --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDClaimType.java @@ -0,0 +1,92 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import com.griefdefender.api.claim.ClaimContexts; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.permission.Context; + +public class GDClaimType implements ClaimType { + + private final String id; + private final String name; + private final Context context; + private final Context defaultContext; + private final Context overrideContext; + + public GDClaimType(String id, String name) { + this.id = id; + this.name = name; + this.context = new Context("gd_claim_type", name.toLowerCase()); + if (name.equalsIgnoreCase("any")) { + this.defaultContext = ClaimContexts.GLOBAL_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.GLOBAL_OVERRIDE_CONTEXT; + } else if (name.equalsIgnoreCase("admin")) { + this.defaultContext = ClaimContexts.ADMIN_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.ADMIN_OVERRIDE_CONTEXT; + } else if (name.equalsIgnoreCase("basic")) { + this.defaultContext = ClaimContexts.BASIC_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.BASIC_OVERRIDE_CONTEXT; + } else if (name.equalsIgnoreCase("subdivision")) { + this.defaultContext = ClaimContexts.SUBDIVISION_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.SUBDIVISION_OVERRIDE_CONTEXT; + } else if (name.equalsIgnoreCase("town")) { + this.defaultContext = ClaimContexts.TOWN_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.TOWN_OVERRIDE_CONTEXT; + } else { + this.defaultContext = ClaimContexts.WILDERNESS_DEFAULT_CONTEXT; + this.overrideContext = ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT; + } + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getName() { + return this.name; + } + + public String toString() { + return this.name; + } + + @Override + public Context getContext() { + return this.context; + } + + @Override + public Context getDefaultContext() { + return this.defaultContext; + } + + @Override + public Context getOverrideContext() { + return this.overrideContext; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDShovelType.java b/bukkit/src/main/java/com/griefdefender/claim/GDShovelType.java new file mode 100644 index 0000000..521a1d2 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDShovelType.java @@ -0,0 +1,49 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import com.griefdefender.api.claim.ShovelType; + +public class GDShovelType implements ShovelType { + + private final String id; + private final String name; + + public GDShovelType(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getName() { + return this.name; + } + +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDTown.java b/bukkit/src/main/java/com/griefdefender/claim/GDTown.java new file mode 100644 index 0000000..f450a1c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDTown.java @@ -0,0 +1,21 @@ +package com.griefdefender.claim; + +import com.flowpowered.math.vector.Vector3i; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.Town; +import com.griefdefender.api.data.TownData; +import org.bukkit.World; + +import java.util.UUID; + +public class GDTown extends GDClaim implements Town { + + public GDTown(World world, Vector3i point1, Vector3i point2, ClaimType type, UUID ownerUniqueId, boolean cuboid) { + super(world, point1, point2, type, ownerUniqueId, cuboid); + } + + @Override + public TownData getData() { + return (TownData) this.claimData; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/claim/GDTrustType.java b/bukkit/src/main/java/com/griefdefender/claim/GDTrustType.java new file mode 100644 index 0000000..c5c9ebc --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/claim/GDTrustType.java @@ -0,0 +1,53 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.claim; + +import com.griefdefender.api.claim.TrustType; + +public class GDTrustType implements TrustType { + + private final String id; + private final String name; + + public GDTrustType(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String toString() { + return this.id + ":" + this.name; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/ClaimFlagBase.java b/bukkit/src/main/java/com/griefdefender/command/ClaimFlagBase.java new file mode 100644 index 0000000..649bd01 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/ClaimFlagBase.java @@ -0,0 +1,998 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.InvalidCommandArgument; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.collect.Maps; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.Tristate; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimContexts; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.api.permission.Context; +import com.griefdefender.api.permission.PermissionResult; +import com.griefdefender.api.permission.flag.Flag; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDFlagClaimEvent; +import com.griefdefender.internal.pagination.PaginationList; +import com.griefdefender.internal.util.NMSUtil; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.registry.FlagRegistryModule; +import com.griefdefender.storage.BaseStorage; +import com.griefdefender.text.action.GDCallbackHolder; +import com.griefdefender.util.CauseContextHelper; +import com.griefdefender.util.ClaimClickData; +import com.griefdefender.util.PaginationUtil; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.event.HoverEvent; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public abstract class ClaimFlagBase extends BaseCommand { + + public enum FlagType { + DEFAULT, + CLAIM, + OVERRIDE, + INHERIT, + GROUP, + PLAYER + } + + protected ClaimSubjectType subjectType; + protected GDPermissionHolder subject; + protected String friendlySubjectName; + protected boolean isAdmin = false; + protected GDPlayerData sourcePlayerData; + private final Cache lastActiveFlagTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES) + .build(); + + protected ClaimFlagBase(ClaimSubjectType type) { + this.subjectType = type; + } + + public void execute(Player player, String[] args) throws InvalidCommandArgument { + String commandFlag = null; + String target = null; + String value = null; + String contexts = null; + final String arguments = String.join(" ", args); + int index = arguments.indexOf("context["); + if (index != -1) { + contexts = arguments.substring(index, arguments.length()); + } + if (args.length > 0) { + if (args.length < 3) { + throw new InvalidCommandArgument(); + } + commandFlag = args[0]; + target = args[1]; + value = args[2]; + } + final Flag flag = FlagRegistryModule.getInstance().getById(commandFlag).orElse(null); + if (commandFlag != null && flag == null) { + TextAdapter.sendComponent(player, TextComponent.of("Flag not found.", TextColor.RED)); + return; + } + + if (flag != null && !player.hasPermission(GDPermissions.USER_CLAIM_FLAGS + flag.getPermission().replace(GDPermissions.FLAG_BASE, ""))) { + TextAdapter.sendComponent(player, TextComponent.of("You do not have permission to change this flag.", TextColor.RED)); + return; + } + + if (target != null && target.equalsIgnoreCase("hand")) { + ItemStack stack = NMSUtil.getInstance().getActiveItem(player); + if (stack != null) { + target = GDPermissionManager.getInstance().getPermissionIdentifier(stack); + } + } + + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final Claim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final Set contextSet = CauseContextHelper.generateContexts(player, claim, contexts); + if (contextSet == null) { + return; + } + + if (claim != null) { + if (flag == null && value == null && player.hasPermission(GDPermissions.COMMAND_LIST_CLAIM_FLAGS)) { + showFlagPermissions(player, (GDClaim) claim, FlagType.CLAIM); + return; + } else if (flag != null && value != null) { + // if (context != null) { + /*claimContext = CommandHelper.validateCustomContext(src, claim, context); + if (claimContext == null) { + final Text message = GriefDefenderPlugin.getInstance().messageData.flagInvalidContext + .apply(ImmutableMap.of( + "context", context, + "flag", flag)).build(); + GriefDefenderPlugin.sendMessage(src, message); + return CommandResult.success(); + }*/ + //} + + GDCauseStackManager.getInstance().pushCause(player); + ((GDPermissionManager) GriefDefender.getPermissionManager()).setPermission(claim, this.subject, this.friendlySubjectName, flag, target, PermissionUtil.getInstance().getTristateFromString(value.toUpperCase()), contextSet); + GDCauseStackManager.getInstance().popCause(); + return; + } + + GriefDefenderPlugin.sendMessage(player, TextComponent.of("Usage: /cf [ [subject|context]]")); + } else { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimNotFound.toText()); + } + } + + protected void showFlagPermissions(CommandSender src, GDClaim claim, FlagType flagType) { + boolean isAdmin = false; + if (src.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) { + isAdmin = true; + } + if (src instanceof Player) { + final Player player = (Player) src; + final FlagType lastFlagType = this.lastActiveFlagTypeMap.getIfPresent(player.getUniqueId()); + if (lastFlagType != null && lastFlagType != flagType) { + PaginationUtil.getInstance().resetActivePage(player.getUniqueId()); + } + } + final Component whiteOpenBracket = TextComponent.of("[", TextColor.AQUA); + final Component whiteCloseBracket = TextComponent.of("]", TextColor.AQUA); + final Component showOverrideText = TextComponent.builder("") + .append("Click here to filter by ") + .append("OVERRIDE ", TextColor.RED) + .append("permissions.").build(); + final Component showDefaultText = TextComponent.builder("") + .append("Click here to filter by ") + .append("DEFAULT ", TextColor.LIGHT_PURPLE) + .append("permissions.").build(); + final Component showClaimText = TextComponent.builder("") + .append("Click here to filter by ") + .append("CLAIM ", TextColor.GOLD) + .append("permissions.").build(); + final Component showInheritText = TextComponent.builder("") + .append("Click here to filter by ") + .append("INHERIT ", TextColor.AQUA) + .append("permissions.").build(); + Component defaultFlagText = TextComponent.empty(); + if (isAdmin) { + defaultFlagText = TextComponent.builder("") + .append(flagType == FlagType.DEFAULT ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("DEFAULT", TextColor.LIGHT_PURPLE) + .append(whiteCloseBracket).build() : TextComponent.of("DEFAULT", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, FlagType.DEFAULT)))) + .hoverEvent(HoverEvent.showText(showDefaultText)).build(); + } + final Component overrideFlagText = TextComponent.builder("") + .append(flagType == FlagType.OVERRIDE ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("OVERRIDE", TextColor.RED) + .append(whiteCloseBracket).build() : TextComponent.of("OVERRIDE", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, FlagType.OVERRIDE)))) + .hoverEvent(HoverEvent.showText(showOverrideText)).build(); + final Component claimFlagText = TextComponent.builder("") + .append(flagType == FlagType.CLAIM ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("CLAIM", TextColor.YELLOW) + .append(whiteCloseBracket).build() : TextComponent.of("CLAIM", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, FlagType.CLAIM)))) + .hoverEvent(HoverEvent.showText(showClaimText)).build(); + final Component inheritFlagText = TextComponent.builder("") + .append(flagType == FlagType.INHERIT ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("INHERIT", TextColor.AQUA) + .append(whiteCloseBracket).build() : TextComponent.of("INHERIT", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimFlagConsumer(src, claim, FlagType.INHERIT)))) + .hoverEvent(HoverEvent.showText(showInheritText)).build(); + Component claimFlagHead = TextComponent.empty(); + if (this.subjectType == ClaimSubjectType.GLOBAL) { + if (isAdmin) { + claimFlagHead = TextComponent.builder("") + .append(" Displaying : ", TextColor.AQUA) + .append(defaultFlagText) + .append(" ") + .append(claimFlagText) + .append(" ") + .append(inheritFlagText) + .append(" ") + .append(overrideFlagText).build(); + } else { + claimFlagHead = TextComponent.builder("") + .append(" Displaying : ", TextColor.AQUA) + .append(claimFlagText) + .append(" ") + .append(inheritFlagText) + .append(" ") + .append(overrideFlagText).build(); + } + } else { + claimFlagHead = TextComponent.builder("") + .append(" " + this.subjectType.getFriendlyName() + " ", TextColor.AQUA) + .append(this.friendlySubjectName, TextColor.YELLOW) + .append(" : ", TextColor.AQUA) + .append(claimFlagText) + .append(" ") + .append(inheritFlagText) + .append(" ") + .append(overrideFlagText).build(); + } + Map, Map> contextMap = new HashMap<>(); + List textList = new ArrayList<>(); + Set defaultContexts = new HashSet<>(); + Set overrideContexts = new HashSet<>(); + //contexts.add(claim.world.getContext()); + if (claim.isAdminClaim()) { + defaultContexts.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT); + overrideContexts.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT); + } else if (claim.isBasicClaim() || claim.isSubdivision()) { + defaultContexts.add(ClaimContexts.BASIC_DEFAULT_CONTEXT); + overrideContexts.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT); + } else if (claim.isTown()) { + defaultContexts.add(ClaimContexts.TOWN_DEFAULT_CONTEXT); + overrideContexts.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT); + } else { + defaultContexts.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT); + overrideContexts.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT); + } + defaultContexts.add(ClaimContexts.GLOBAL_DEFAULT_CONTEXT); + overrideContexts.add(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT); + overrideContexts.add(claim.getOverrideClaimContext()); + + Map, Map> defaultPermissionMap = new HashMap<>(); + Map, Map> defaultTransientPermissionMap = new HashMap<>(); + for (Map.Entry, Map> mapEntry : PermissionUtil.getInstance().getTransientPermissions(claim, this.subject).entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(claim.getDefaultTypeContext()) || contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) { + defaultTransientPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + } + + Map, Map> overridePermissionMap = new HashMap<>(); + Map, Map> claimPermissionMap = new HashMap<>(); + for (Map.Entry, Map> mapEntry : PermissionUtil.getInstance().getPermanentPermissions(claim, this.subject).entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(claim.getDefaultTypeContext())) { + defaultPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + if (contextSet.contains(claim.getContext())) { + claimPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + if (contextSet.contains(claim.getOverrideClaimContext())) { + overridePermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } else if (contextSet.contains(claim.getOverrideTypeContext())) { + overridePermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + if (contextSet.contains(ClaimContexts.GLOBAL_DEFAULT_CONTEXT)) { + defaultPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + if (contextSet.contains(ClaimContexts.GLOBAL_OVERRIDE_CONTEXT)) { + overridePermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + } + + Map, ClaimClickData> inheritPermissionMap = Maps.newHashMap(); + + final List inheritParents = claim.getInheritedParents(); + Collections.reverse(inheritParents); + for (Claim current : inheritParents) { + GDClaim currentClaim = (GDClaim) current; + for (Map.Entry, Map> mapEntry : PermissionUtil.getInstance().getPermanentPermissions(claim, this.subject).entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(currentClaim.getContext())) { + inheritPermissionMap.put(mapEntry.getKey(), new ClaimClickData(currentClaim, mapEntry.getValue())); + } + } + } + + final Component denyText = claim.allowEdit((Player) src); + final boolean hasPermission = denyText == null; + if (flagType == FlagType.CLAIM) { + final Map> permissionMap = new HashMap<>(); + for (Map.Entry, Map> mapEntry : defaultTransientPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Component flagText = null; + String flagPermission = permissionEntry.getKey(); + /*if (textMap.containsKey(flagPermission)) { + // only display flags not overridden + continue; + }*/ + + Boolean flagValue = permissionEntry.getValue(); + String baseFlagPerm = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + final Flag baseFlag = FlagRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (baseFlag == null) { + // invalid flag + continue; + } + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + Component undefinedText = null; + if (hasPermission) { + undefinedText = TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(baseFlagPerm) + .append(" is currently being ") + .append("overridden", TextColor.RED) + .append(" by an administrator.\nClick here to remove this flag.").build())) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, overrideContextSet, flagPermission, Tristate.UNDEFINED, flagType, FlagType.CLAIM, false)))).build(); + } else { + undefinedText = TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .hoverEvent(HoverEvent.showText(denyText)).build(); + } + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append("[", TextColor.AQUA) + .append(String.valueOf(overridePermissionEntry.getValue()), TextColor.RED) + .append("]", TextColor.AQUA) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append("This flag has been overridden by an administrator and can ") + .append(TextComponent.of("NOT").color(TextColor.RED).decoration(TextDecoration.UNDERLINED, true)) + .append(" be changed.").build())) + .build(); + break; + } + } + } + if (!hasOverride) { + // check if transient default has been overridden and if so display that value instead + final Set claimContexts = createClaimContextSet(claim, contextSet); + final Map subjectPerms = PermissionUtil.getInstance().getPermissions(this.subject, claimContexts); + Boolean overriddenValue = subjectPerms.get(flagPermission); + if (overriddenValue == null && this.subject != GriefDefenderPlugin.DEFAULT_HOLDER) { + // Check claim + final Map claimPerms = claimPermissionMap.get(claimContexts); + if (claimPerms != null) { + overriddenValue = claimPerms.get(flagPermission); + } + } + + final Tristate currentValue = overriddenValue == null ? Tristate.UNDEFINED : Tristate.fromBoolean(overriddenValue); + Component undefinedText = null; + if (hasPermission) { + undefinedText = TextComponent.builder("") + .append(currentValue == Tristate.UNDEFINED ? + TextComponent.builder("") + .append(whiteOpenBracket) + .append("undefined", TextColor.GOLD) + .append(whiteCloseBracket) + .build() : + TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .append(TextComponent.empty()) + .build()) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(baseFlagPerm) + .append(" is currently not set.\nThe default claim value of ") + .append(String.valueOf(flagValue), TextColor.LIGHT_PURPLE) + .append(" will be active until set.").build())) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, claimContexts, flagPermission, Tristate.UNDEFINED, flagType, FlagType.CLAIM, false)))).build(); + } else { + undefinedText = TextComponent.builder("").append( + TextComponent.builder("") + .append(currentValue == Tristate.UNDEFINED ? + TextComponent.builder("") + .append(whiteOpenBracket) + .append("undefined", TextColor.GOLD) + .append(whiteCloseBracket) + .build() : + TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .append(currentValue == Tristate.UNDEFINED ? whiteCloseBracket : TextComponent.empty()) + .build()) + .build()) + .hoverEvent(HoverEvent.showText(denyText)).build(); + } + + final Component trueText = TextComponent.builder("") + .append(getClickableText(src, claim, claimContexts, flagPermission, currentValue, Tristate.TRUE, flagType, FlagType.CLAIM, false).color(TextColor.GRAY)).build(); + final Component falseText = TextComponent.builder("") + .append(getClickableText(src, claim, claimContexts, flagPermission, currentValue, Tristate.FALSE, flagType, FlagType.CLAIM, false).color(TextColor.GRAY)).build(); + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append(trueText) + .append(" ") + .append(falseText).build(); + } + Component baseFlagText = getFlagText(defaultContexts, flagPermission, baseFlag.toString(), flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + textMap.put(flagPermission, flagText); + permissionMap.put(flagPermission, contextSet); + textList.add(flagText); + } + } + for (Map.Entry, Map> mapEntry : claimPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + final Flag claimFlag = FlagRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (claimFlag == null) { + // invalid flag + continue; + } + final String baseFlag = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + final Set contexts = permissionMap.get(flagPermission); + if (this.ignoreFlagEntry(contextSet)) { + continue; + } + + Boolean flagValue = permissionEntry.getValue(); + Component flagText = null; + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + Component undefinedText = null; + if (hasPermission) { + undefinedText = TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(baseFlag, TextColor.GREEN) + .append(" is currently being ") + .append("overridden", TextColor.RED) + .append(" by an administrator.\nClick here to remove this flag.").build())) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, overrideContextSet, flagPermission, Tristate.UNDEFINED, flagType, FlagType.CLAIM, false)))).build(); + } else { + undefinedText = TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .hoverEvent(HoverEvent.showText(denyText)).build(); + } + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append("[", TextColor.AQUA) + .append(String.valueOf(overridePermissionEntry.getValue()), TextColor.RED) + .append("]", TextColor.AQUA) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append("This flag has been overridden by an administrator and can ") + .append(TextComponent.of("NOT").color(TextColor.RED).decoration(TextDecoration.UNDERLINED, true)) + .append(" be changed.").build())) + .build(); + break; + } + } + } + if (!hasOverride) { + final Tristate currentValue = Tristate.fromBoolean(flagValue); + ClaimClickData claimClickData = inheritPermissionMap.get(flagPermission); + if (claimClickData != null) { + Set claimClickContexts = new HashSet<>(contextSet); + claimClickContexts.remove(claim.getContext()); + claimClickContexts.add(claimClickData.claim.getContext()); + final Component undefinedText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.UNDEFINED, flagType, FlagType.INHERIT, false); + final Component trueText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.TRUE, flagType, FlagType.INHERIT, false); + final Component falseText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.FALSE, flagType, FlagType.INHERIT, false); + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append(trueText) + .append(" ") + .append(falseText).build(); + } else { + final Component undefinedText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.UNDEFINED, flagType, FlagType.CLAIM, false); + final Component trueText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.TRUE, flagType, FlagType.CLAIM, false); + final Component falseText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.FALSE, flagType, FlagType.CLAIM, false); + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append(trueText) + .append(" ") + .append(falseText).build(); + } + } + + Component currentText = textMap.get(flagPermission); + if (currentText == null) { + currentText = TextComponent.builder("") + .append(baseFlag, TextColor.GREEN) + .append(" ") + .hoverEvent(HoverEvent.showText(CommandHelper.getBaseFlagOverlayText(baseFlag))).build(); + } + Component baseFlagText = getFlagText(defaultContexts, flagPermission, baseFlag.toString(), flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + textMap.put(flagPermission, flagText);//Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText))); + textList.add(flagText); + } + } + } else if (flagType == FlagType.OVERRIDE) { + for (Map.Entry, Map> mapEntry : overridePermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + Boolean flagValue = permissionEntry.getValue(); + Component flagText = TextComponent.builder("") + .append(getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), FlagType.OVERRIDE).color(TextColor.RED)).build(); + // Text currentText = textMap.get(flagPermission); + String baseFlag = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + //boolean customFlag = false; + //Text hover = CommandHelper.getBaseFlagOverlayText(baseFlag); + Component baseFlagText = getFlagText(contextSet, flagPermission, baseFlag, flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + /*if (claim.isWilderness()) { + Text reason = GriefDefenderPlugin.getGlobalConfig().getConfig().bans.getReason(baseFlag); + if (reason != null && !reason.isEmpty()) { + hover = Text.of(TextColors.GREEN, "Ban Reason", TextColors.WHITE, " : ", reason); + } + } + if (currentText == null) { + customFlag = true; + // custom flag + currentText = TextComponent.builder("").append(Text.of( + TextColors.GREEN, baseFlag, " ", + TextColors.WHITE, "[")) + .hoverEvent(HoverEvent.showText(hover)).build(); + } + final Text text = Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText, TextColors.WHITE, "]"));*/ + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + } else if (flagType == FlagType.INHERIT) { + for (Map.Entry, ClaimClickData> mapEntry : inheritPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + Map permissionMap = (Map) mapEntry.getValue().value; + for (Map.Entry permissionEntry : permissionMap.entrySet()) { + String flagPermission = permissionEntry.getKey(); + final String baseFlagPerm = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + final ClaimClickData claimClickData = mapEntry.getValue(); + Component flagText = null; + final Flag baseFlag = FlagRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (baseFlag == null) { + // invalid flag + continue; + } + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + final Component undefinedText = TextComponent.builder("") + .append("undefined", TextColor.GRAY) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(baseFlagPerm, TextColor.GREEN) + .append(" is currently being ") + .append("overridden", TextColor.RED) + .append(" by an administrator.\nClick here to remove this flag.").build())) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, overrideContextSet, flagPermission, Tristate.UNDEFINED, flagType, FlagType.CLAIM, false)))).build(); + flagText = TextComponent.builder("") + .append(undefinedText) + .append(" ") + .append("[", TextColor.AQUA) + .append(String.valueOf(overridePermissionEntry.getValue())) + .append("]", TextColor.AQUA) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append("This flag has been overridden by an administrator and can ") + .append(TextComponent.of("NOT").color(TextColor.RED).decoration(TextDecoration.UNDERLINED, true)) + .append(" be changed.").build())) + .build(); + break; + } + } + } + + if (!hasOverride) { + flagText = TextComponent.builder("") + .append(getClickableText(src, claimClickData.claim, contextSet, flagPermission, Tristate.fromBoolean(permissionEntry.getValue()), FlagType.INHERIT).color(TextColor.AQUA)) + .build(); + } + + Component currentText = textMap.get(flagPermission); + if (currentText == null) { + currentText = TextComponent.builder("") + .append(baseFlagPerm, TextColor.GREEN) + .append(" ") + .hoverEvent(HoverEvent.showText(CommandHelper.getBaseFlagOverlayText(baseFlagPerm))).build(); + } + Component baseFlagText = getFlagText(defaultContexts, flagPermission, baseFlag.toString(), flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + } else if (flagType == FlagType.DEFAULT) { + final Map> permissionMap = new HashMap<>(); + for (Map.Entry, Map> mapEntry : defaultTransientPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Component flagText = null; + String flagPermission = permissionEntry.getKey(); + Boolean flagValue = permissionEntry.getValue(); + String baseFlagPerm = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + final Flag baseFlag = FlagRegistryModule.getInstance().getById(baseFlagPerm).orElse(null); + if (baseFlag == null) { + continue; + } + + // check if transient default has been overridden and if so display that value instead + Tristate actualValue = PermissionUtil.getInstance().getPermissionValue(claim, this.subject, flagPermission, contextSet); + + final Component trueText = getClickableText(src, claim, contextSet, flagPermission, actualValue, Tristate.TRUE, flagType, FlagType.DEFAULT, false).color(TextColor.GRAY); + final Component falseText = getClickableText(src, claim, contextSet, flagPermission, actualValue, Tristate.FALSE, flagType, FlagType.DEFAULT, false).color(TextColor.GRAY); + + flagText = TextComponent.builder("") + .append(trueText) + .append(" ") + .append(falseText).build(); + Component baseFlagText = getFlagText(defaultContexts, flagPermission, baseFlag.toString(), flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + textMap.put(flagPermission, flagText); + permissionMap.put(flagPermission, contextSet); + textList.add(flagText); + } + } + + // Handle custom defaults + for (Map.Entry, Map> mapEntry : defaultPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, textMap); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Component flagText = null; + String flagPermission = permissionEntry.getKey(); + final Flag claimFlag = FlagRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (claimFlag == null) { + // invalid flag + continue; + } + final String baseFlag = flagPermission.replace(GDPermissions.FLAG_BASE + ".", "").replace(".*", ""); + final Set contexts = permissionMap.get(flagPermission); + if (this.ignoreFlagEntry(contexts)) { + continue; + } + + Boolean flagValue = permissionEntry.getValue(); + + // check if transient default has been overridden and if so display that value instead + final Map subjectPerms = PermissionUtil.getInstance().getPermissions(this.subject, contextSet); + Boolean defaultTransientOverrideValue = subjectPerms.get(flagPermission); + if (defaultTransientOverrideValue != null) { + flagValue = defaultTransientOverrideValue; + } + + final Tristate currentValue = Tristate.fromBoolean(flagValue); + final Component trueText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.TRUE, flagType, FlagType.DEFAULT, false).color(TextColor.GRAY); + final Component falseText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.FALSE, flagType, FlagType.DEFAULT, false).color(TextColor.GRAY); + final Component undefinedText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.UNDEFINED, flagType, FlagType.DEFAULT, false).color(TextColor.GRAY); + flagText = TextComponent.builder("") + .append(trueText) + .append(" ") + .append(falseText) + .append(" ") + .append(undefinedText).build(); + Component baseFlagText = getFlagText(defaultContexts, flagPermission, baseFlag.toString(), flagType); + flagText = TextComponent.builder("") + .append(baseFlagText) + .append(" ") + .append(flagText).build(); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + } + + //List textList = new ArrayList<>(contextMap.values()); + Collections.sort(textList, CommandHelper.PLAIN_COMPARATOR); + int fillSize = 20 - (textList.size() + 2); + for (int i = 0; i < fillSize; i++) { + textList.add(TextComponent.of(" ")); + } + + //PaginationList.Builder paginationBuilder = paginationService.builder() + // .title(claimFlagHead).padding(Text.of(TextStyles.STRIKETHROUGH,"-")).contents(textList); + //final PaginationList paginationList = paginationBuilder.build(); + PaginationList.Builder paginationBuilder = PaginationList.builder() + .title(claimFlagHead).padding(TextComponent.builder(" ").decoration(TextDecoration.STRIKETHROUGH, true).build()).contents(textList); + final PaginationList paginationList = paginationBuilder.build(); + Integer activePage = 1; + if (src instanceof Player) { + final Player player = (Player) src; + activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId()); + if (activePage == null) { + activePage = 1; + } + this.lastActiveFlagTypeMap.put(player.getUniqueId(), flagType); + } + paginationList.sendTo(src, activePage); + } + + private static Set createClaimContextSet(GDClaim claim, Set contexts) { + Set claimContexts = new HashSet<>(); + claimContexts.add(claim.getContext()); + for (Context context : contexts) { + if (context.getKey().contains("world") || context.getKey().contains("gd_claim")) { + continue; + } + claimContexts.add(context); + } + return claimContexts; + } + + private static Component getFlagText(Set contexts, String flagPermission, String baseFlag, FlagType type) { + //final String flagTarget = GPPermissionHandler.getTargetPermission(flagPermission); + TextComponent.Builder builder = TextComponent.builder(""); + boolean customContext = false; + for (Context context : contexts) { + if (type != FlagType.INHERIT && type != FlagType.OVERRIDE && (context.getKey().contains("gd_") || context.getKey().contains("world"))) { + continue; + } + + customContext = true; + builder.append(context.getKey() + "=", TextColor.GREEN).append(context.getValue() + "\n").build(); + } + + final Component hoverText = builder.build(); + final Component baseFlagText = TextComponent.builder("").color(customContext ? TextColor.YELLOW : TextColor.GREEN).append(baseFlag.toString() + " ") + .hoverEvent(HoverEvent.showText(customContext ? hoverText : CommandHelper.getBaseFlagOverlayText(baseFlag))).build(); + return baseFlagText; + } + + private Consumer createFlagConsumer(CommandSender src, GDClaim claim, Set contexts, String flagPermission, Tristate flagValue, FlagType displayType, FlagType flagType, boolean toggleType) { + return consumer -> { + // Toggle DEFAULT type + final String targetId = GDPermissionManager.getInstance().getTargetPermission(flagPermission); + final Flag claimFlag = FlagRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (claimFlag == null) { + return; + } + //Context claimContext = claim.getContext(); + Tristate newValue = Tristate.UNDEFINED; + if (flagType == FlagType.DEFAULT) { + if (toggleType) { + if (flagValue == Tristate.TRUE) { + newValue = Tristate.FALSE; + } else { + newValue = Tristate.TRUE; + } + ClaimType claimType = claim.getType(); + if (claimType == ClaimTypes.SUBDIVISION) { + claimType = ClaimTypes.BASIC; + } + final Boolean defaultValue = BaseStorage.CLAIM_FLAG_DEFAULTS.get(claimType).get(claimFlag.getName()); + if (defaultValue != null && defaultValue == newValue.asBoolean()) { + newValue = Tristate.UNDEFINED; + } + } + //claimContext = CommandHelper.validateCustomContext(src, claim, "default"); + // Toggle CLAIM type + } else if (flagType == FlagType.CLAIM) { + if (flagValue == Tristate.TRUE) { + newValue = Tristate.FALSE; + } else if (flagValue == Tristate.UNDEFINED) { + newValue = Tristate.TRUE; + } + // Toggle OVERRIDE type + } else if (flagType == FlagType.OVERRIDE) { + if (flagValue == Tristate.TRUE) { + newValue = Tristate.FALSE; + } else if (flagValue == Tristate.UNDEFINED) { + newValue = Tristate.TRUE; + } + } + + GDCauseStackManager.getInstance().pushCause(src); + GDFlagClaimEvent.Set event = new GDFlagClaimEvent.Set(claim, this.subject, claimFlag, targetId, toggleType ? newValue :flagValue, contexts); + GriefDefender.getEventManager().post(event); + GDCauseStackManager.getInstance().popCause(); + if (event.cancelled()) { + return; + } + PermissionResult result = CommandHelper.applyFlagPermission(src, this.subject, "ALL", claim, flagPermission, "any", toggleType ? newValue : flagValue, contexts, flagType, true); + if (result.successful()) { + showFlagPermissions(src, claim, displayType); + } + }; + } + + private Component getClickableText(CommandSender src, GDClaim claim, Set contexts, String flagPermission, Tristate flagValue, FlagType flagType) { + return getClickableText(src, claim, contexts, flagPermission, Tristate.UNDEFINED, flagValue, FlagType.CLAIM, flagType, true); + } + + private Component getClickableText(CommandSender src, GDClaim claim, Set contexts, String flagPermission, Tristate currentValue, Tristate flagValue, FlagType displayType, FlagType flagType, boolean toggleType) { + Component hoverEventText = TextComponent.of("Click here to toggle " + flagType.name().toLowerCase() + " value."); + if (!toggleType) { + if (flagValue == Tristate.TRUE) { + hoverEventText = TextComponent.of("Click here to allow this flag."); + } else if (flagValue == Tristate.FALSE) { + hoverEventText = TextComponent.of("Click here to deny this flag."); + } else { + hoverEventText = TextComponent.of("Click here to remove this flag."); + } + } + TextColor flagColor = TextColor.GOLD; + boolean hasPermission = true; + if (flagType == FlagType.DEFAULT) { + flagColor = TextColor.LIGHT_PURPLE; + if (!src.hasPermission(GDPermissions.MANAGE_FLAG_DEFAULTS)) { + hoverEventText = TextComponent.of("You do not have permission to change flag defaults.").color(TextColor.RED); + hasPermission = false; + } + } + if (flagType == FlagType.OVERRIDE) { + flagColor = TextColor.RED; + if (!src.hasPermission(GDPermissions.MANAGE_FLAG_OVERRIDES)) { + hoverEventText = TextComponent.of("This flag has been forced by an admin and cannot be changed.").color(TextColor.RED); + hasPermission = false; + } + } else if (flagType == FlagType.INHERIT) { + flagColor = TextColor.AQUA; + hoverEventText = TextComponent.builder("This flag is inherited from parent claim ") + .append(claim.getName().orElse(claim.getFriendlyNameType())) + .append(" and ") + .append(TextComponent.of("cannot").decoration(TextDecoration.UNDERLINED, true)) + .append(" be changed.").build(); + hasPermission = false; + } else if (src instanceof Player) { + Component denyReason = claim.allowEdit((Player) src); + if (denyReason != null) { + hoverEventText = denyReason; + hasPermission = false; + } else { + // check flag perm + if (!src.hasPermission(GDPermissions.USER_CLAIM_FLAGS + flagPermission.replace(GDPermissions.FLAG_BASE, ""))) { + hoverEventText = TextComponent.of("You do not have permission to change this flag.").color(TextColor.RED); + hasPermission = false; + } + } + } + + if (toggleType) { + TextComponent.Builder textBuilder = TextComponent.builder("") + .append(flagValue.toString().toLowerCase()) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(hoverEventText) + .append("\n") + .append(CommandHelper.getFlagTypeHoverText(flagType)).build())); + if (hasPermission) { + textBuilder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, contexts, flagPermission, flagValue, displayType, flagType, true)))); + } + return textBuilder.build(); + } + + TextComponent.Builder textBuilder = TextComponent.builder("") + .append(flagValue.toString().toLowerCase()) + .hoverEvent(HoverEvent.showText(TextComponent.builder("") + .append(hoverEventText) + .append("\n") + .append(CommandHelper.getFlagTypeHoverText(flagType)).build())); + if (hasPermission) { + textBuilder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createFlagConsumer(src, claim, contexts, flagPermission, flagValue, displayType, flagType, false)))); + } + Component result = textBuilder.build(); + if (currentValue == flagValue) { + final Component whiteOpenBracket = TextComponent.of("[", TextColor.AQUA); + final Component whiteCloseBracket = TextComponent.of("]", TextColor.AQUA); + result = TextComponent.builder("") + .append(whiteOpenBracket) + .append(result.color(flagColor)) + .append(whiteCloseBracket).build(); + } else { + result = result.color(TextColor.GRAY); + } + return result; + } + + private Consumer createClaimFlagConsumer(CommandSender src, GDClaim claim, FlagType flagType) { + return consumer -> { + showFlagPermissions(src, claim, flagType); + }; + } + + private boolean ignoreFlagEntry(Set contexts) { + if (contexts == null) { + return false; + } + + for (Context context : contexts) { + if (!context.getKey().startsWith("gd_")) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/command/ClaimOptionBase.java b/bukkit/src/main/java/com/griefdefender/command/ClaimOptionBase.java new file mode 100644 index 0000000..988f175 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/ClaimOptionBase.java @@ -0,0 +1,791 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.griefdefender.GDPlayerData; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.permission.option.Option; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDOptionEvent; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.registry.OptionRegistryModule; + +import org.bukkit.command.CommandSender; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class ClaimOptionBase { + + public enum OptionType { + ALL, + DEFAULT, + CLAIM, + //OVERRIDE, + INHERIT, + GROUP, + PLAYER + } + + protected ClaimSubjectType subjectType; + protected GDPermissionHolder subject; + protected String friendlySubjectName; + protected boolean isAdmin = false; + protected GDPlayerData sourcePlayerData; + private final Cache lastActiveFlagTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES) + .build(); + + protected ClaimOptionBase(ClaimSubjectType type) { + this.subjectType = type; + } + + /*protected void showOptions(CommandSource src, GPClaim claim, OptionType optionType) { + + boolean isAdmin = false; + if (src.hasPermission(GPPermissions.DELETE_CLAIM_ADMIN)) { + isAdmin = true; + } + if (src instanceof Player) { + final Player player = (Player) src; + final OptionType lastFlagType = this.lastActiveFlagTypeMap.getIfPresent(player.getUniqueId()); + if (lastFlagType != null && lastFlagType != optionType) { + PaginationUtils.resetActivePage(player.getUniqueId()); + } + } + final Text whiteOpenBracket = Text.of(TextColors.AQUA, "["); + final Text whiteCloseBracket = Text.of(TextColors.AQUA, "]"); + final Text showAllText = Text.of("Click here to show all options for claim."); + //final Text showOverrideText = Text.of("Click here to filter by ", TextColors.RED, "OVERRIDE ", TextColors.RESET, "permissions."); + final Text showDefaultText = Text.of("Click here to filter by ", TextColors.LIGHT_PURPLE, "DEFAULT ", TextColors.RESET, "options."); + final Text showClaimText = Text.of("Click here to filter by ", TextColors.GOLD, "CLAIM ", TextColors.RESET, "options."); + final Text showInheritText = Text.of("Click here to filter by ", TextColors.AQUA, "INHERIT ", TextColors.RESET, "options."); + Text allTypeText = Text.EMPTY; + Text defaultFlagText = Text.EMPTY; + if (isAdmin) { + allTypeText = Text.builder() + .append(Text.of(optionType == OptionType.ALL ? Text.of(whiteOpenBracket, TextColors.GOLD, "ALL", whiteCloseBracket) : Text.of(TextColors.GRAY, "ALL"))) + .onClick(TextActions.executeCallback(createClaimOptionConsumer(src, claim, OptionType.ALL))) + .onHover(TextActions.showText(showAllText)).build(); + defaultFlagText = Text.builder() + .append(Text.of(optionType == OptionType.DEFAULT ? Text.of(whiteOpenBracket, TextColors.LIGHT_PURPLE, "DEFAULT", whiteCloseBracket) : Text.of(TextColors.GRAY, "DEFAULT"))) + .onClick(TextActions.executeCallback(createClaimOptionConsumer(src, claim, OptionType.DEFAULT))) + .onHover(TextActions.showText(showDefaultText)).build(); + } + // final Text overrideFlagText = Text.builder() + // .append(Text.of(flagType == OptionType.OVERRIDE ? Text.of(whiteOpenBracket, TextColors.RED, "OVERRIDE", whiteCloseBracket) : Text.of(TextColors.GRAY, "OVERRIDE"))) + // .onClick(TextActions.executeCallback(createClaimFlagConsumer(src, claim, OptionType.OVERRIDE))) + // .onHover(TextActions.showText(showOverrideText)).build(); + final Text claimFlagText = Text.builder() + .append(Text.of(optionType == OptionType.CLAIM ? Text.of(whiteOpenBracket, TextColors.YELLOW, "CLAIM", whiteCloseBracket) : Text.of(TextColors.GRAY, "CLAIM"))) + .onClick(TextActions.executeCallback(createClaimOptionConsumer(src, claim, OptionType.CLAIM))) + .onHover(TextActions.showText(showClaimText)).build(); + final Text inheritFlagText = Text.builder() + .append(Text.of(optionType == OptionType.INHERIT ? Text.of(whiteOpenBracket, TextColors.AQUA, "INHERIT", whiteCloseBracket) : Text.of(TextColors.GRAY, "INHERIT"))) + .onClick(TextActions.executeCallback(createClaimOptionConsumer(src, claim, OptionType.INHERIT))) + .onHover(TextActions.showText(showInheritText)).build(); + Text claimFlagHead = Text.of(); + if (this.subjectType == ClaimSubjectType.GLOBAL) { + if (isAdmin) { + claimFlagHead = Text.builder().append(Text.of( + TextColors.AQUA," Displaying : ", allTypeText, " ", defaultFlagText, " ", claimFlagText, " ", inheritFlagText)).build(); + } else { + claimFlagHead = Text.builder().append(Text.of( + TextColors.AQUA," Displaying : ", claimFlagText, " ", inheritFlagText)).build(); + } + } else { + claimFlagHead = Text.builder().append(Text.of( + TextColors.AQUA," ", this.subjectType.getFriendlyName(), " ", TextColors.YELLOW, this.friendlySubjectName, TextColors.AQUA, " : ", allTypeText, " ", claimFlagText, " ", inheritFlagText)).build(); + } + Map, Map> contextMap = new HashMap<>(); + List textList = new ArrayList<>(); + Set contexts = new HashSet<>(); + Set overrideContexts = new HashSet<>(); + contexts.add(claim.world.getContext()); + if (claim.isAdminClaim()) { + contexts.add(ClaimContexts.ADMIN_DEFAULT_CONTEXT); + //overrideContexts.add(ClaimContexts.ADMIN_OVERRIDE_CONTEXT); + //overrideContexts.add(claim.world.getContext()); + } else if (claim.isBasicClaim() || claim.isSubdivision()) { + contexts.add(ClaimContexts.BASIC_DEFAULT_CONTEXT); + //overrideContexts.add(ClaimContexts.BASIC_OVERRIDE_CONTEXT); + //overrideContexts.add(claim.world.getContext()); + } else if (claim.isTown()) { + contexts.add(ClaimContexts.TOWN_DEFAULT_CONTEXT); + //overrideContexts.add(ClaimContexts.TOWN_OVERRIDE_CONTEXT); + //overrideContexts.add(claim.world.getContext()); + } else { + contexts.add(ClaimContexts.WILDERNESS_DEFAULT_CONTEXT); + // overrideContexts.add(ClaimContexts.WILDERNESS_OVERRIDE_CONTEXT); + } + + Map, Map> defaultOptionMap = new HashMap<>(); + Map, Map> defaultTransientOptionMap = new HashMap<>(); + if (isAdmin) { + // defaultTransientPermissions = this.subject.getTransientSubjectData().getPermissions(contexts); + for (Map.Entry, Map> mapEntry : this.subject.getTransientSubjectData().getAllOptions().entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(claim.getDefaultContext()) { // && contextSet.contains(claim.world.getContext()) + defaultTransientOptionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + } + for (Map.Entry, Map> mapEntry : this.subject.getSubjectData().getAllOptions().entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(claim.getDefaultContext()) { // && contextSet.contains(claim.world.getContext()) + defaultOptionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + } + } else { + + } + + Map, Map> overridePermissionMap = new HashMap<>(); + // Map claimPermissions = new HashMap<>(); + Map, Map> claimPermissionMap = new HashMap<>(); + for (Map.Entry, Map> mapEntry : this.subject.getSubjectData().getAllOptions().entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(claim.getContext())) { + claimPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + if (contextSet.contains(claim.getOverrideContext()) { //&& contextSet.contains(claim.world.getContext()) + overridePermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + } + + Map, ClaimClickData> inheritPermissionMap = Maps.newHashMap(); + + final List inheritParents = claim.getInheritedParents(); + Collections.reverse(inheritParents); + for (Claim current : inheritParents) { + GPClaim currentClaim = (GPClaim) current; + for (Map.Entry, Map> mapEntry : this.subject.getSubjectData().getAllOptions().entrySet()) { + final Set contextSet = mapEntry.getKey(); + if (contextSet.contains(currentClaim.getContext())) { + if (this.subjectType == ClaimSubjectType.GLOBAL) { + //claimPermissions.put(mapEntry.getKey(), mapEntry.getValue()); + } else { + // subjectPermissionMap.put(mapEntry.getKey(), mapEntry.getValue()); + } + inheritPermissionMap.put(mapEntry.getKey(), new ClaimClickData(currentClaim, mapEntry.getValue())); + } + } + } + + final Text denyText = claim.allowEdit((Player) src); + final boolean hasPermission = denyText == null; + + if (optionType == OptionType.ALL) { + for (Map.Entry, Map> mapEntry : defaultTransientOptionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Text flagText = null; + String optionPermission = permissionEntry.getKey(); + String baseFlagPerm = optionPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + final ClaimOption baseOption = GPOptionHandler.getOptionFromPermission(optionPermission); + if (baseOption == null) { + // invalid flag + continue; + } + // check if transient default has been overridden and if so display that value instead + String flagValue = permissionEntry.getValue(); + final Map subjectPerms = this.subject.getSubjectData().getOptions(contextSet); + final String overriddenValue = subjectPerms.get(optionPermission); + if (overriddenValue != null) { + flagValue = overriddenValue; + } + + Text baseFlagText = getFlagText(contextSet, optionPermission, baseFlagPerm.toString()); + //Text baseFlagText = Text.builder().append(Text.of(TextColors.GREEN, baseFlagPerm)) + // .onHover(TextActions.showText(CommandHelper.getBaseFlagOverlayText(baseFlagPerm))).build(); + flagText = Text.of( + baseFlagText, " ", + TextColors.WHITE, "[", + TextColors.LIGHT_PURPLE, getClickableText(src, claim, contextSet, optionPermission, flagValue, OptionType.DEFAULT)); + + final Set claimContexts = createClaimContextSet(claim, contextSet); + final Map claimPermissions = claimPermissionMap.get(claimContexts); + String claimValue = claimPermissions == null ? null : claimPermissions.get(permissionEntry.getKey()); + final String claimFinalValue = claimValue == null ? "undefined" : claimValue; + //if (claimPermissions == null || claimPermissions.get(permissionEntry.getKey()) == null) { + flagText = Text.join(flagText, + Text.of( + TextColors.WHITE, ", ", + TextColors.GOLD, getClickableText(src, claim, claimContexts, optionPermission, claimFinalValue, OptionType.CLAIM))); + if (overridePermissionMap.get(optionPermission) == null) { + flagText = Text.join(flagText, Text.of(TextColors.WHITE, "]")); + } + //} + textMap.put(optionPermission, flagText); + textList.add(flagText); + } + } + + + + for (Map.Entry, Map> mapEntry : claimPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + final ClaimOption claimFlag = GPOptionHandler.getOptionFromPermission(flagPermission); + if (claimFlag == null) { + // invalid flag + continue; + } + final String baseFlag = flagPermission.replace("griefdefender.", ""); + if (claimFlag.toString().equalsIgnoreCase(baseFlag)) { + // All base flag permissions are handled above in transient logic + continue; + } + + String flagValue = permissionEntry.getValue(); + Text flagText = null; + ClaimClickData claimClickData = inheritPermissionMap.get(flagPermission); + if (claimClickData != null) { + flagText = Text.of(TextColors.AQUA, getClickableText(src, claimClickData.claim, contextSet, flagPermission, Tristate.fromBoolean((boolean) claimClickData.value), OptionType.INHERIT)); + } else { + flagText = Text.of(TextColors.GOLD, getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), OptionType.CLAIM)); + } + + Text currentText = textMap.get(flagPermission); + boolean customFlag = false; + if (currentText == null) { + customFlag = true; + // custom flag + Text baseFlagText = getFlagText(contextSet, flagPermission, baseFlag.toString()); + currentText = Text.builder().append(Text.of( + TextColors.GREEN, baseFlagText, " ", + TextColors.WHITE, "[")).build(); + //.onHover(TextActions.showText(CommandHelper.getBaseFlagOverlayText(baseFlagPerm))).build(); + + } + + if (overridePermissionMap.get(flagPermission) == null) { + final Text text = Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText, TextColors.WHITE, "]")); + textMap.put(flagPermission, text); + textList.add(text); + } else { + final Text text = Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText)); + textMap.put(flagPermission, text); + textList.add(text); + } + } + } + + for (Map.Entry, Map> mapEntry : overridePermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + Boolean flagValue = permissionEntry.getValue(); + Text flagText = Text.of(TextColors.RED, getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), OptionType.OVERRIDE)); + Text currentText = textMap.get(flagPermission); + boolean customFlag = false; + if (currentText == null) { + customFlag = true; + // custom flag + String baseFlagPerm = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + currentText = Text.builder().append(Text.of( + TextColors.GREEN, baseFlagPerm, " ", + TextColors.WHITE, "[")) + .onHover(TextActions.showText(CommandHelper.getBaseFlagOverlayText(baseFlagPerm))).build(); + } + final Text text = Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText, TextColors.WHITE, "]")); + textMap.put(flagPermission, text); + textList.add(text); + } + } + } else if (optionType == OptionType.CLAIM) { + for (Map.Entry, Map> mapEntry : defaultTransientOptionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Text flagText = null; + String flagPermission = permissionEntry.getKey(); + if (contextMap.containsKey(flagPermission)) { + // only display flags not overridden + continue; + } + + Boolean flagValue = permissionEntry.getValue(); + String baseFlagPerm = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + final ClaimFlag baseFlag = GPPermissionManager.getInstance().getFlagFromPermission(baseFlagPerm); + if (baseFlag == null) { + // invalid flag + continue; + } + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + flagText = Text.builder().append( + Text.of(TextColors.RED, mapEntry.getValue())) + .onHover(TextActions.showText(Text.of(TextColors.GREEN, baseFlagPerm, TextColors.WHITE, " is currently being ", TextColors.RED, "overridden", TextColors.WHITE, " by an administrator and can ", TextColors.RED, TextStyles.UNDERLINE, "NOT", TextStyles.RESET, TextColors.WHITE, " be changed."))) + .build(); + break; + } + } + } + if (!hasOverride) { + // check if transient default has been overridden and if so display that value instead + final Set claimContexts = createClaimContextSet(claim, contextSet); + final Map subjectPerms = this.subject.getSubjectData().getPermissions(claimContexts); + Boolean overriddenValue = subjectPerms.get(flagPermission); + if (overriddenValue == null && this.subject != GriefDefenderPlugin.GLOBAL_SUBJECT) { + // Check claim + final Map claimPerms = claimPermissionMap.get(claimContexts); + if (claimPerms != null) { + overriddenValue = claimPerms.get(flagPermission); + } + } + + final Tristate currentValue = overriddenValue == null ? Tristate.UNDEFINED : Tristate.fromBoolean(overriddenValue); + Text undefinedText = null; + if (hasPermission) { + undefinedText = Text.builder().append( + Text.of(currentValue == Tristate.UNDEFINED ? Text.of(whiteOpenBracket, TextColors.GOLD, "undefined") : Text.of(TextColors.GRAY, "undefined"), TextStyles.RESET, currentValue == Tristate.UNDEFINED ? whiteCloseBracket : "")) + .onHover(TextActions.showText(Text.of(TextColors.GREEN, baseFlagPerm, TextColors.WHITE, " is currently not set.\nThe default claim value of ", TextColors.LIGHT_PURPLE, flagValue, TextColors.WHITE, " will be active until set."))) + .onClick(TextActions.executeCallback(createFlagConsumer(src, claim, claimContexts, flagPermission, Tristate.UNDEFINED, optionType, OptionType.CLAIM, false))).build(); + } else { + undefinedText = Text.builder().append( + Text.of(currentValue == Tristate.UNDEFINED ? Text.of(whiteOpenBracket, TextColors.GOLD, "undefined") : Text.of(TextColors.GRAY, "undefined"), TextStyles.RESET, currentValue == Tristate.UNDEFINED ? whiteCloseBracket : "")) + .onHover(TextActions.showText(denyText)).build(); + } + + final Text trueText = Text.of(TextColors.GRAY, getClickableText(src, claim, claimContexts, flagPermission, currentValue, Tristate.TRUE, optionType, OptionType.CLAIM, false)); + final Text falseText = Text.of(TextColors.GRAY, getClickableText(src, claim, claimContexts, flagPermission, currentValue, Tristate.FALSE, optionType, OptionType.CLAIM, false)); + flagText = Text.of(undefinedText, " ", trueText, " ", falseText); + } + Text baseFlagText = getFlagText(contexts, flagPermission, baseFlag.toString()); + flagText = Text.of( + baseFlagText, " ", + flagText); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + for (Map.Entry, Map> mapEntry : claimPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + final ClaimFlag claimFlag = GPPermissionManager.getInstance().getFlagFromPermission(flagPermission); + if (claimFlag == null) { + // invalid flag + continue; + } + final String baseFlag = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + if (claimFlag.toString().equalsIgnoreCase(baseFlag)) { + // All base flag permissions are handled above in transient logic + continue; + } + + Boolean flagValue = permissionEntry.getValue(); + Text flagText = null; + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + Text undefinedText = null; + if (hasPermission) { + undefinedText = Text.builder().append( + Text.of(TextColors.GRAY, "undefined")) + .onHover(TextActions.showText(Text.of(TextColors.GREEN, baseFlag, TextColors.WHITE, " is currently being ", TextColors.RED, "overridden", TextColors.WHITE, " by an administrator", TextColors.WHITE, ".\nClick here to remove this flag."))) + .onClick(TextActions.executeCallback(createFlagConsumer(src, claim, overrideContextSet, flagPermission, Tristate.UNDEFINED, optionType, OptionType.CLAIM, false))).build(); + } else { + undefinedText = Text.builder().append( + Text.of(TextColors.GRAY, "undefined")) + .onHover(TextActions.showText(denyText)).build(); + } + flagText = Text.builder().append( + Text.of(undefinedText, " ", TextColors.AQUA, "[", TextColors.RED, mapEntry.getValue(), TextStyles.RESET, TextColors.AQUA, "]")) + .onHover(TextActions.showText(Text.of(TextColors.WHITE, "This flag has been overridden by an administrator and can ", TextColors.RED, TextStyles.UNDERLINE, "NOT", TextStyles.RESET, TextColors.WHITE, " be changed."))) + .build(); + break; + } + } + } + if (!hasOverride) { + final Tristate currentValue = Tristate.fromBoolean(flagValue); + ClaimClickData claimClickData = inheritPermissionMap.get(flagPermission); + if (claimClickData != null) { + Set claimClickContexts = new HashSet<>(contextSet); + claimClickContexts.remove(claim.getContext()); + claimClickContexts.add(claimClickData.claim.getContext()); + final Text undefinedText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.UNDEFINED, optionType, OptionType.INHERIT, false); + final Text trueText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.TRUE, optionType, OptionType.INHERIT, false); + final Text falseText = getClickableText(src, claimClickData.claim, claimClickContexts, flagPermission, currentValue, Tristate.FALSE, optionType, OptionType.INHERIT, false); + flagText = Text.of(undefinedText, " ", trueText, " ", falseText); + } else { + final Text undefinedText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.UNDEFINED, optionType, OptionType.CLAIM, false); + final Text trueText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.TRUE, optionType, OptionType.CLAIM, false); + final Text falseText = getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.FALSE, optionType, OptionType.CLAIM, false); + flagText = Text.of(undefinedText, " ", trueText, " ", falseText); + } + } + + Text currentText = textMap.get(flagPermission); + if (currentText == null) { + currentText = Text.builder().append(Text.of( + TextColors.GREEN, baseFlag, " ")) + //TextColors.WHITE, "[")) + .onHover(TextActions.showText(CommandHelper.getBaseFlagOverlayText(baseFlag))).build(); + } + Text baseFlagText = getFlagText(contexts, flagPermission, baseFlag.toString()); + flagText = Text.of( + baseFlagText, " ", + flagText); + textMap.put(flagPermission, flagText);//Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText))); + textList.add(flagText); + } + } + } else if (optionType == OptionType.OVERRIDE) { + for (Map.Entry, Map> mapEntry : overridePermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + String flagPermission = permissionEntry.getKey(); + Boolean flagValue = permissionEntry.getValue(); + Text flagText = Text.of(TextColors.RED, getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), OptionType.OVERRIDE)); + Text currentText = textMap.get(flagPermission); + String baseFlagPerm = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + boolean customFlag = false; + Text hover = CommandHelper.getBaseFlagOverlayText(baseFlagPerm); + if (claim.isWilderness()) { + Text reason = GriefDefenderPlugin.getGlobalConfig().getConfig().bans.getReason(baseFlagPerm); + if (reason != null && !reason.isEmpty()) { + hover = Text.of(TextColors.GREEN, "Ban Reason", TextColors.WHITE, " : ", reason); + } + } + if (currentText == null) { + customFlag = true; + // custom flag + currentText = Text.builder().append(Text.of( + TextColors.GREEN, baseFlagPerm, " ", + TextColors.WHITE, "[")) + .onHover(TextActions.showText(hover)).build(); + } + final Text text = Text.join(currentText, Text.of(customFlag ? "" : ", ", flagText, TextColors.WHITE, "]")); + textMap.put(flagPermission, text); + textList.add(text); + } + } + } else if (optionType == OptionType.INHERIT) { + for (Map.Entry, ClaimClickData> mapEntry : inheritPermissionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + Map permissionMap = (Map) mapEntry.getValue().value; + for (Map.Entry permissionEntry : permissionMap.entrySet()) { + String flagPermission = permissionEntry.getKey(); + final String baseFlagPerm = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + final ClaimClickData claimClickData = mapEntry.getValue(); + //final boolean flagValue = (boolean) claimClickData.value; + Text flagText = null; + final ClaimFlag baseFlag = GPPermissionManager.getInstance().getFlagFromPermission(flagPermission); + if (baseFlag == null) { + // invalid flag + continue; + } + + boolean hasOverride = false; + for (Map.Entry, Map> overrideEntry : overridePermissionMap.entrySet()) { + final Set overrideContextSet = overrideEntry.getKey(); + for (Map.Entry overridePermissionEntry : overrideEntry.getValue().entrySet()) { + if (flagPermission.contains(overridePermissionEntry.getKey())) { + hasOverride = true; + final Text undefinedText = Text.builder().append( + Text.of(TextColors.GRAY, "undefined")) + .onHover(TextActions.showText(Text.of(TextColors.GREEN, baseFlagPerm, TextColors.WHITE, " is currently being ", TextColors.RED, "overridden", TextColors.WHITE, " by an administrator", TextColors.WHITE, ".\nClick here to remove this flag."))) + .onClick(TextActions.executeCallback(createFlagConsumer(src, claim, overrideContextSet, flagPermission, Tristate.UNDEFINED, optionType, OptionType.CLAIM, false))).build(); + flagText = Text.builder().append( + Text.of(undefinedText, " ", TextColors.AQUA, "[", TextColors.RED, mapEntry.getValue(), TextStyles.RESET, TextColors.AQUA, "]")) + .onHover(TextActions.showText(Text.of(TextColors.WHITE, "This flag has been overridden by an administrator and can ", TextColors.RED, TextStyles.UNDERLINE, "NOT", TextStyles.RESET, TextColors.WHITE, " be changed."))) + .build(); + break; + } + } + } + + if (!hasOverride) { + //flagText = Text.of(TextColors.AQUA, getClickableText(src, claimClickData.claim, flagPermission, Tristate.fromBoolean(flagValue), FlagType.INHERIT)); + } + + Text currentText = textMap.get(flagPermission); + if (currentText == null) { + currentText = Text.builder().append(Text.of( + TextColors.GREEN, baseFlagPerm, " ")) + .onHover(TextActions.showText(CommandHelper.getBaseFlagOverlayText(baseFlagPerm))).build(); + } + Text baseFlagText = getFlagText(contexts, flagPermission, baseFlag.toString()); + flagText = Text.of( + baseFlagText, " ", + flagText); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + } else if (optionType == OptionType.DEFAULT) { + for (Map.Entry, Map> mapEntry : defaultTransientOptionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Text flagText = null; + String flagPermission = permissionEntry.getKey(); + Boolean flagValue = permissionEntry.getValue(); + String baseFlagPerm = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + if (!ClaimFlag.contains(baseFlagPerm)) { + continue; + } + final ClaimFlag baseFlag = ClaimFlag.getEnum(baseFlagPerm); + + // check if transient default has been overridden and if so display that value instead + final Map subjectPerms = this.subject.getSubjectData().getPermissions(contextSet); + Boolean defaultTransientOverrideValue = subjectPerms.get(flagPermission); + if (defaultTransientOverrideValue != null) { + flagValue = defaultTransientOverrideValue; + } + + final Text trueText = Text.of(TextColors.GRAY, getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), Tristate.TRUE, optionType, OptionType.DEFAULT, false)); + final Text falseText = Text.of(TextColors.GRAY, getClickableText(src, claim, contextSet, flagPermission, Tristate.fromBoolean(flagValue), Tristate.FALSE, optionType, OptionType.DEFAULT, false)); + flagText = Text.of(trueText, " ", falseText); + Text baseFlagText = getFlagText(contexts, flagPermission, baseFlag.toString()); + flagText = Text.of( + baseFlagText, " ", + flagText); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + + // Handle custom defaults + for (Map.Entry, Map> mapEntry : defaultOptionMap.entrySet()) { + final Set contextSet = mapEntry.getKey(); + Map textMap = contextMap.get(contextSet); + if (textMap == null) { + textMap = new HashMap(); + contextMap.put(contextSet, new HashMap()); + } + for (Map.Entry permissionEntry : mapEntry.getValue().entrySet()) { + Text flagText = null; + String flagPermission = permissionEntry.getKey(); + final ClaimFlag claimFlag = GPPermissionManager.getInstance().getFlagFromPermission(flagPermission); + if (claimFlag == null) { + // invalid flag + continue; + } + final String baseFlag = flagPermission.replace(GPPermissions.FLAG_BASE + ".", ""); + if (claimFlag.toString().equalsIgnoreCase(baseFlag)) { + // All base flag permissions are handled above in transient logic + continue; + } + + Boolean flagValue = permissionEntry.getValue(); + + // check if transient default has been overridden and if so display that value instead + final Map subjectPerms = this.subject.getSubjectData().getPermissions(contextSet); + Boolean defaultTransientOverrideValue = subjectPerms.get(flagPermission); + if (defaultTransientOverrideValue != null) { + flagValue = defaultTransientOverrideValue; + } + + final Tristate currentValue = Tristate.fromBoolean(flagValue); + final Text trueText = Text.of(TextColors.GRAY, getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.TRUE, optionType, OptionType.DEFAULT, false)); + final Text falseText = Text.of(TextColors.GRAY, getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.FALSE, optionType, OptionType.DEFAULT, false)); + final Text undefinedText = Text.of(TextColors.GRAY, getClickableText(src, claim, contextSet, flagPermission, currentValue, Tristate.UNDEFINED, optionType, OptionType.DEFAULT, false)); + flagText = Text.of(trueText, " ", falseText, " ", undefinedText); + Text baseFlagText = getFlagText(contexts, flagPermission, baseFlag.toString()); + flagText = Text.of( + baseFlagText, " ", + flagText); + textMap.put(flagPermission, flagText); + textList.add(flagText); + } + } + } + + //List textList = new ArrayList<>(contextMap.values()); + Collections.sort(textList); + int fillSize = 20 - (textList.size() + 2); + for (int i = 0; i < fillSize; i++) { + textList.add(Text.of(" ")); + } + PaginationService paginationService = Sponge.getServiceManager().provide(PaginationService.class).get(); + PaginationList.Builder paginationBuilder = paginationService.builder() + .title(claimFlagHead).padding(Text.of(TextStyles.STRIKETHROUGH,"-")).contents(textList); + final PaginationList paginationList = paginationBuilder.build(); + Integer activePage = 1; + if (src instanceof Player) { + final Player player = (Player) src; + activePage = PaginationUtils.getActivePage(player.getUniqueId()); + if (activePage == null) { + activePage = 1; + } + this.lastActiveFlagTypeMap.put(player.getUniqueId(), optionType); + } + paginationList.sendTo(src, activePage); + } + + private static Set createClaimContextSet(GPClaim claim, Set contexts) { + Set claimContexts = new HashSet<>(); + claimContexts.add(claim.getContext()); + for (Context context : contexts) { + if (context.getKey().contains("world") || context.getKey().contains("gd_claim")) { + continue; + } + claimContexts.add(context); + } + return claimContexts; + } + + private static Text getFlagText(Set contexts, String flagPermission, String baseFlag) { + //final String flagTarget = GPPermissionHandler.getTargetPermission(flagPermission); + Text.Builder builder = Text.builder(); + boolean customContext = false; + for (Context context : contexts) { + if (context.getKey().contains("gd_claim") || context.getKey().contains("world")) { + continue; + } + + customContext = true; + builder.append(Text.of(TextColors.WHITE, context.getKey() + "=", TextColors.GREEN, context.getValue(),"\n")); + } + //builder.append(Text.of("target=", TextColors.GREEN, flagTarget)); + final Text hoverText = builder.build(); + final Text baseFlagText = Text.builder().append(Text.of(customContext ? TextColors.YELLOW : TextColors.GREEN, baseFlag.toString(), " ")) + .onHover(TextActions.showText(customContext ? hoverText : CommandHelper.getBaseFlagOverlayText(baseFlag))).build(); + final Text baseText = Text.builder().append(Text.of( + baseFlagText)).build(); + //sourceText, + //targetText)).build(); + return baseText; + /*String flagSource = null; + String flagUsedItem = null; + for (Context context : contexts) { + if (context.getKey().equalsIgnoreCase("source")) { + flagSource = context.getValue(); + } + if (context.getKey().equalsIgnoreCase("used_item")) { + flagUsedItem = context.getValue(); + } + } + Text sourceText = flagSource == null ? null : Text.of(TextColors.WHITE, "source=",TextColors.GREEN, flagSource); + Text targetText = flagTarget == null ? null : Text.of(TextColors.WHITE, "target=",TextColors.GREEN, flagTarget); + Text usedItemText = flagUsedItem == null ? null : Text.of(TextColors.WHITE, "used_item=", TextColors.GREEN, flagUsedItem); + if (sourceText != null) { + sourceText = Text.of(sourceText, "\n"); + //} + } else { + sourceText = Text.of(); + } + if (targetText != null) { + targetText = Text.of(targetText); + } else { + targetText = Text.of(); + } + Text baseFlagText = Text.of(); + if (flagSource == null && flagTarget == null && flagUsedItem == null) { + baseFlagText = Text.builder().append(Text.of(TextColors.GREEN, baseFlag.toString(), " ")) + .onHover(TextActions.showText(Text.of(sourceText, targetText))).build(); + } else { + baseFlagText = Text.builder().append(Text.of(TextStyles.ITALIC, TextColors.YELLOW, baseFlag.toString(), " ", TextStyles.RESET)) + .onHover(TextActions.showText(Text.of(sourceText, usedItemText))).build(); + } + final Text baseText = Text.builder().append(Text.of( + baseFlagText)).build(); + //sourceText, + //targetText)).build(); + return baseText; + */ + //} + + private Consumer createFlagConsumer(CommandSender src, GDClaim claim, Set contexts, String flagPermission, String flagValue, OptionType displayType, OptionType flagType, boolean toggleType) { + return consumer -> { + // Toggle DEFAULT type + //final String targetId = GPPermissionManager.getInstance().getTargetPermission(flagPermission); + final Option claimOption = OptionRegistryModule.getInstance().getById(flagPermission).orElse(null); + if (claimOption == null) { + return; + } + //Context claimContext = claim.getContext(); + //final Set contexts = new HashSet<>(); + //contexts.add(claimContext); + GDCauseStackManager.getInstance().pushCause(src); + GDOptionEvent.Set event = new GDOptionEvent.Set(this.subject, claimOption, flagValue == null ? "undefined" : flagValue, contexts); + GriefDefender.getEventManager().post(event); + GDCauseStackManager.getInstance().popCause(); + if (event.cancelled()) { + return; + } + // TODO + //OptionResult result = CommandHelper.applyFlagPermission(src, this.subject, "ALL", claim, flagPermission, flagValue == null ? "undefined" : flagValue, contexts, flagType, true); + //if (result.successful()) { + // showOptions(src, claim, displayType); + //} + }; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/command/ClaimSubjectType.java b/bukkit/src/main/java/com/griefdefender/command/ClaimSubjectType.java new file mode 100644 index 0000000..af6eb97 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/ClaimSubjectType.java @@ -0,0 +1,42 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +public enum ClaimSubjectType { + + GLOBAL("Global"), + GROUP("Group"), + PLAYER("Player"); + + private String friendlyName; + + private ClaimSubjectType(String name) { + this.friendlyName = name; + } + + public String getFriendlyName() { + return this.friendlyName; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java b/bukkit/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java new file mode 100644 index 0000000..5ca3c1d --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandAdjustBonusClaimBlocks.java @@ -0,0 +1,93 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_SET_ACCRUED_CLAIM_BLOCKS) +public class CommandAdjustBonusClaimBlocks extends BaseCommand { + + @CommandAlias("acb|adjustclaimblocks") + @Description("Updates a player's accrued claim block total") + @Syntax(" ") + @Subcommand("player adjustbonusblocks") + public void execute(CommandSender src, OfflinePlayer user, int amount, @Optional String worldName) { + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + TextAdapter.sendComponent(src, TextComponent.of("This command is not available while server is in economy mode.", TextColor.RED)); + return; + } + + World world = worldName == null ? null : Bukkit.getServer().getWorld(worldName); + if (world == null) { + if (src instanceof Player) { + world = ((Player) src).getWorld(); + } else { + world = Bukkit.getServer().getWorlds().get(0); + } + } + if (world == null || !GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUID())) { + GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.claimDisabledWorld.toText()); + return; + } + + // parse the adjustment amount + int adjustment = amount; + //User user = args.getOne("user").get(); + + // give blocks to player + GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world.getUID(), user.getUniqueId()); + playerData.setBonusClaimBlocks(playerData.getBonusClaimBlocks() + adjustment); + playerData.getStorageData().save(); + final Component message = GriefDefenderPlugin.getInstance().messageData.adjustBlocksSuccess + .apply(ImmutableMap.of( + "player", user.getName(), + "adjustment", adjustment, + "total", playerData.getBonusClaimBlocks())).build(); + TextAdapter.sendComponent(src, message); + GriefDefenderPlugin.getInstance().getLogger().info( + src.getName() + " adjusted " + user.getName() + "'s bonus claim blocks by " + adjustment + "."); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandCallback.java b/bukkit/src/main/java/com/griefdefender/command/CommandCallback.java new file mode 100644 index 0000000..296c7b8 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandCallback.java @@ -0,0 +1,23 @@ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.Description; +import com.griefdefender.text.action.GDCallbackHolder; +import org.bukkit.command.CommandSender; + +import java.util.UUID; +import java.util.function.Consumer; + +public class CommandCallback extends BaseCommand { + + @CommandAlias("gp:callback") + @Description("Execute a callback registered as part of a Text object. Primarily for internal use") + public void execute(CommandSender src, String[] args) { + final UUID callbackId = UUID.fromString(args[0]); + Consumer callback = GDCallbackHolder.getInstance().getCallbackForUUID(callbackId).orElse(null); + if (callback != null) { + callback.accept(src); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandon.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandon.java new file mode 100644 index 0000000..0302f9c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandon.java @@ -0,0 +1,165 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.TrustTypes; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.claim.GDClaimManager; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDDeleteClaimEvent; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.entity.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_ABANDON_BASIC) +public class CommandClaimAbandon extends BaseCommand { + + protected boolean abandonTopClaim = false; + + @CommandAlias("abandonclaim") + @Description("Abandons a claim") + @Subcommand("abandon claim") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + final UUID ownerId = claim.getOwnerUniqueId(); + + final boolean isAdmin = playerData.canIgnoreClaim(claim); + final boolean isTown = claim.isTown(); + if (claim.isWilderness()) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.commandAbandonClaimMissing.toText()); + return; + } else if (!isAdmin && !player.getUniqueId().equals(ownerId) && claim.isUserTrusted(player, TrustTypes.MANAGER)) { + if (claim.parent == null) { + // Managers can only abandon child claims + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimNotYours.toText()); + return; + } + } else if (!isAdmin && (claim.allowEdit(player) != null || (!claim.isAdminClaim() && !player.getUniqueId().equals(ownerId)))) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimNotYours.toText()); + return; + } + + if (!claim.isTown() && !claim.isAdminClaim() && claim.children.size() > 0 && !this.abandonTopClaim) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.commandAbandonTopLevel.toText()); + return; + } else { + if (this.abandonTopClaim && (claim.isTown() || claim.isAdminClaim()) && claim.children.size() > 0) { + Set invalidClaims = new HashSet<>(); + for (Claim child : claim.getChildren(true)) { + if (child.getOwnerUniqueId() == null || !child.getOwnerUniqueId().equals(ownerId)) { + //return CommandResult.empty(); + invalidClaims.add(child); + } + } + + if (!invalidClaims.isEmpty()) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.commandAbandonTownChildren.toText()); + CommandHelper.showClaims(player, invalidClaims, 0, true); + return; + } + } + + GDCauseStackManager.getInstance().pushCause(player); + GDDeleteClaimEvent.Abandon event = new GDDeleteClaimEvent.Abandon(claim); + GriefDefender.getEventManager().post(event); + GDCauseStackManager.getInstance().popCause(); + if (event.cancelled()) { + TextAdapter.sendComponent(player, event.getMessage().orElse(TextComponent.of("Could not abandon claim. A plugin has denied it.").color(TextColor.RED))); + return; + } + + if (!claim.isSubdivision() && !claim.isAdminClaim()) { + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(player)) { + return; + } + } + } + + GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(player.getWorld().getUID()); + claimManager.deleteClaimInternal(claim, this.abandonTopClaim); + // remove all context permissions + PermissionUtil.getInstance().clearPermissions(player, claim.getContext()); + PermissionUtil.getInstance().clearPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, claim.getContext()); + + playerData.revertActiveVisual(player); + if (isTown) { + playerData.inTown = false; + playerData.townChat = false; + } + + if (!claim.isSubdivision() && !claim.isAdminClaim()) { + final double abandonReturnRatio = GDPermissionManager.getInstance().getInternalOptionValue(player, Options.ABANDON_RETURN_RATIO, claim, playerData); + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + final double requiredClaimBlocks = claim.getClaimBlocks() * abandonReturnRatio; + final double refund = requiredClaimBlocks * claim.getOwnerEconomyBlockCost(); + final EconomyResponse result = economy.depositPlayer(player, refund); + if (result.transactionSuccess()) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyClaimAbandonSuccess + .apply(ImmutableMap.of( + "refund", refund + )).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + } else { + int newAccruedClaimCount = playerData.getAccruedClaimBlocks() - ((int) Math.ceil(claim.getClaimBlocks() * (1 - abandonReturnRatio))); + playerData.setAccruedClaimBlocks(newAccruedClaimCount); + int remainingBlocks = playerData.getRemainingClaimBlocks(); + final Component message = GriefDefenderPlugin.getInstance().messageData.claimAbandonSuccess + .apply(ImmutableMap.of( + "remaining-blocks", remainingBlocks + )).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + } + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java new file mode 100644 index 0000000..9994e48 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonAll.java @@ -0,0 +1,123 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDDeleteClaimEvent; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_ABANDON_ALL_CLAIMS) +public class CommandClaimAbandonAll extends BaseCommand { + + @CommandAlias("abandonall") + @Description("Abandons ALL your claims") + @Subcommand("abandon all") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + int originalClaimCount = playerData.getInternalClaims().size(); + + if (originalClaimCount == 0) { + try { + throw new CommandException(GriefDefenderPlugin.getInstance().messageData.claimNoClaims.toText()); + } catch (CommandException e) { + TextAdapter.sendComponent(player, e.getText()); + return; + } + } + GDCauseStackManager.getInstance().pushCause(player); + GDDeleteClaimEvent.Abandon event = new GDDeleteClaimEvent.Abandon(ImmutableList.copyOf(playerData.getInternalClaims())); + GriefDefender.getEventManager().post(event); + GDCauseStackManager.getInstance().popCause(); + if (event.cancelled()) { + TextAdapter.sendComponent(player, event.getMessage().orElse(TextComponent.of("Could not abandon claim. A plugin has denied it.").color(TextColor.RED))); + return; + } + + double refund = 0; + // adjust claim blocks + for (Claim claim : playerData.getInternalClaims()) { + // remove all context permissions + PermissionUtil.getInstance().clearPermissions(player, claim.getContext()); + if (claim.isSubdivision() || claim.isAdminClaim() || claim.isWilderness()) { + continue; + } + final double abandonReturnRatio = GDPermissionManager.getInstance().getInternalOptionValue(player, Options.ABANDON_RETURN_RATIO, claim, playerData); + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + refund += claim.getClaimBlocks() * abandonReturnRatio; + } else { + playerData.setAccruedClaimBlocks(playerData.getAccruedClaimBlocks() - ((int) Math.ceil(claim.getClaimBlocks() * (1 - abandonReturnRatio)))); + } + } + + GriefDefenderPlugin.getInstance().dataStore.deleteClaimsForPlayer(player.getUniqueId()); + + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(player)) { + return; + } + + final EconomyResponse result = economy.depositPlayer(player, refund); + if (result.transactionSuccess()) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyClaimAbandonSuccess + .apply(ImmutableMap.of( + "refund", TextComponent.of(String.valueOf(refund)) + )).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + } else { + int remainingBlocks = playerData.getRemainingClaimBlocks(); + final Component message = GriefDefenderPlugin.getInstance().messageData.claimAbandonSuccess + .apply(ImmutableMap.of( + "remaining-blocks", remainingBlocks + )).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + + playerData.revertActiveVisual(player); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java new file mode 100644 index 0000000..6be45b8 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAbandonTop.java @@ -0,0 +1,48 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_ABANDON_TOP_LEVEL_CLAIM) +public class CommandClaimAbandonTop extends CommandClaimAbandon { + + public CommandClaimAbandonTop() { + this.abandonTopClaim = true; + } + + @CommandAlias("abandontop") + @Description("Abandons top level claim") + @Subcommand("abandon top") + public void execute(Player player) { + super.execute(player); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimAdmin.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAdmin.java new file mode 100644 index 0000000..e5d0439 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimAdmin.java @@ -0,0 +1,51 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.ShovelTypes; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_ADMIN_CLAIMS) +public class CommandClaimAdmin extends BaseCommand { + + @CommandAlias("modeadmin") + @Description("Switches the shovel tool to administrative claims mode") + @Subcommand("mode admin") + public void execute(Player player) { + + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + playerData.shovelMode = ShovelTypes.ADMIN; + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimModeAdmin.toText()); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimBank.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBank.java new file mode 100644 index 0000000..fa4f17c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBank.java @@ -0,0 +1,74 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_BANK) +public class CommandClaimBank extends BaseCommand { + + protected boolean townOnly = false; + + @CommandAlias("claimbank") + @Description("Used for claim bank queries") + @Syntax(" ") + @Subcommand("claim bank") + public void execute(Player player, @Optional String[] args) throws CommandException { + if (!GriefDefenderPlugin.getActiveConfig(player.getWorld().getUID()).getConfig().claim.bankTaxSystem) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimBankTaxSystemNotEnabled.toText()); + return; + } + GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + if (this.townOnly) { + if (!claim.isInTown()) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.townNotIn.toText()); + return; + } + claim = claim.getTownClaim(); + } else { + if (claim.isSubdivision() || claim.isAdminClaim()) { + return; + } + } + + if (args.length == 0 || args.length < 2) { + CommandHelper.displayClaimBankInfo(player, claim); + return; + } + + CommandHelper.handleBankTransaction(player, args, claim); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimBasic.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBasic.java new file mode 100644 index 0000000..6231a69 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBasic.java @@ -0,0 +1,51 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.ShovelTypes; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_BASIC_MODE) +public class CommandClaimBasic extends BaseCommand { + + @CommandAlias("modebasic") + @Description("Switches the shovel tool back to basic claims mode") + @Subcommand("mode basic") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + playerData.shovelMode = ShovelTypes.BASIC; + playerData.claimSubdividing = null; + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimModeBasic.toText()); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuy.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuy.java new file mode 100644 index 0000000..d820dca --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuy.java @@ -0,0 +1,97 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.claim.GDClaimManager; +import com.griefdefender.internal.pagination.PaginationList; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; +import net.milkbowl.vault.economy.Economy; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_BUY) +public class CommandClaimBuy extends BaseCommand { + + @CommandAlias("claimbuy") + @Description("List all claims available for purchase.\nNote: Requires economy plugin.") + @Subcommand("buy claim") + public void execute(Player player) { + if (GriefDefenderPlugin.getInstance().getVaultProvider() == null) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.economyNotInstalled.toText()); + return; + } + + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(player)) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyUserNotFound + .apply(ImmutableMap.of( + "user", player.getName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + Set claimsForSale = new HashSet<>(); + GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(player.getWorld().getUID()); + for (Claim worldClaim : claimManager.getWorldClaims()) { + if (worldClaim.isWilderness()) { + continue; + } + if (!worldClaim.isAdminClaim() && worldClaim.getEconomyData().isForSale() && worldClaim.getEconomyData().getSalePrice() > -1) { + claimsForSale.add(worldClaim); + } + for (Claim child : worldClaim.getChildren(true)) { + if (child.isAdminClaim()) { + continue; + } + if (child.getEconomyData().isForSale() && child.getEconomyData().getSalePrice() > -1) { + claimsForSale.add(child); + } + } + } + + List claimsTextList = CommandHelper.generateClaimTextList(new ArrayList(), claimsForSale, player.getWorld().getName(), null, player, CommandHelper.createCommandConsumer(player, "claimbuy", ""), true, false); + PaginationList.Builder paginationBuilder = PaginationList.builder() + .title(TextComponent.of("Claims for sale", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(claimsTextList); + paginationBuilder.sendTo(player); + return; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java new file mode 100644 index 0000000..fc0f948 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimBuyBlocks.java @@ -0,0 +1,137 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.permission.option.Options; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.economy.EconomyResponse; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_BUY_CLAIM_BLOCKS) +public class CommandClaimBuyBlocks extends BaseCommand { + + @CommandAlias("buyblocks") + @Description("Purchases additional claim blocks with server money.\nNote: Requires economy plugin.") + @Syntax("[]") + @Subcommand("buy blocks") + public void execute(Player player, @Optional Integer blockCount) { + if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { + TextAdapter.sendComponent(player, TextComponent.of("This command is not available while server is in economy mode.", TextColor.RED)); + return; + } + + if (GriefDefenderPlugin.getInstance().getVaultProvider() == null) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.economyNotInstalled.toText()); + return; + } + + final Economy economy = GriefDefenderPlugin.getInstance().getVaultProvider().getApi(); + if (!economy.hasAccount(player)) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyUserNotFound + .apply(ImmutableMap.of( + "user", player.getName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + final double economyBlockCost = GDPermissionManager.getInstance().getGlobalInternalOptionValue(player, Options.ECONOMY_BLOCK_COST, playerData); + final double economyBlockSell = GDPermissionManager.getInstance().getGlobalInternalOptionValue(player, Options.ECONOMY_BLOCK_SELL_RETURN, playerData); + if (economyBlockCost == 0 && economyBlockSell == 0) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.economyBuySellNotConfigured.toText()); + return; + } + + if (economyBlockCost == 0) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.economyOnlySellBlocks.toText()); + return; + } + + final double balance = economy.getBalance(player); + if (blockCount == null) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyBlockPurchaseCost + .apply(ImmutableMap.of( + "cost", economyBlockCost, + "balance", balance)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } else { + if (blockCount <= 0) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.economyBuyInvalidBlockCount.toText()); + return; + } + + final double totalCost = blockCount * economyBlockCost; + final int newClaimBlockTotal = playerData.getAccruedClaimBlocks() + blockCount; + if (newClaimBlockTotal > playerData.getMaxAccruedClaimBlocks()) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimBlockPurchaseLimit + .apply(ImmutableMap.of( + "new_total", newClaimBlockTotal, + "block_limit", playerData.getMaxAccruedClaimBlocks())).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + final EconomyResponse result = economy.withdrawPlayer(player, totalCost); + + if (!result.transactionSuccess()) { + final Component message = GriefDefenderPlugin.getInstance().messageData.economyWithdrawError + .apply(ImmutableMap.of( + "reason", result.errorMessage)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + playerData.addAccruedClaimBlocks(blockCount); + playerData.getStorageData().save(); + + final Component message = GriefDefenderPlugin.getInstance().messageData.economyBlocksPurchaseConfirmation + .apply(ImmutableMap.of( + "cost", totalCost, + "remaining-blocks", playerData.getRemainingClaimBlocks())).build(); + GriefDefenderPlugin.sendMessage(player, message); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimClear.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimClear.java new file mode 100644 index 0000000..92155e4 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimClear.java @@ -0,0 +1,139 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.internal.util.NMSUtil; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Tameable; +import org.bukkit.entity.Villager; + +import java.util.UUID; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_CLEAR) +public class CommandClaimClear extends BaseCommand { + + @CommandAlias("claimclear") + @Description("Allows clearing of entities within one or more claims.") + @Syntax(" [claim_uuid]") + @Subcommand("claim clear") + public void execute(Player player, String target, @Optional String claimId) { + World world = player.getWorld(); + if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUID())) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimDisabledWorld.toText()); + return; + } + + UUID claimUniqueId = null; + GDClaim targetClaim = null; + if (claimId == null) { + targetClaim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + final Component result = targetClaim.allowEdit(player); + if (result != null) { + GriefDefenderPlugin.sendMessage(player, result); + return; + } + claimUniqueId = targetClaim.getUniqueId(); + } else { + if (!player.hasPermission(GDPermissions.COMMAND_DELETE_CLAIMS)) { + GriefDefenderPlugin.sendMessage(player, TextComponent.of("Only administrators may clear claims by UUID.", TextColor.RED)); + return; + } + try { + claimUniqueId = UUID.fromString(claimId); + } catch (IllegalArgumentException e) { + return; + } + } + + if (targetClaim.isWilderness()) { + GriefDefenderPlugin.sendMessage(player, TextComponent.of("This action is not available in the wilderness.", TextColor.RED)); + return; + } + + int count = 0; + String[] parts = target.split(":"); + if (parts.length > 1) { + target = parts[1]; + } + + for (Chunk chunk : targetClaim.getChunks()) { + for (Entity entity : chunk.getEntities()) { + if (entity instanceof Player) { + continue; + } + if (entity instanceof Villager || !(entity instanceof LivingEntity)) { + continue; + } + if (entity instanceof Tameable) { + final UUID ownerUniqueId = NMSUtil.getInstance().getTameableOwnerUUID(entity); + if (ownerUniqueId != null && !ownerUniqueId.equals(player.getUniqueId())) { + continue; + } + } + LivingEntity livingEntity = (LivingEntity) entity; + + String entityName = entity.getType().getName().toLowerCase(); + if (target.equalsIgnoreCase("any") || target.equalsIgnoreCase("all") || target.equalsIgnoreCase("minecraft") || target.equalsIgnoreCase(entityName)) { + livingEntity.setHealth(0); + count++; + } + } + } + + if (count == 0) { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Could not locate any entities of type ") + .append(target, TextColor.GREEN) + .append(".").build()); + } else { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Killed ", TextColor.RED) + .append(String.valueOf(count), TextColor.AQUA) + .append(" entities of type ") + .append(target, TextColor.GREEN) + .append(".").build()); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimCuboid.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimCuboid.java new file mode 100644 index 0000000..c5f5b07 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimCuboid.java @@ -0,0 +1,54 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CUBOID_CLAIMS) +public class CommandClaimCuboid extends BaseCommand { + + @CommandAlias("cuboid") + @Description("Toggles cuboid claims mode.") + @Subcommand("cuboid") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + if (playerData.getClaimCreateMode() == 0) { + playerData.setClaimCreateMode(1); + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimCuboidEnabled.toText()); + } else { + playerData.setClaimCreateMode(0); + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimCuboidDisabled.toText()); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimDelete.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDelete.java new file mode 100644 index 0000000..3a1b494 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDelete.java @@ -0,0 +1,99 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS) +public class CommandClaimDelete extends BaseCommand { + + protected boolean deleteTopLevelClaim = false; + + @CommandAlias("deleteclaim") + @Description("Deletes the claim you're standing in, even if it's not your claim.") + @Subcommand("delete claim") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + final boolean isTown = claim.isTown(); + if (claim.isWilderness()) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimNotFound.toText()); + return; + } + + final Component message = GriefDefenderPlugin.getInstance().messageData.permissionClaimDelete + .apply(ImmutableMap.of( + "type", claim.getType().getName())).build(); + + if (claim.isAdminClaim() && !player.hasPermission(GDPermissions.DELETE_CLAIM_ADMIN)) { + GriefDefenderPlugin.sendMessage(player, message); + return; + } + if (claim.isBasicClaim() && !player.hasPermission(GDPermissions.DELETE_CLAIM_BASIC)) { + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + if (!this.deleteTopLevelClaim && !claim.isTown() && claim.children.size() > 0 /*&& !playerData.warnedAboutMajorDeletion*/) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimChildrenWarning.toText()); + return; + } + + GDCauseStackManager.getInstance().pushCause(player); + ClaimResult claimResult = GriefDefenderPlugin.getInstance().dataStore.deleteClaim(claim, !this.deleteTopLevelClaim); + GDCauseStackManager.getInstance().popCause(); + if (!claimResult.successful()) { + GriefDefenderPlugin.sendMessage(player, claimResult.getMessage().orElse(TextComponent.of("Could not delete claim. A plugin has denied it.").color(TextColor.RED))); + return; + } + + PermissionUtil.getInstance().clearPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, claim.getContext()); + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimDeleted.toText()); + + playerData.revertActiveVisual(player); + + if (isTown) { + playerData.inTown = false; + playerData.townChat = false; + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java new file mode 100644 index 0000000..d38a27a --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAll.java @@ -0,0 +1,84 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.GriefDefender; +import com.griefdefender.event.GDCauseStackManager; +import com.griefdefender.event.GDDeleteClaimEvent; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS) +public class CommandClaimDeleteAll extends BaseCommand { + + @CommandAlias("deleteall") + @Description("Delete all of another player's claims.") + @Subcommand("delete all") + public void execute(Player player, OfflinePlayer otherPlayer) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + int originalClaimCount = playerData.getInternalClaims().size(); + + if (originalClaimCount == 0) { + TextAdapter.sendComponent(player, TextComponent.of("Player " + otherPlayer.getName() + " has no claims to delete.", TextColor.RED)); + return; + } + + GDCauseStackManager.getInstance().pushCause(player); + GDDeleteClaimEvent event = new GDDeleteClaimEvent(ImmutableList.copyOf(playerData.getInternalClaims())); + GriefDefender.getEventManager().post(event); + GDCauseStackManager.getInstance().popCause(); + if (event.cancelled()) { + GriefDefenderPlugin.sendMessage(player, event.getMessage().orElse(TextComponent.of("Could not delete all claims. A plugin has denied it.").color(TextColor.RED))); + return; + } + + GriefDefenderPlugin.getInstance().dataStore.deleteClaimsForPlayer(otherPlayer.getUniqueId()); + final Component message = GriefDefenderPlugin.getInstance().messageData.claimDeleteAllSuccess + .apply(ImmutableMap.of( + "owner", otherPlayer.getName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + if (player != null) { + GriefDefenderPlugin.getInstance().getLogger().info(player.getName() + " deleted all claims belonging to " + otherPlayer.getName() + "."); + + // revert any current visualization + playerData.revertActiveVisual(player); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java new file mode 100644 index 0000000..192dd96 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteAllAdmin.java @@ -0,0 +1,63 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_DELETE_ADMIN_CLAIMS) +public class CommandClaimDeleteAllAdmin extends BaseCommand { + + @CommandAlias("deletealladmin") + @Description("Deletes all administrative claims.") + @Subcommand("delete alladmin") + public void execute(Player player) { + ClaimResult claimResult = GriefDefenderPlugin.getInstance().dataStore.deleteAllAdminClaims(player, player.getWorld()); + if (!claimResult.successful()) { + final Component message = GriefDefenderPlugin.getInstance().messageData.claimTypeNotFound + .apply(ImmutableMap.of( + "type", ClaimTypes.ADMIN.getName().toLowerCase())).build(); + GriefDefenderPlugin.sendMessage(player, claimResult.getMessage().orElse(message)); + return; + } + + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimDeleteAllAdminSuccess.toText()); + GriefDefenderPlugin.getInstance().getLogger().info(player.getName() + " deleted all administrative claims."); + GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + playerData.revertActiveVisual(player); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java new file mode 100644 index 0000000..ac9c84c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimDeleteTop.java @@ -0,0 +1,48 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_DELETE_CLAIMS) +public class CommandClaimDeleteTop extends CommandClaimDelete { + + public CommandClaimDeleteTop() { + this.deleteTopLevelClaim = true; + } + + @CommandAlias("deletetop") + @Description("Deletes the claim you're standing in, even if it's not your claim.") + @Subcommand("delete top") + public void execute(Player player) { + super.execute(player); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFarewell.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFarewell.java new file mode 100644 index 0000000..1cba8f0 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFarewell.java @@ -0,0 +1,78 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_FAREWELL) +public class CommandClaimFarewell extends BaseCommand { + + @CommandAlias("claimfarewell") + @Description("Sets the farewell message of your claim.") + @Syntax("") + @Subcommand("claim farewell") + public void execute(Player player, String message) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + if (claim.allowEdit(player) != null) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionEditClaim.toText()); + return; + } + + TextComponent farewell = LegacyComponentSerializer.legacy().deserialize(message, '&'); + if (farewell == TextComponent.empty() || farewell.content().equals("clear")) { + claim.getInternalClaimData().setFarewell(null); + } else { + claim.getInternalClaimData().setFarewell(farewell); + } + claim.getInternalClaimData().setRequiresSave(true); + Component resultMessage = null; + if (!claim.getInternalClaimData().getFarewell().isPresent()) { + resultMessage = GriefDefenderPlugin.getInstance().messageData.claimFarewellClear.toText(); + } else { + resultMessage = GriefDefenderPlugin.getInstance().messageData.claimFarewell + .apply(ImmutableMap.of( + "farewell", farewell)).build(); + } + TextAdapter.sendComponent(player, resultMessage); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlag.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlag.java new file mode 100644 index 0000000..7ddb111 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlag.java @@ -0,0 +1,55 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.permission.GDPermissions; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_FLAGS_CLAIM) +public class CommandClaimFlag extends ClaimFlagBase { + + public CommandClaimFlag() { + super(ClaimSubjectType.GLOBAL); + } + + @CommandAlias("cf|claimflag") + @Description("Gets/Sets claim flags in the claim you are standing in.") + @Syntax(" [context[key=value]]") + @Subcommand("flag claim") + public void execute(Player player, @Optional String[] args) throws InvalidCommandArgument { + this.subject = GriefDefenderPlugin.DEFAULT_HOLDER; + this.friendlySubjectName = "ALL"; + super.execute(player, args); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java new file mode 100644 index 0000000..b29db0e --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagDebug.java @@ -0,0 +1,72 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_FLAGS_DEBUG) +public class CommandClaimFlagDebug extends BaseCommand { + + @CommandAlias("cfd") + @Description("Toggles claim flag debug mode.") + @Subcommand("claim debug") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(player); + final Component result = claim.allowEdit(user, true); + if (result != null) { + GriefDefenderPlugin.sendMessage(player, result); + return; + } + + playerData.debugClaimPermissions = !playerData.debugClaimPermissions; + + if (!playerData.debugClaimPermissions) { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Claim flags debug ") + .append("OFF", TextColor.RED).build()); + } else { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Claim flags debug ") + .append("ON", TextColor.GREEN).build()); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java new file mode 100644 index 0000000..a4cc74d --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagGroup.java @@ -0,0 +1,78 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_FLAGS_GROUP) +public class CommandClaimFlagGroup extends ClaimFlagBase { + + public CommandClaimFlagGroup() { + super(ClaimSubjectType.GROUP); + } + + @CommandAlias("cfg") + @Description("Gets/Sets flag permission for a group in claim you are standing in.") + @Syntax(" [context[key=value]]") + @Subcommand("flag group") + public void execute(Player player, String group, @Optional String[] args) throws InvalidCommandArgument { + if (args.length < 2 || args.length > 3) { + throw new InvalidCommandArgument(); + } + + if (!PermissionUtil.getInstance().hasGroupSubject(group)) { + final Component message = GriefDefenderPlugin.getInstance().messageData.commandGroupInvalid + .apply(ImmutableMap.of( + "group", group)).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + /*String reason = ctx.getOne("reason").orElse(null); + Text reasonText = null; + if (reason != null) { + reasonText = TextSerializers.FORMATTING_CODE.deserialize(reason); + }*/ + + this.subject = PermissionHolderCache.getInstance().getOrCreateHolder(group); + this.friendlySubjectName = group; + + super.execute(player, args); + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java new file mode 100644 index 0000000..5325e33 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagPlayer.java @@ -0,0 +1,65 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.Tristate; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_FLAGS_PLAYER) +public class CommandClaimFlagPlayer extends ClaimFlagBase { + + public CommandClaimFlagPlayer() { + super(ClaimSubjectType.PLAYER); + } + + @CommandAlias("cfp") + @Description("Gets/Sets flag permission for a player in claim you are standing in.") + @Syntax(" [context[key=value]]") + @Subcommand("flag player") + public void execute(Player src, OfflinePlayer player, @Optional String[] args) throws InvalidCommandArgument { + this.subject = PermissionHolderCache.getInstance().getOrCreateUser(player); + this.friendlySubjectName = player.getName(); + + if (PermissionUtil.getInstance().getPermissionValue(this.subject, GDPermissions.COMMAND_ADMIN_CLAIMS) == Tristate.TRUE && !src.hasPermission(GDPermissions.SET_ADMIN_FLAGS)) { + GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.permissionSetAdminFlags.toText()); + return; + } + + super.execute(src, args); + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java new file mode 100644 index 0000000..4d09fe9 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimFlagReset.java @@ -0,0 +1,81 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.permission.Context; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import org.bukkit.entity.Player; + +import java.util.Set; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_FLAGS_RESET) +public class CommandClaimFlagReset extends BaseCommand { + + @CommandAlias("cfr") + @Description("Resets a claim to flag defaults.") + @Subcommand("flag reset") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final Component message = GriefDefenderPlugin.getInstance().messageData.permissionClaimResetFlags + .apply(ImmutableMap.of( + "type", claim.getType().getName())).build(); + if (claim.isWilderness()) { + if (!player.hasPermission(GDPermissions.MANAGE_WILDERNESS)) { + GriefDefenderPlugin.sendMessage(player, message); + return; + } + } else if (claim.isAdminClaim()) { + if (!player.getUniqueId().equals(claim.getOwnerUniqueId()) && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) { + GriefDefenderPlugin.sendMessage(player, message); + return; + } + } else if (!player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS) && (claim.isBasicClaim() || claim.isSubdivision()) && !player.getUniqueId().equals(claim.getOwnerUniqueId())) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.permissionClaimResetFlagsSelf.toText()); + return; + } + + // Remove persisted data + for (Set contextSet : PermissionUtil.getInstance().getAllPermissions(claim, GriefDefenderPlugin.DEFAULT_HOLDER).keySet()) { + if (contextSet.contains(claim.getContext())) { + PermissionUtil.getInstance().clearPermissions(GriefDefenderPlugin.DEFAULT_HOLDER, contextSet); + } + } + + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.flagResetSuccess.toText()); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimGreeting.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimGreeting.java new file mode 100644 index 0000000..a77c797 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimGreeting.java @@ -0,0 +1,79 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_GREETING) +public class CommandClaimGreeting extends BaseCommand { + + @CommandAlias("claimgreeting") + @Description("Sets the greeting message of your claim.") + @Syntax("") + @Subcommand("claim greeting") + public void execute(Player player, String message) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final Component result = claim.allowEdit(player); + if (result != null) { + GriefDefenderPlugin.sendMessage(player, result); + return; + } + + final TextComponent greeting = LegacyComponentSerializer.legacy().deserialize(message, '&'); + if (greeting == TextComponent.empty() || greeting.content().equals("clear")) { + claim.getInternalClaimData().setGreeting(null); + } else { + claim.getInternalClaimData().setGreeting(greeting); + } + claim.getInternalClaimData().setRequiresSave(true); + Component resultMessage = null; + if (!claim.getInternalClaimData().getGreeting().isPresent()) { + resultMessage = GriefDefenderPlugin.getInstance().messageData.claimGreetingClear.toText(); + } else { + resultMessage = GriefDefenderPlugin.getInstance().messageData.claimGreeting + .apply(ImmutableMap.of( + "greeting", greeting)).build(); + } + TextAdapter.sendComponent(player, resultMessage); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimIgnore.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimIgnore.java new file mode 100644 index 0000000..c3a010e --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimIgnore.java @@ -0,0 +1,66 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_IGNORE_CLAIMS) +public class CommandClaimIgnore extends BaseCommand { + + @CommandAlias("claimignore|ignoreclaims|ic") + @Description("Toggles ignore claims mode.") + @Subcommand("claim ignore") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + if (claim.isBasicClaim() && !playerData.ignoreBasicClaims || claim.isWilderness() && !playerData.ignoreWilderness || claim.isAdminClaim() && !playerData.ignoreAdminClaims) { + final Component message = GriefDefenderPlugin.getInstance().messageData.permissionClaimIgnore + .apply(ImmutableMap.of( + "type", claim.getType().getName())).build(); + GriefDefenderPlugin.sendMessage(player, message); + return; + } + + playerData.ignoreClaims = !playerData.ignoreClaims; + + if (!playerData.ignoreClaims) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimRespecting.toText()); + } else { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.claimIgnore.toText()); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimInfo.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimInfo.java new file mode 100644 index 0000000..197193f --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimInfo.java @@ -0,0 +1,840 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.flowpowered.math.vector.Vector3i; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.Tristate; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimResult; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.api.claim.TrustTypes; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.claim.GDClaimManager; +import com.griefdefender.internal.pagination.PaginationList; +import com.griefdefender.internal.util.VecHelper; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.text.action.GDCallbackHolder; +import com.griefdefender.util.PlayerUtil; + +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.event.HoverEvent; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_INFO_BASE) +public class CommandClaimInfo extends BaseCommand { + + private static final Component NONE = TextComponent.of("none", TextColor.GRAY); + private static final String ADMIN_SETTINGS = "Admin Settings"; + private static final String CLAIM_EXPIRATION = "ClaimExpiration"; + private static final String DENY_MESSAGES = "DenyMessages"; + private static final String FLAG_OVERRIDES = "FlagOverrides"; + private static final String INHERIT_PARENT = "InheritParent"; + private static final String PVP_OVERRIDE = "PvPOverride"; + private static final String RESIZABLE = "Resizable"; + private static final String REQUIRES_CLAIM_BLOCKS = "RequiresClaimBlocks"; + private static final String SIZE_RESTRICTIONS = "SizeRestrictions"; + private static final String FOR_SALE = "ForSale"; + private boolean useTownInfo = false; + + public CommandClaimInfo() { + + } + + public CommandClaimInfo(boolean useTownInfo) { + this.useTownInfo = useTownInfo; + } + + @CommandAlias("claiminfo") + @Syntax("[claim_uuid]") + @Subcommand("claim info") + public void execute(CommandSender src, String[] args) { + String claimIdentifier = null; + if (args.length > 0) { + claimIdentifier = args[0]; + } + + Player player = null; + if (src instanceof Player) { + player = (Player) src; + if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(player.getWorld().getUID())) { + GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.claimDisabledWorld.toText()); + return; + } + } + + if (player == null && claimIdentifier == null) { + TextAdapter.sendComponent(src, TextComponent.of("No valid player or claim UUID found.", TextColor.RED)); + return; + } + + boolean isAdmin = src.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS); + final GDPlayerData playerData = player != null ? GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()) : null; + Claim claim = null; + if (claimIdentifier == null) { + if (player != null) { + claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAt(player.getLocation()); + } else { + TextAdapter.sendComponent(src, TextComponent.of("Claim UUID is required if executing from non-player source.", TextColor.RED)); + return; + } + } else { + for (World world : Bukkit.getServer().getWorlds()) { + if (!GriefDefenderPlugin.getInstance().claimsEnabledForWorld(world.getUID())) { + continue; + } + + final GDClaimManager claimManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUID()); + UUID uuid = null; + try { + uuid = UUID.fromString(claimIdentifier); + claim = claimManager.getClaimByUUID(uuid).orElse(null); + if (claim != null) { + break; + } + } catch (IllegalArgumentException e) { + + } + if (uuid == null) { + final List claimList = claimManager.getClaimsByName(claimIdentifier); + if (!claimList.isEmpty()) { + claim = claimList.get(0); + } + } + } + } + + if (claim == null) { + GriefDefenderPlugin.sendMessage(src, GriefDefenderPlugin.getInstance().messageData.claimNotFound.toText()); + return; + } + + if (this.useTownInfo) { + if (!claim.isInTown()) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.townNotIn.toText()); + return; + } + claim = claim.getTown().get(); + } + + final GDClaim gpClaim = (GDClaim) claim; + UUID ownerUniqueId = claim.getOwnerUniqueId(); + + if (!isAdmin) { + isAdmin = playerData.canIgnoreClaim(gpClaim); + } + // if not owner of claim, validate perms + if (!isAdmin && !player.getUniqueId().equals(claim.getOwnerUniqueId())) { + if (!gpClaim.getInternalClaimData().getContainers().contains(player.getUniqueId()) + && !gpClaim.getInternalClaimData().getBuilders().contains(player.getUniqueId()) + && !gpClaim.getInternalClaimData().getManagers().contains(player.getUniqueId()) + && !player.hasPermission(GDPermissions.COMMAND_CLAIM_INFO_OTHERS)) { + TextAdapter.sendComponent(player, GriefDefenderPlugin.getInstance().messageData.claimNotYours.toText()); + return; + } + } + + final Component allowEdit = gpClaim.allowEdit(player); + + List textList = new ArrayList<>(); + Component name = claim.getName().orElse(null); + Component greeting = claim.getData().getGreeting().orElse(null); + Component farewell = claim.getData().getFarewell().orElse(null); + String accessors = ""; + String builders = ""; + String containers = ""; + String managers = ""; + String accessorGroups = ""; + String builderGroups = ""; + String containerGroups = ""; + String managerGroups = ""; + + final int minClaimLevel = gpClaim.getOwnerMinClaimLevel(); + double claimY = gpClaim.getOwnerPlayerData() == null ? 65.0D : (minClaimLevel > 65.0D ? minClaimLevel : 65); + if (gpClaim.isCuboid()) { + claimY = gpClaim.lesserBoundaryCorner.getY(); + } + Location southWest = new Location(gpClaim.getWorld(), gpClaim.lesserBoundaryCorner.getX(), claimY, gpClaim.greaterBoundaryCorner.getZ()); + Location northWest = new Location(gpClaim.getWorld(), gpClaim.lesserBoundaryCorner.getX(), claimY, gpClaim.lesserBoundaryCorner.getZ()); + Location southEast = new Location(gpClaim.getWorld(), gpClaim.greaterBoundaryCorner.getX(), claimY, gpClaim.greaterBoundaryCorner.getZ()); + Location northEast = new Location(gpClaim.getWorld(), gpClaim.greaterBoundaryCorner.getX(), claimY, gpClaim.lesserBoundaryCorner.getZ()); + // String southWestCorner = + Date created = null; + Date lastActive = null; + try { + Instant instant = claim.getData().getDateCreated(); + created = Date.from(instant); + } catch(DateTimeParseException ex) { + // ignore + } + + try { + Instant instant = claim.getData().getDateLastActive(); + lastActive = Date.from(instant); + } catch(DateTimeParseException ex) { + // ignore + } + + final int sizeX = Math.abs(claim.getGreaterBoundaryCorner().getX() - claim.getLesserBoundaryCorner().getX()) + 1; + final int sizeY = Math.abs(claim.getGreaterBoundaryCorner().getY() - claim.getLesserBoundaryCorner().getY()) + 1; + final int sizeZ = Math.abs(claim.getGreaterBoundaryCorner().getZ() - claim.getLesserBoundaryCorner().getZ()) + 1; + Component claimSize = TextComponent.empty(); + if (claim.isCuboid()) { + claimSize = TextComponent.builder(" ") + .append("Area: ", TextColor.YELLOW) + .append(sizeX + "x" + sizeY + "x" + sizeZ, TextColor.GRAY).build(); + } else { + claimSize = TextComponent.builder(" ") + .append("Area: ", TextColor.YELLOW) + .append(sizeX + "x" + sizeZ, TextColor.GRAY).build(); + } + final Component claimCost = TextComponent.builder(" ") + .append("Blocks: ", TextColor.YELLOW) + .append(String.valueOf(claim.getClaimBlocks()), TextColor.GRAY).build(); + if (claim.isWilderness() && name == null) { + name = TextComponent.of("Wilderness", TextColor.GREEN); + } + Component claimName = TextComponent.builder("") + .append("Name", TextColor.YELLOW) + .append(" : ") + .append(name == null ? NONE : name).build(); + if (!claim.isWilderness() && !claim.isAdminClaim()) { + claimName = TextComponent.builder("") + .append(claimName) + .append(claimSize) + .append(claimCost).build(); + } + // users + final List accessorList = gpClaim.getUserTrustList(TrustTypes.ACCESSOR, true); + final List builderList = gpClaim.getUserTrustList(TrustTypes.BUILDER, true); + final List containerList = gpClaim.getUserTrustList(TrustTypes.CONTAINER, true); + final List managerList = gpClaim.getUserTrustList(TrustTypes.MANAGER, true); + for (UUID uuid : accessorList) { + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName != null) { + accessors += PlayerUtil.getInstance().getUserName(uuid) + " "; + } + } + for (UUID uuid : builderList) { + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName != null) { + builders += PlayerUtil.getInstance().getUserName(uuid) + " "; + } + } + for (UUID uuid : containerList) { + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName != null) { + containers += PlayerUtil.getInstance().getUserName(uuid) + " "; + } + } + for (UUID uuid : managerList) { + final String userName = PlayerUtil.getInstance().getUserName(uuid); + if (userName != null) { + managers += PlayerUtil.getInstance().getUserName(uuid) + " "; + } + } + + // groups + for (String group : gpClaim.getInternalClaimData().getAccessorGroups()) { + accessorGroups += group + " "; + } + for (String group : gpClaim.getInternalClaimData().getBuilderGroups()) { + builderGroups += group + " "; + } + for (String group : gpClaim.getInternalClaimData().getContainerGroups()) { + containerGroups += group + " "; + } + for (String group : gpClaim.getInternalClaimData().getManagerGroups()) { + managerGroups += group + " "; + } + + /*if (gpClaim.isInTown()) { + Text returnToClaimInfo = Text.builder().append(Text.of( + TextColors.WHITE, "\n[", TextColors.AQUA, "Return to standard settings", TextColors.WHITE, "]\n")) + .onClick(TextActions.executeCallback(CommandHelper.createCommandConsumer(src, "claiminfo", ""))).build(); + Text townName = Text.of(TextColors.YELLOW, "Name", TextColors.WHITE, " : ", TextColors.RESET, + gpClaim.getTownClaim().getTownData().getName().orElse(NONE)); + Text townTag = Text.of(TextColors.YELLOW, "Tag", TextColors.WHITE, " : ", TextColors.RESET, + gpClaim.getTownClaim().getTownData().getTownTag().orElse(NONE)); + townTextList.add(returnToClaimInfo); + townTextList.add(townName); + townTextList.add(townTag); + Text townSettings = Text.builder() + .append(Text.of(TextStyles.ITALIC, TextColors.GREEN, TOWN_SETTINGS)) + .onClick(TextActions.executeCallback(createSettingsConsumer(src, claim, townTextList, ClaimTypes.TOWN))) + .onHover(TextActions.showText(Text.of("Click here to view town settings"))) + .build(); + textList.add(townSettings); + }*/ + + if (isAdmin) { + Component adminSettings = TextComponent.builder("") + .append(TextComponent.of(ADMIN_SETTINGS, TextColor.RED).decoration(TextDecoration.ITALIC, true)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createSettingsConsumer(src, claim, generateAdminSettings(src, gpClaim), ClaimTypes.ADMIN)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to view admin settings"))) + .build(); + textList.add(adminSettings); + } + + Component bankInfo = null; + Component forSaleText = null; + if (GriefDefenderPlugin.getInstance().getVaultProvider() != null) { + if (GriefDefenderPlugin.getActiveConfig(gpClaim.getWorld().getUID()).getConfig().claim.bankTaxSystem) { + bankInfo = TextComponent.builder("") + .append("Bank Info", TextColor.GOLD, TextDecoration.ITALIC) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click to check bank information"))) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(Consumer -> { CommandHelper.displayClaimBankInfo(src, gpClaim, gpClaim.isTown() ? true : false, true); }))) + .build(); + } + forSaleText = TextComponent.builder("") + .append("ForSale", TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, FOR_SALE, claim.getEconomyData().isForSale() ? TextComponent.of("YES", TextColor.GREEN) : TextComponent.of("NO", TextColor.GRAY))).build(); + if (claim.getEconomyData().isForSale()) { + forSaleText = TextComponent.builder("") + .append(forSaleText) + .append(" ") + .append("Price", TextColor.YELLOW) + .append(" : ") + .append(String.valueOf(claim.getEconomyData().getSalePrice()), TextColor.GOLD) + .build(); + } + } + + Component claimId = TextComponent.builder("") + .append("UUID", TextColor.YELLOW) + .append(" : ") + .append(TextComponent.builder("") + .append(claim.getUniqueId().toString(), TextColor.GRAY) + .insertion(claim.getUniqueId().toString()).build()).build(); + final String ownerName = PlayerUtil.getInstance().getUserName(ownerUniqueId); + Component ownerLine = TextComponent.builder("") + .append("Owner", TextColor.YELLOW) + .append(" : ") + .append(ownerName != null && !claim.isAdminClaim() ? ownerName : "administrator", TextColor.GOLD).build(); + Component adminShowText = TextComponent.empty(); + Component basicShowText = TextComponent.empty(); + Component subdivisionShowText = TextComponent.empty(); + Component townShowText = TextComponent.empty(); + Component claimType = TextComponent.empty(); + final Component whiteOpenBracket = TextComponent.of("["); + final Component whiteCloseBracket = TextComponent.of("]"); + Component defaultTypeText = TextComponent.builder("") + .append(whiteOpenBracket) + .append(gpClaim.getFriendlyNameType(true)) + .append(whiteCloseBracket).build(); + if (allowEdit != null && !isAdmin) { + adminShowText = allowEdit; + basicShowText = allowEdit; + subdivisionShowText = allowEdit; + townShowText = allowEdit; + Component adminTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.ADMIN ? + defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(adminShowText)).build(); + Component basicTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.BASIC ? + defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(basicShowText)).build(); + Component subTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.SUBDIVISION ? + defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(subdivisionShowText)).build(); + Component townTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.TOWN ? + defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(townShowText)).build(); + claimType = TextComponent.builder("") + .append(claim.isCuboid() ? "3D " : "2D ", TextColor.GREEN) + .append(adminTypeText) + .append(" ") + .append(basicTypeText) + .append(" ") + .append(subTypeText) + .append(" ") + .append(townTypeText) + .build(); + } else { + Component adminTypeText = defaultTypeText; + Component basicTypeText = defaultTypeText; + Component subTypeText = defaultTypeText; + Component townTypeText = defaultTypeText; + if (!claim.isAdminClaim()) { + final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.ADMIN, ownerUniqueId, playerData).getMessage().orElse(null); + adminShowText = message != null ? message : TextComponent.builder("") + .append("Click here to change claim to ") + .append("ADMIN ", TextColor.RED) + .append("type.").build(); + + if (message == null) { + adminTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.ADMIN ? + defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.ADMIN, isAdmin)))) + .hoverEvent(HoverEvent.showText(adminShowText)).build(); + } else { + adminTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.ADMIN ? + defaultTypeText : TextComponent.of("ADMIN", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(adminShowText)).build(); + } + } + if (!claim.isBasicClaim()) { + final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.BASIC, ownerUniqueId, playerData).getMessage().orElse(null); + basicShowText = message != null ? message : TextComponent.builder("") + .append("Click here to change claim to ") + .append("BASIC ", TextColor.YELLOW) + .append("type.").build(); + + if (message == null) { + basicTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.BASIC ? defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.BASIC, isAdmin)))) + .hoverEvent(HoverEvent.showText(basicShowText)).build(); + } else { + basicTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.BASIC ? defaultTypeText : TextComponent.of("BASIC", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(basicShowText)).build(); + } + } + if (!claim.isSubdivision()) { + final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.SUBDIVISION, ownerUniqueId, playerData).getMessage().orElse(null); + subdivisionShowText = message != null ? message : TextComponent.builder("") + .append("Click here to change claim to ") + .append("SUBDIVISION ", TextColor.AQUA) + .append("type.").build(); + + if (message == null) { + subTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.SUBDIVISION ? defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.SUBDIVISION, isAdmin)))) + .hoverEvent(HoverEvent.showText(subdivisionShowText)).build(); + } else { + subTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.SUBDIVISION ? defaultTypeText : TextComponent.of("SUBDIVISION", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(subdivisionShowText)).build(); + } + } + if (!claim.isTown()) { + final Component message = ((GDClaim) claim).validateClaimType(ClaimTypes.TOWN, ownerUniqueId, playerData).getMessage().orElse(null); + townShowText = message != null ? message : TextComponent.builder("") + .append("Click here to change claim to ") + .append("TOWN ", TextColor.GREEN) + .append("type.").build(); + + if (message == null) { + townTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.TOWN ? defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimTypeConsumer(src, claim, ClaimTypes.TOWN, isAdmin)))) + .hoverEvent(HoverEvent.showText(townShowText)).build(); + } else { + townTypeText = TextComponent.builder("") + .append(claim.getType() == ClaimTypes.TOWN ? defaultTypeText : TextComponent.of("TOWN", TextColor.GRAY)) + .hoverEvent(HoverEvent.showText(townShowText)).build(); + } + } + + claimType = TextComponent.builder("") + .append(claim.isCuboid() ? "3D " : "2D ", TextColor.GREEN) + .append(adminTypeText) + .append(" ") + .append(basicTypeText) + .append(" ") + .append(subTypeText) + .append(" ") + .append(townTypeText) + .build(); + } + + Component claimTypeInfo = TextComponent.builder("") + .append("Type", TextColor.YELLOW) + .append(" : ") + .append(claimType).build(); + Component claimInherit = TextComponent.builder("") + .append(INHERIT_PARENT, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, INHERIT_PARENT, claim.getData().doesInheritParent() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimExpired = TextComponent.builder("") + .append("Expired", TextColor.YELLOW) + .append(" : ") + .append(claim.getData().isExpired() ? TextComponent.of("YES", TextColor.RED) : TextComponent.of("NO", TextColor.GRAY)).build(); + Component claimFarewell = TextComponent.builder("") + .append("Farewell", TextColor.YELLOW) + .append(" : ") + .append(farewell == null ? NONE : farewell).build(); + Component claimGreeting = TextComponent.builder("") + .append("Greeting", TextColor.YELLOW) + .append(" : ") + .append(greeting == null ? NONE : greeting).build(); + Component claimSpawn = null; + if (claim.getData().getSpawnPos().isPresent()) { + Vector3i spawnPos = claim.getData().getSpawnPos().get(); + Location spawnLoc = new Location(gpClaim.getWorld(), spawnPos.getX(), spawnPos.getY(), spawnPos.getZ()); + claimSpawn = TextComponent.builder("") + .append("Spawn", TextColor.GREEN) + .append(" : ") + .append(spawnPos.toString(), TextColor.GRAY) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, spawnLoc, claim)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to teleport to claim spawn."))) + .build(); + } + Component southWestCorner = TextComponent.builder("") + .append("SW", TextColor.LIGHT_PURPLE) + .append(" : ") + .append(VecHelper.toVector3i(southWest).toString(), TextColor.GRAY) + .append(" ") + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, southWest, claim)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to teleport to SW corner of claim."))) + .build(); + Component southEastCorner = TextComponent.builder("") + .append("SE", TextColor.LIGHT_PURPLE) + .append(" : ") + .append(VecHelper.toVector3i(southEast).toString(), TextColor.GRAY) + .append(" ") + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, southEast, claim)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to teleport to SE corner of claim."))) + .build(); + Component southCorners = TextComponent.builder("") + .append("SouthCorners", TextColor.YELLOW) + .append(" : ") + .append(southWestCorner) + .append(southEastCorner).build(); + Component northWestCorner = TextComponent.builder("") + .append("NW", TextColor.LIGHT_PURPLE) + .append(" : ") + .append(VecHelper.toVector3i(northWest).toString(), TextColor.GRAY) + .append(" ") + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, northWest, claim)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to teleport to NW corner of claim."))) + .build(); + Component northEastCorner = TextComponent.builder("") + .append("NE", TextColor.LIGHT_PURPLE) + .append(" : ") + .append(VecHelper.toVector3i(northEast).toString(), TextColor.GRAY) + .append(" ") + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createTeleportConsumer(player, northEast, claim)))) + .hoverEvent(HoverEvent.showText(TextComponent.of("Click here to teleport to NE corner of claim."))) + .build(); + Component northCorners = TextComponent.builder("") + .append("NorthCorners", TextColor.YELLOW) + .append(" : ") + .append(northWestCorner) + .append(northEastCorner).build(); + Component claimAccessors = TextComponent.builder("") + .append("Accessors", TextColor.YELLOW) + .append(" : ") + .append(accessors.equals("") ? NONE : TextComponent.of(accessors, TextColor.BLUE)) + .append(" ") + .append(accessorGroups, TextColor.LIGHT_PURPLE).build(); + Component claimBuilders = TextComponent.builder("") + .append("Builders", TextColor.YELLOW) + .append(" : ") + .append(builders.equals("") ? NONE : TextComponent.of(builders, TextColor.BLUE)) + .append(" ") + .append(builderGroups, TextColor.LIGHT_PURPLE).build(); + Component claimContainers = TextComponent.builder("") + .append("Containers", TextColor.YELLOW) + .append(" : ") + .append(containers.equals("") ? NONE : TextComponent.of(containers, TextColor.BLUE)) + .append(" ") + .append(containerGroups, TextColor.LIGHT_PURPLE).build(); + Component claimCoowners = TextComponent.builder("") + .append("Managers", TextColor.YELLOW) + .append(" : ") + .append(managers.equals("") ? NONE : TextComponent.of(managers, TextColor.BLUE)) + .append(" ") + .append(managerGroups, TextColor.LIGHT_PURPLE).build(); + Component dateCreated = TextComponent.builder("") + .append("Created", TextColor.YELLOW) + .append(" : ") + .append(created != null ? created.toString() : "Unknown", TextColor.GRAY).build(); + Component dateLastActive = TextComponent.builder("") + .append("LastActive", TextColor.YELLOW) + .append(" : ") + .append(lastActive != null ? lastActive.toString() : "Unknown", TextColor.GRAY).build(); + Component worldName = TextComponent.builder("") + .append("World", TextColor.YELLOW) + .append(" : ") + .append(gpClaim.getWorld().getName(), TextColor.GRAY).build(); + + if (claimSpawn != null) { + textList.add(claimSpawn); + } + if (bankInfo != null) { + textList.add(bankInfo); + } + textList.add(claimName); + textList.add(ownerLine); + textList.add(claimTypeInfo); + if (!claim.isAdminClaim() && !claim.isWilderness()) { + textList.add(TextComponent.builder("") + .append(claimInherit) + .append(" ") + .append(claimExpired).build()); + if (forSaleText != null) { + textList.add(forSaleText); + } + } + textList.add(claimAccessors); + textList.add(claimBuilders); + textList.add(claimContainers); + textList.add(claimCoowners); + textList.add(claimGreeting); + textList.add(claimFarewell); + textList.add(worldName); + textList.add(dateCreated); + textList.add(dateLastActive); + textList.add(claimId); + textList.add(northCorners); + textList.add(southCorners); + if (!claim.getParent().isPresent()) { + textList.remove(claimInherit); + } + if (claim.isAdminClaim()) { + textList.remove(bankInfo); + textList.remove(dateLastActive); + } + if (claim.isWilderness()) { + textList.remove(bankInfo); + textList.remove(claimInherit); + textList.remove(claimTypeInfo); + textList.remove(dateLastActive); + textList.remove(northCorners); + textList.remove(southCorners); + } + + PaginationList.Builder paginationBuilder = PaginationList.builder() + .title(TextComponent.of("Claim Info", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(textList); + paginationBuilder.sendTo(src); + } + + public static Consumer createSettingsConsumer(CommandSender src, Claim claim, List textList, ClaimType type) { + return settings -> { + String name = type == ClaimTypes.TOWN ? "Town Settings" : "Admin Settings"; + PaginationList.Builder paginationBuilder = PaginationList.builder() + .title(TextComponent.of(name, TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(textList); + paginationBuilder.sendTo(src); + }; + } + + private static List generateAdminSettings(CommandSender src, GDClaim claim) { + List textList = new ArrayList<>(); + Component returnToClaimInfo = TextComponent.builder("") + .append("\n[") + .append("Return to standard settings", TextColor.AQUA) + .append("]\n") + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(CommandHelper.createCommandConsumer(src, "claiminfo", claim.getUniqueId().toString())))).build(); + Component claimDenyMessages = TextComponent.builder("") + .append(DENY_MESSAGES, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, DENY_MESSAGES, claim.getInternalClaimData().allowDenyMessages() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimResizable = TextComponent.builder("") + .append(RESIZABLE, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, RESIZABLE, claim.getInternalClaimData().isResizable() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimRequiresClaimBlocks = TextComponent.builder("") + .append(REQUIRES_CLAIM_BLOCKS, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, REQUIRES_CLAIM_BLOCKS, claim.getInternalClaimData().requiresClaimBlocks() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimSizeRestrictions = TextComponent.builder("") + .append(SIZE_RESTRICTIONS, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, SIZE_RESTRICTIONS, claim.getInternalClaimData().hasSizeRestrictions() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimExpiration = TextComponent.builder("") + .append(CLAIM_EXPIRATION, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, CLAIM_EXPIRATION, claim.getInternalClaimData().allowExpiration() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component claimFlagOverrides = TextComponent.builder("") + .append(FLAG_OVERRIDES, TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, FLAG_OVERRIDES, claim.getInternalClaimData().allowFlagOverrides() ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of("OFF", TextColor.RED))).build(); + Component pvp = TextComponent.builder("") + .append("PvP", TextColor.YELLOW) + .append(" : ") + .append(getClickableInfoText(src, claim, PVP_OVERRIDE, claim.getInternalClaimData().getPvpOverride() == Tristate.TRUE ? TextComponent.of("ON", TextColor.GREEN) : TextComponent.of(claim.getInternalClaimData().getPvpOverride().name(), TextColor.RED))).build(); + textList.add(returnToClaimInfo); + textList.add(claimDenyMessages); + if (!claim.isAdminClaim() && !claim.isWilderness()) { + textList.add(claimRequiresClaimBlocks); + textList.add(claimExpiration); + textList.add(claimResizable); + textList.add(claimSizeRestrictions); + } + textList.add(claimFlagOverrides); + textList.add(pvp); + int fillSize = 20 - (textList.size() + 4); + for (int i = 0; i < fillSize; i++) { + textList.add(TextComponent.of(" ")); + } + return textList; + } + + private static void executeAdminSettings(CommandSender src, GDClaim claim) { + PaginationList.Builder paginationBuilder = PaginationList.builder() + .title(TextComponent.of("Admin Settings", TextColor.AQUA)).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(generateAdminSettings(src, claim)); + paginationBuilder.sendTo(src); + } + + public static Component getClickableInfoText(CommandSender src, Claim claim, String title, Component infoText) { + Component onClickText = TextComponent.of("Click here to toggle value."); + boolean hasPermission = true; + if (src instanceof Player) { + Component denyReason = ((GDClaim) claim).allowEdit((Player) src); + if (denyReason != null) { + onClickText = denyReason; + hasPermission = false; + } + } + + TextComponent.Builder textBuilder = TextComponent.builder("") + .append(infoText) + .hoverEvent(HoverEvent.showText(onClickText)); + if (hasPermission) { + textBuilder.clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimInfoConsumer(src, claim, title)))); + } + return textBuilder.build(); + } + + private static Consumer createClaimInfoConsumer(CommandSender src, Claim claim, String title) { + GDClaim gpClaim = (GDClaim) claim; + return info -> { + switch (title) { + case INHERIT_PARENT : + if (!src.hasPermission(GDPermissions.COMMAND_CLAIM_INHERIT)) { + return; + } + + gpClaim.getInternalClaimData().setInheritParent(!gpClaim.getInternalClaimData().doesInheritParent()); + gpClaim.getInternalClaimData().setRequiresSave(true); + claim.getData().save(); + CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString()); + return; + case CLAIM_EXPIRATION : + gpClaim.getInternalClaimData().setExpiration(!gpClaim.getInternalClaimData().allowExpiration()); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case DENY_MESSAGES : + gpClaim.getInternalClaimData().setDenyMessages(!gpClaim.getInternalClaimData().allowDenyMessages()); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case FLAG_OVERRIDES : + gpClaim.getInternalClaimData().setFlagOverrides(!gpClaim.getInternalClaimData().allowFlagOverrides()); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case PVP_OVERRIDE : + Tristate value = gpClaim.getInternalClaimData().getPvpOverride(); + if (value == Tristate.UNDEFINED) { + gpClaim.getInternalClaimData().setPvpOverride(Tristate.TRUE); + } else if (value == Tristate.TRUE) { + gpClaim.getInternalClaimData().setPvpOverride(Tristate.FALSE); + } else { + gpClaim.getInternalClaimData().setPvpOverride(Tristate.UNDEFINED); + } + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case RESIZABLE : + boolean resizable = gpClaim.getInternalClaimData().isResizable(); + gpClaim.getInternalClaimData().setResizable(!resizable); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case REQUIRES_CLAIM_BLOCKS : + boolean requiresClaimBlocks = gpClaim.getInternalClaimData().requiresClaimBlocks(); + gpClaim.getInternalClaimData().setRequiresClaimBlocks(!requiresClaimBlocks); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case SIZE_RESTRICTIONS : + boolean sizeRestrictions = gpClaim.getInternalClaimData().hasSizeRestrictions(); + gpClaim.getInternalClaimData().setSizeRestrictions(!sizeRestrictions); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + break; + case FOR_SALE : + boolean forSale = gpClaim.getEconomyData().isForSale(); + gpClaim.getEconomyData().setForSale(!forSale); + gpClaim.getInternalClaimData().setRequiresSave(true); + gpClaim.getClaimStorage().save(); + CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString()); + return; + default: + } + executeAdminSettings(src, gpClaim); + }; + } + + private static Consumer createClaimTypeConsumer(CommandSender src, Claim gpClaim, ClaimType clicked, boolean isAdmin) { + GDClaim claim = (GDClaim) gpClaim; + return type -> { + if (!(src instanceof Player)) { + // ignore + return; + } + + final Player player = (Player) src; + if (!isAdmin && ((GDClaim) gpClaim).allowEdit(player) != null) { + TextAdapter.sendComponent(src, GriefDefenderPlugin.getInstance().messageData.claimNotYours.toText()); + return; + } + final ClaimResult result = claim.changeType(clicked, Optional.of(player.getUniqueId()), src); + if (result.successful()) { + CommandHelper.executeCommand(src, "claiminfo", gpClaim.getUniqueId().toString()); + } else { + TextAdapter.sendComponent(src, result.getMessage().get()); + } + }; + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimInherit.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimInherit.java new file mode 100644 index 0000000..64c41cf --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimInherit.java @@ -0,0 +1,74 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Subcommand; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.format.TextColor; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_INHERIT) +public class CommandClaimInherit extends BaseCommand { + + @CommandAlias("claiminherit") + @Description("Toggles subdivision inherit mode.") + @Subcommand("claim inherit") + public void execute(Player player) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final Component result = claim.allowEdit(player); + if (result != null) { + GriefDefenderPlugin.sendMessage(player, result); + return; + } + + if (claim.parent == null) { + GriefDefenderPlugin.sendMessage(player, GriefDefenderPlugin.getInstance().messageData.commandInherit.toText()); + return; + } + claim.getData().setInheritParent(!claim.getData().doesInheritParent()); + claim.getInternalClaimData().setRequiresSave(true); + + if (!claim.getData().doesInheritParent()) { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Parent claim inheritance ") + .append("OFF", TextColor.RED).build()); + } else { + GriefDefenderPlugin.sendMessage(player, TextComponent.builder("") + .append("Parent claim inheritance ") + .append("ON", TextColor.GREEN).build()); + } + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimList.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimList.java new file mode 100644 index 0000000..46ed54c --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimList.java @@ -0,0 +1,273 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.Claim; +import com.griefdefender.api.claim.ClaimType; +import com.griefdefender.api.claim.ClaimTypes; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.claim.GDClaimManager; +import com.griefdefender.internal.pagination.PaginationList; +import com.griefdefender.internal.util.BlockUtil; +import com.griefdefender.permission.GDPermissionUser; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.text.action.GDCallbackHolder; +import com.griefdefender.util.PaginationUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.event.HoverEvent; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_CLAIM_LIST) +public class CommandClaimList extends BaseCommand { + + private final ClaimType forcedType; + private boolean canListOthers; + private boolean canListAdmin; + private boolean displayOwned = true; + private final Cache lastActiveClaimTypeMap = Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES) + .build(); + + public CommandClaimList() { + this.forcedType = null; + } + + public CommandClaimList(ClaimType type) { + this.forcedType = type; + } + + @CommandAlias("claimlist") + @Syntax("[| ]") + @Subcommand("claim list") + public void execute(Player src, @Optional String[] args) { + OfflinePlayer user = null; + World world = null; + if (args.length > 0) { + user = Bukkit.getServer().getOfflinePlayer(args[0]); + if (user == null) { + TextAdapter.sendComponent(src, TextComponent.of("User ' " + args[0] + "' could not be found.", TextColor.RED)); + return; + } + if (args.length > 1) { + world = Bukkit.getServer().getWorld(args[1]); + if (world == null) { + TextAdapter.sendComponent(src, TextComponent.of("World ' " + args[1] + "' could not be found.", TextColor.RED)); + return; + } + } + } + + if (user == null) { + user = (OfflinePlayer) src; + if (world == null) { + world = ((Player) user).getWorld(); + } + } + if (world == null) { + world = Bukkit.getServer().getWorlds().get(0); + } + + this.canListOthers = src.hasPermission(GDPermissions.LIST_OTHER_CLAIMS); + this.canListAdmin = src.hasPermission(GDPermissions.LIST_OTHER_CLAIMS); + showClaimList(src, PermissionHolderCache.getInstance().getOrCreateUser(user), this.forcedType, world.getUID()); + } + + private void showClaimList(Player src, GDPermissionUser user, ClaimType type, UUID worldUniqueId) { + List claimsTextList = new ArrayList<>(); + Set claims = new HashSet<>(); + final String worldName = worldUniqueId == null ? "" : Bukkit.getWorld(worldUniqueId).getName(); + for (World world : Bukkit.getServer().getWorlds()) { + if (!this.displayOwned && !world.getUID().equals(worldUniqueId)) { + continue; + } + final GDClaimManager claimWorldManager = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(world.getUID()); + // load the target player's data + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(world, user.getUniqueId()); + Set claimList = null; + if (this.displayOwned) { + claimList = playerData.getClaims(); + } else { + claimList = BlockUtil.getInstance().getNearbyClaims(src.getLocation(), 100); + } + + for (Claim claim : claimList) { + if (claims.contains(claim)) { + continue; + } + + if (user != null && this.displayOwned) { + if (user.getUniqueId().equals(claim.getOwnerUniqueId())) { + claims.add(claim); + } + } else if (type != null) { + if (claim.getType() == type) { + claims.add(claim); + } + } else { + claims.add(claim); + } + } + } + if (src instanceof Player) { + final Player player = (Player) src; + final String lastClaimType = this.lastActiveClaimTypeMap.getIfPresent(player.getUniqueId()); + String currentType = type == null ? "ALL" : type.toString(); + if (lastClaimType != null && !lastClaimType.equals(currentType.toString())) { + PaginationUtil.getInstance().resetActivePage(player.getUniqueId()); + } + } + claimsTextList = CommandHelper.generateClaimTextList(claimsTextList, claims, worldName, user, src, createClaimListConsumer(src, user, type, worldUniqueId), this.canListOthers, false); + + final Component whiteOpenBracket = TextComponent.of("["); + final Component whiteCloseBracket = TextComponent.of("]"); + Component ownedShowText = TextComponent.of("Click here to view the claims you own."); + Component adminShowText = TextComponent.builder("") + .append("Click here to filter by ") + .append("ADMIN ", TextColor.RED) + .append("type.").build(); + Component basicShowText = TextComponent.builder("") + .append("Click here to filter by ") + .append("BASIC ", TextColor.YELLOW) + .append("type.").build(); + Component subdivisionShowText = TextComponent.builder("") + .append("Click here to filter by ") + .append("SUBDIVISION ", TextColor.AQUA) + .append("type.").build(); + Component townShowText = TextComponent.builder("") + .append("Click here to filter by ") + .append("TOWN ", TextColor.GREEN) + .append("type.").build(); + Component ownedTypeText = TextComponent.builder("") + .append(this.displayOwned && type == null ? + TextComponent.builder("") + .append(whiteOpenBracket) + .append("OWN", TextColor.GOLD) + .append(whiteCloseBracket).build() : TextComponent.of("OWN", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, "OWN", worldUniqueId)))) + .hoverEvent(HoverEvent.showText(ownedShowText)).build(); + Component adminTypeText = TextComponent.builder("") + .append(type == ClaimTypes.ADMIN ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("ADMIN", TextColor.RED) + .append(whiteCloseBracket).build() : TextComponent.of("ADMIN", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.ADMIN, worldUniqueId)))) + .hoverEvent(HoverEvent.showText(adminShowText)).build(); + Component basicTypeText = TextComponent.builder("") + .append(type == ClaimTypes.BASIC ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("BASIC", TextColor.YELLOW) + .append(whiteCloseBracket).build() : TextComponent.of("BASIC", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.BASIC, worldUniqueId)))) + .hoverEvent(HoverEvent.showText(basicShowText)).build(); + Component subTypeText = TextComponent.builder("") + .append(type == ClaimTypes.SUBDIVISION ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("SUBDIVISION", TextColor.AQUA) + .append(whiteCloseBracket).build() : TextComponent.of("SUBDIVISION", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.SUBDIVISION, worldUniqueId)))) + .hoverEvent(HoverEvent.showText(subdivisionShowText)).build(); + Component townTypeText = TextComponent.builder("") + .append(type == ClaimTypes.TOWN ? TextComponent.builder("") + .append(whiteOpenBracket) + .append("TOWN", TextColor.GREEN) + .append(whiteCloseBracket).build() : TextComponent.of("TOWN", TextColor.GRAY)) + .clickEvent(ClickEvent.runCommand(GDCallbackHolder.getInstance().createCallbackRunCommand(createClaimListConsumer(src, user, ClaimTypes.TOWN, worldUniqueId)))) + .hoverEvent(HoverEvent.showText(townShowText)).build(); + Component claimListHead = TextComponent.builder("") + .append(" Displaying : ", TextColor.AQUA) + .append(ownedTypeText) + .append(" ") + .append(adminTypeText) + .append(" ") + .append(basicTypeText) + .append(" ") + .append(subTypeText) + .append(" ") + .append(townTypeText).build(); + final int fillSize = 20 - (claimsTextList.size() + 2); + for (int i = 0; i < fillSize; i++) { + claimsTextList.add(TextComponent.of(" ")); + } + + PaginationList paginationList = PaginationList.builder() + .title(claimListHead).padding(TextComponent.of(" ").decoration(TextDecoration.STRIKETHROUGH, true)).contents(claimsTextList).build(); + Integer activePage = 1; + if (src instanceof Player) { + final Player player = (Player) src; + activePage = PaginationUtil.getInstance().getActivePage(player.getUniqueId()); + if (activePage == null) { + activePage = 1; + } + this.lastActiveClaimTypeMap.put(player.getUniqueId(), type == null ? "ALL" : type.toString()); + } + paginationList.sendTo(src, activePage); + } + + private Consumer createClaimListConsumer(Player src, GDPermissionUser user, String type, UUID worldUniqueId) { + return consumer -> { + if (type.equalsIgnoreCase("ALL")) { + this.displayOwned = false; + } else { + this.displayOwned = true; + } + showClaimList(src, user, null, worldUniqueId); + }; + } + + private Consumer createClaimListConsumer(Player src, GDPermissionUser user, ClaimType type, UUID worldUniqueId) { + return consumer -> { + this.displayOwned = false; + showClaimList(src, user, type, worldUniqueId); + }; + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimName.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimName.java new file mode 100644 index 0000000..b156557 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimName.java @@ -0,0 +1,72 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.permission.GDPermissions; + +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.entity.Player; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_SET_CLAIM_NAME) +public class CommandClaimName extends BaseCommand { + + @CommandAlias("claimname") + @Syntax("") + @Subcommand("claim name") + public void execute(Player player, String name) { + final GDPlayerData playerData = GriefDefenderPlugin.getInstance().dataStore.getOrCreatePlayerData(player.getWorld(), player.getUniqueId()); + final GDClaim claim = GriefDefenderPlugin.getInstance().dataStore.getClaimAtPlayer(playerData, player.getLocation()); + final Component result = claim.allowEdit(player); + if (result != null) { + GriefDefenderPlugin.sendMessage(player, result); + return; + } + + final Component text = LegacyComponentSerializer.legacy().deserialize(name, '&'); + if (text == TextComponent.empty()) { + claim.getInternalClaimData().setName(null); + } else { + claim.getInternalClaimData().setName(text); + } + claim.getInternalClaimData().setRequiresSave(true); + final Component message = GriefDefenderPlugin.getInstance().messageData.commandClaimName + .apply(ImmutableMap.of( + "name", text)).build(); + GriefDefenderPlugin.sendMessage(player, message); + } +} diff --git a/bukkit/src/main/java/com/griefdefender/command/CommandClaimOption.java b/bukkit/src/main/java/com/griefdefender/command/CommandClaimOption.java new file mode 100644 index 0000000..921be63 --- /dev/null +++ b/bukkit/src/main/java/com/griefdefender/command/CommandClaimOption.java @@ -0,0 +1,251 @@ +/* + * This file is part of GriefDefender, licensed under the MIT License (MIT). + * + * Copyright (c) bloodmc + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.griefdefender.command; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Description; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Subcommand; +import co.aikar.commands.annotation.Syntax; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.griefdefender.GDPlayerData; +import com.griefdefender.GriefDefenderPlugin; +import com.griefdefender.api.claim.ClaimContexts; +import com.griefdefender.api.permission.Context; +import com.griefdefender.api.permission.option.Option; +import com.griefdefender.cache.PermissionHolderCache; +import com.griefdefender.claim.GDClaim; +import com.griefdefender.internal.pagination.PaginationList; +import com.griefdefender.permission.GDPermissionHolder; +import com.griefdefender.permission.GDPermissionManager; +import com.griefdefender.permission.GDPermissions; +import com.griefdefender.registry.OptionRegistryModule; +import com.griefdefender.util.CauseContextHelper; +import com.griefdefender.util.PermissionUtil; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.adapter.bukkit.TextAdapter; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; +import org.bukkit.entity.Player; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@CommandAlias("%griefdefender") +@CommandPermission(GDPermissions.COMMAND_OPTIONS_BASE) +public class CommandClaimOption extends BaseCommand { + + private GDPermissionHolder subject = GriefDefenderPlugin.DEFAULT_HOLDER; + private ClaimSubjectType subjectType = ClaimSubjectType.GLOBAL; + private String friendlySubjectName; + + @CommandAlias("cop|claimoption") + @Description("Gets/Sets claim options in the claim you are standing in.") + @Syntax("[