### Build ProtectionStones Plugin Source: https://github.com/espidev/protectionstones/blob/master/README.md Clone the repository, navigate to the project directory, and run the Maven clean install command to build the plugin. Ensure Java 21 JDK and Maven are installed. ```bash git clone https://github.com/espidev/ProtectionStones.git cd ProtectionStones mvn clean install ``` -------------------------------- ### Listen for Region Creation Events Source: https://context7.com/espidev/protectionstones/llms.txt Provides an example of how to implement a Bukkit listener for the `PSCreateEvent`. This event is cancellable and can be used to enforce custom rules, such as preventing region creation on specific days. ```java import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.event.PSCreateEvent; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; public class RegionCreateListener implements Listener { @EventHandler(priority = EventPriority.NORMAL) public void onRegionCreate(PSCreateEvent event) { PSRegion region = event.getRegion(); Player player = event.getPlayer(); // null if not player-caused if (player != null) { // Example: block creation on Mondays for trolling prevention java.time.DayOfWeek day = java.time.LocalDate.now().getDayOfWeek(); if (day == java.time.DayOfWeek.MONDAY) { event.setCancelled(true); player.sendMessage("No claims on Mondays!"); return; } player.sendMessage("Region " + region.getId() + " created!"); } } } ``` -------------------------------- ### Get ProtectionStones Plugin Instance and Check Integrations Source: https://context7.com/espidev/protectionstones/llms.txt Obtain the singleton plugin instance to access its API. Check for the availability of optional integrations like Vault, LuckPerms, and PlaceholderAPI before utilizing their features. ```java import dev.espi.protectionstones.ProtectionStones; // Obtain the plugin instance from any other plugin ProtectionStones ps = ProtectionStones.getInstance(); // Check integration availability before using optional features if (ps.isVaultSupportEnabled()) { double balance = ps.getVaultEconomy().getBalance(player); ps.getLogger().info("Player balance: " + balance); } if (ps.isLuckPermsSupportEnabled()) { // LuckPerms API is available for offline permission queries ps.getLuckPerms().getUserManager().loadUser(player.getUniqueId()); } if (ps.isPlaceholderAPISupportEnabled()) { // PlaceholderAPI is hooked for greeting/farewell flag placeholders ps.getLogger().info("PlaceholderAPI is active."); } ``` -------------------------------- ### Maven Dependency Setup for ProtectionStones Source: https://context7.com/espidev/protectionstones/llms.txt Configures the Maven dependency for ProtectionStones as a compile-time API dependency. Also shows plugin.yml configuration for soft-dependency. ```xml dev.espi protectionstones 2.10.6 provided com.sk89q.worldguard worldguard-bukkit 7.0.9-SNAPSHOT provided ``` ```yaml # plugin.yml name: MyPlugin version: 1.0.0 main: com.example.MyPlugin depend: [WorldGuard] softdepend: [ProtectionStones] ``` -------------------------------- ### Get Economy Information and Process Taxes Source: https://context7.com/espidev/protectionstones/llms.txt Retrieves economy information, lists rented regions, and demonstrates manual tax processing for a region. Requires Vault for economy initialization. ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSEconomy; import dev.espi.protectionstones.PSRegion; import java.util.List; public void economyInfo() { PSEconomy eco = ProtectionStones.getEconomy(); if (eco == null) { System.out.println("Economy not initialized (Vault missing)."); return; } // List all currently rented regions List rented = eco.getRentedList(); System.out.println("Regions currently being rented: " + rented.size()); for (PSRegion r : rented) { System.out.println(" " + r.getId() + " | tenant: " + r.getTenant() + " | price: $" + r.getPrice() + " | period: " + r.getRentPeriod()); } // Manually process taxes for a specific region (normally done by scheduler) PSRegion.fromName("myregion").values().stream() .flatMap(List::stream) .forEach(PSEconomy::processTaxes); } ``` -------------------------------- ### Get Player Region Limits with PSPlayer Source: https://context7.com/espidev/protectionstones/llms.txt Demonstrates how to retrieve a player's global and per-protect-block-type region limits. Supports offline players if LuckPerms is enabled. Limits are read from player permissions. ```java import dev.espi.protectionstones.PSPlayer; import dev.espi.protectionstones.PSProtectBlock; import org.bukkit.entity.Player; import java.util.Map; public void showLimits(Player player) { PSPlayer psp = PSPlayer.fromPlayer(player); int globalMax = psp.getGlobalRegionLimits(); player.sendMessage("Total claim limit: " + (globalMax < 0 ? "unlimited" : globalMax)); Map perType = psp.getRegionLimits(); if (perType.isEmpty()) { player.sendMessage("No per-type limits set."); } else { perType.forEach((block, limit) -> player.sendMessage(" " + block.alias + ": " + limit)); } } ``` -------------------------------- ### Rent Out a Region Source: https://context7.com/espidev/protectionstones/llms.txt Manages the lifecycle of renting a region, from advertising availability to starting and managing the rental period. The `rentOut` method initiates the rental, and `removeRenting` can be used to stop it. ```java import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.PSRegion.RentStage; import org.bukkit.Location; import org.bukkit.entity.Player; import java.util.UUID; public void rentOutRegion(Player landlord, Location regionLoc, UUID tenantUuid) { PSRegion region = PSRegion.fromLocation(regionLoc); if (region == null) return; // Step 1 – advertise as available for rent (no tenant yet) region.setRentable(landlord.getUniqueId(), "7d", 50.0); // 7-day period, $50/period landlord.sendMessage("Region is now looking for a tenant."); // Step 2 – tenant accepts: start renting region.rentOut(landlord.getUniqueId(), tenantUuid, "7d", 50.0); landlord.sendMessage("Renting started. Stage: " + region.getRentStage()); // RENTING // Read state UUID tenant = region.getTenant(); String period = region.getRentPeriod(); Double rentPrice = region.getPrice(); landlord.sendMessage("Tenant: " + tenant + " | Period: " + period + " | Price: " + rentPrice); // Stop renting // region.removeRenting(); } ``` -------------------------------- ### Retrieve Protection Block Options Source: https://context7.com/espidev/protectionstones/llms.txt Get the configuration details for a protection block type. This method is useful for understanding the properties of a protection block when it's placed, such as its alias, region dimensions, and tax amount. ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSProtectBlock; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; public class MyListener implements Listener { @EventHandler public void onBlockPlace(BlockPlaceEvent event) { Block b = event.getBlockPlaced(); PSProtectBlock opts = ProtectionStones.getBlockOptions(b); if (opts != null) { Player p = event.getPlayer(); p.sendMessage("You placed a protection block: " + opts.alias); p.sendMessage("Region size: " + opts.xRadius + "x" + opts.yRadius + "x" + opts.zRadius); p.sendMessage("Tax amount: " + opts.taxAmount); } } } ``` -------------------------------- ### Manage Region Ownership and Membership Source: https://context7.com/espidev/protectionstones/llms.txt Use `PSRegion.fromLocation` to get a region at a specific location. Methods like `getOwners`, `getMembers`, `addMember`, and `removeOwner` allow modification of region access. ```java import dev.espi.protectionstones.PSRegion; import org.bukkit.Location; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.UUID; public void manageMembers(Player admin, Location regionLoc, UUID newMemberUuid) { PSRegion region = PSRegion.fromLocation(regionLoc); if (region == null) { admin.sendMessage("No PS region at that location."); return; } // Read current membership ArrayList owners = region.getOwners(); ArrayList members = region.getMembers(); admin.sendMessage("Owners: " + owners.size() + ", Members: " + members.size()); // Modify membership if (!region.isMember(newMemberUuid)) { region.addMember(newMemberUuid); admin.sendMessage("Member added."); } // Remove an owner (also clears landlord/autopayer side-effects) UUID oldOwner = owners.get(0); region.removeOwner(oldOwner); admin.sendMessage("Owner removed: " + oldOwner); } ``` -------------------------------- ### PSRegion — Renting Regions Source: https://context7.com/espidev/protectionstones/llms.txt Full lifecycle API for renting regions, including listing, renting out to a tenant, and removing rent. This covers advertising a region as available, starting a rental agreement, and retrieving rental details. ```APIDOC ## PSRegion — Renting Regions Full lifecycle API for renting: listing, renting out to a tenant, and removing rent. ### Method Signature `void rentOutRegion(Player landlord, Location regionLoc, UUID tenantUuid)` ### Description This method facilitates the renting of a region. It first allows a landlord to advertise a region as rentable with a specified period and price. Then, a tenant can accept, initiating the rental agreement. The method also shows how to retrieve current rental information. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```java PSRegion region = PSRegion.fromLocation(regionLoc); if (region != null) { // Step 1 – advertise as available for rent region.setRentable(landlord.getUniqueId(), "7d", 50.0); // Step 2 – tenant accepts: start renting region.rentOut(landlord.getUniqueId(), tenantUuid, "7d", 50.0); // Read state UUID tenant = region.getTenant(); String period = region.getRentPeriod(); Double rentPrice = region.getPrice(); } ``` ### Response #### Success Response None (operations are reflected in region state and player messages) #### Response Example None ``` -------------------------------- ### Configure Region Home, Name, and Parent Source: https://context7.com/espidev/protectionstones/llms.txt Set a region's teleport home using `setHome`, its nickname with `setName`, and assign a parent region for flag inheritance using `setParent`. Ensure no circular inheritance is created when setting a parent. ```java import dev.espi.protectionstones.PSRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import org.bukkit.Location; import org.bukkit.entity.Player; public void configureRegion(PSRegion region, Player player) throws ProtectedRegion.CircularInheritanceException { // Set home (teleport destination for /ps home) Location loc = player.getLocation(); region.setHome(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); Location home = region.getHome(); player.sendMessage("Home set at: " + home.getBlockX() + "," + home.getBlockY() + "," + home.getBlockZ()); // Set a friendly name (/ps name equivalent) region.setName("MyFarm"); player.sendMessage("Region name: " + region.getName()); // "MyFarm" // Assign a parent region for flag inheritance PSRegion parent = PSRegion.fromLocation(player.getWorld().getSpawnLocation()); if (parent != null) { region.setParent(parent); player.sendMessage("Parent set to: " + region.getParent().getId()); } } ``` -------------------------------- ### Show Player Info with PSPlayer Source: https://context7.com/espidev/protectionstones/llms.txt Demonstrates how to retrieve and display a player's balance, global region limits, owned/member regions in a world, and homes using the PSPlayer class. Requires Vault for economy features. ```java import dev.espi.protectionstones.PSPlayer; import dev.espi.protectionstones.PSRegion; import org.bukkit.World; import org.bukkit.entity.Player; import java.util.List; public void showPlayerInfo(Player player, World world) { PSPlayer psp = PSPlayer.fromPlayer(player); // Economy (requires Vault) double bal = psp.getBalance(); player.sendMessage("Balance: $" + bal); // Region limits from permissions (protectionstones.limit.x) int globalLimit = psp.getGlobalRegionLimits(); // -1 = unlimited player.sendMessage("Global region limit: " + (globalLimit == -1 ? "unlimited" : globalLimit)); // List owned/member regions in a world List regions = psp.getPSRegions(world, true); // true = include as member player.sendMessage("You have " + regions.size() + " region(s) in " + world.getName()); // Cross-world home list List homes = psp.getHomes(world); homes.forEach(r -> player.sendMessage("Home: " + (r.getName() != null ? r.getName() : r.getId()))); } ``` -------------------------------- ### Transfer Funds and Manage Balances with PSPlayer Source: https://context7.com/espidev/protectionstones/llms.txt Shows how to transfer funds between players and perform individual balance operations (withdraw/deposit) using PSPlayer. Includes checks for sufficient funds and basic error handling for economy transactions. Requires Vault. ```java import dev.espi.protectionstones.PSPlayer; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.entity.Player; import java.util.UUID; public void transferFunds(Player payer, UUID payeeUuid, double amount) { PSPlayer from = PSPlayer.fromPlayer(payer); PSPlayer to = PSPlayer.fromUUID(payeeUuid); if (!from.hasAmount(amount)) { payer.sendMessage("Insufficient funds. You have $" + from.getBalance()); return; } // Direct pay (withdraw from + deposit to) from.pay(to, amount); payer.sendMessage("Transferred $" + amount + " to " + to.getName()); // Individual operations (return EconomyResponse for error handling) EconomyResponse withdrawResp = from.withdrawBalance(10.0); EconomyResponse depositResp = to.depositBalance(10.0); if (!withdrawResp.transactionSuccess()) { payer.sendMessage("Withdraw failed: " + withdrawResp.errorMessage); } } ``` -------------------------------- ### ProtectionStones.getInstance() Source: https://context7.com/espidev/protectionstones/llms.txt Retrieves the singleton instance of the ProtectionStones plugin, which serves as the primary entry point for accessing all API functionalities. It also provides methods to check for and access integrations with other plugins like Vault, LuckPerms, and PlaceholderAPI. ```APIDOC ## ProtectionStones.getInstance() ### Description Returns the singleton plugin instance, the entry point for all API access. ### Method `static ProtectionStones getInstance()` ### Usage Example ```java import dev.espi.protectionstones.ProtectionStones; // Obtain the plugin instance from any other plugin ProtectionStones ps = ProtectionStones.getInstance(); // Check integration availability before using optional features if (ps.isVaultSupportEnabled()) { // Access Vault economy } if (ps.isLuckPermsSupportEnabled()) { // Access LuckPerms API } if (ps.isPlaceholderAPISupportEnabled()) { // PlaceholderAPI is hooked for greeting/farewell flag placeholders } ``` ``` -------------------------------- ### PSRegion Home, Name, and Parent Methods Source: https://context7.com/espidev/protectionstones/llms.txt Methods for managing a region's teleport home, nickname, and parent hierarchy. ```APIDOC ## PSRegion Home, Name, and Parent ### Description Methods for managing a region's teleport home, nickname, and parent hierarchy. ### Methods * **`void setHome(double x, double y, double z, float yaw, float pitch)`** * Description: Sets the teleport home location for the region. * **`Location getHome()`** * Description: Retrieves the current home location of the region. * **`void setName(String name)`** * Description: Sets a friendly nickname for the region. * **`String getName()`** * Description: Retrieves the nickname of the region. * **`void setParent(PSRegion parent)`** * Description: Assigns a parent region for flag inheritance. Throws `ProtectedRegion.CircularInheritanceException` if a circular hierarchy is detected. * **`PSRegion getParent()`** * Description: Retrieves the parent region of this region. ``` -------------------------------- ### PSRegion.fromWGRegion(World, ProtectedRegion) Source: https://context7.com/espidev/protectionstones/llms.txt Wraps an existing WorldGuard ProtectedRegion into a PSRegion. Returns null if the region does not have the PS format. Use this when you already have a WG region reference. ```APIDOC ## PSRegion.fromWGRegion(World, ProtectedRegion) ### Description Wraps an existing WorldGuard `ProtectedRegion` into a `PSRegion`. Returns `null` if the region does not have the PS format. Use this when you already have a WG region reference. ### Method Signature `PSRegion.fromWGRegion(World world, ProtectedRegion protectedRegion)` ### Parameters * **world** (World) - The world the region belongs to. * **protectedRegion** (ProtectedRegion) - The WorldGuard region to wrap. ### Returns * **PSRegion** - The wrapped PSRegion object, or null if it's not a PS region. ``` -------------------------------- ### PSCreateEvent — Listen for Region Creation Source: https://context7.com/espidev/protectionstones/llms.txt A cancellable Bukkit event that is triggered whenever a new ProtectionStones region is created. Allows for custom logic or cancellation of region creation. ```APIDOC ## `PSCreateEvent` — Listen for Region Creation A cancellable Bukkit event fired whenever a PS region is created. ### Description This event allows server administrators and plugin developers to hook into the region creation process. You can listen for this event to perform custom actions, validate region creation based on certain conditions, or cancel the creation entirely if specific criteria are not met. The event provides access to the `PSRegion` object being created and the `Player` who initiated the creation (if applicable). ### Event Details - **`PSCreateEvent`**: The event class that needs to be implemented. - **`getRegion()`**: Returns the `PSRegion` object that is being created. - **`getPlayer()`**: Returns the `Player` who initiated the region creation. This can be `null` if the creation was not directly player-initiated (e.g., by a command or another plugin). - **`isCancelled()`**: Checks if the event has been cancelled. - **`setCancelled(boolean cancel)`**: Sets the cancellation state of the event. Setting this to `true` will prevent the region from being created. ### Usage Examples ```java import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.event.PSCreateEvent; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; public class RegionCreateListener implements Listener { @EventHandler(priority = EventPriority.NORMAL) public void onRegionCreate(PSCreateEvent event) { PSRegion region = event.getRegion(); Player player = event.getPlayer(); // null if not player-caused if (player != null) { // Example: block creation on Mondays for trolling prevention java.time.DayOfWeek day = java.time.LocalDate.now().getDayOfWeek(); if (day == java.time.DayOfWeek.MONDAY) { event.setCancelled(true); player.sendMessage("No claims on Mondays!"); return; } player.sendMessage("Region " + region.getId() + " created!"); } } } ``` ``` -------------------------------- ### ProtectionStones.getBlockOptions(Block | ItemStack | String) Source: https://context7.com/espidev/protectionstones/llms.txt Fetches the configuration details for a protection block. This method can accept a Bukkit Block, an ItemStack, or a String representing the material name, returning a PSProtectBlock object if the type is configured as a protection block, otherwise null. ```APIDOC ## ProtectionStones.getBlockOptions(Block | ItemStack | String) ### Description Retrieves the `PSProtectBlock` config object for a given block, item, or material-type string. Returns `null` if the block type is not configured as a protection block. ### Method `static PSProtectBlock getBlockOptions(Block block)` `static PSProtectBlock getBlockOptions(ItemStack item) `static PSProtectBlock getBlockOptions(String materialName)` ### Usage Example ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSProtectBlock; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; public class MyListener implements Listener { @EventHandler public void onBlockPlace(BlockPlaceEvent event) { Block b = event.getBlockPlaced(); PSProtectBlock opts = ProtectionStones.getBlockOptions(b); if (opts != null) { Player p = event.getPlayer(); p.sendMessage("You placed a protection block: " + opts.alias); p.sendMessage("Region size: " + opts.xRadius + "x" + opts.yRadius + "x" + opts.zRadius); p.sendMessage("Tax amount: " + opts.taxAmount); } } } ``` ``` -------------------------------- ### PSPlayer — Economy Methods Source: https://context7.com/espidev/protectionstones/llms.txt Provides methods for managing a player's in-game balance using Vault integration. Supports direct transfers, withdrawals, and deposits. ```APIDOC ## `PSPlayer` — Economy Methods Vault-backed balance manipulation on a `PSPlayer`. ### Description These methods allow for the management of a player's in-game currency, provided that the Vault plugin and an economy provider are installed. You can check balances, transfer funds directly between players, or perform individual withdrawal and deposit operations. ### Methods - `double getBalance()`: Retrieves the player's current balance. - `boolean hasAmount(double amount)`: Checks if the player has sufficient funds for a given amount. - `void pay(PSPlayer payee, double amount)`: Transfers a specified amount from the current player to another `PSPlayer`. - `EconomyResponse withdrawBalance(double amount)`: Withdraws a specified amount from the player's balance. Returns an `EconomyResponse` object for error handling. - `EconomyResponse depositBalance(double amount)`: Deposits a specified amount into the player's balance. Returns an `EconomyResponse` object for error handling. ### Usage Examples ```java import dev.espi.protectionstones.PSPlayer; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.entity.Player; import java.util.UUID; public void transferFunds(Player payer, UUID payeeUuid, double amount) { PSPlayer from = PSPlayer.fromPlayer(payer); PSPlayer to = PSPlayer.fromUUID(payeeUuid); if (!from.hasAmount(amount)) { payer.sendMessage("Insufficient funds. You have $" + from.getBalance()); return; } // Direct pay (withdraw from + deposit to) from.pay(to, amount); payer.sendMessage("Transferred $" + amount + " to " + to.getName()); // Individual operations (return EconomyResponse for error handling) EconomyResponse withdrawResp = from.withdrawBalance(10.0); EconomyResponse depositResp = to.depositBalance(10.0); if (!withdrawResp.transactionSuccess()) { payer.sendMessage("Withdraw failed: " + withdrawResp.errorMessage); } } ``` ``` -------------------------------- ### Create an ItemStack for a Protection Block Source: https://context7.com/espidev/protectionstones/llms.txt Generates a custom `ItemStack` for a specified protection block alias. This item includes NBT tags, display name, lore, and effects as configured. The `PSProtectBlock` object also has a direct `createItem()` method. ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSProtectBlock; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; public void giveProtectionBlock(Player player, String blockAlias) { PSProtectBlock block = ProtectionStones.getProtectBlockFromAlias(blockAlias); if (block == null) { player.sendMessage("Unknown protection block alias: " + blockAlias); return; } ItemStack item = ProtectionStones.createProtectBlockItem(block); // Also available directly as: block.createItem() player.getInventory().addItem(item); player.sendMessage("Given 1x " + block.displayName); } ``` -------------------------------- ### PSPlayer.fromPlayer(Player) / fromUUID(UUID) Source: https://context7.com/espidev/protectionstones/llms.txt Wraps a Bukkit player (online or offline) into PSPlayer for ProtectionStones-aware operations. Provides methods to access player balance, region limits, and lists of owned or member regions within a world. ```APIDOC ## `PSPlayer.fromPlayer(Player)` / `fromUUID(UUID)` Wraps a Bukkit player (online or offline) into `PSPlayer` for ProtectionStones-aware operations. ### Description This method allows you to obtain a `PSPlayer` object, which provides access to various ProtectionStones-specific functionalities for a given player. You can retrieve the player's balance, their global and per-type region limits, and lists of regions they own or are members of within a specific world. ### Methods - `PSPlayer.fromPlayer(Player player)`: Creates a `PSPlayer` instance from an online Bukkit `Player` object. - `PSPlayer.fromUUID(UUID uuid)`: Creates a `PSPlayer` instance from a player's UUID, supporting offline players. ### Usage Examples ```java import dev.espi.protectionstones.PSPlayer; import dev.espi.protectionstones.PSRegion; import org.bukkit.World; import org.bukkit.entity.Player; import java.util.List; public void showPlayerInfo(Player player, World world) { PSPlayer psp = PSPlayer.fromPlayer(player); // Economy (requires Vault) double bal = psp.getBalance(); player.sendMessage("Balance: $" + bal); // Region limits from permissions (protectionstones.limit.x) int globalLimit = psp.getGlobalRegionLimits(); // -1 = unlimited player.sendMessage("Global region limit: " + (globalLimit == -1 ? "unlimited" : globalLimit)); // List owned/member regions in a world List regions = psp.getPSRegions(world, true); // true = include as member player.sendMessage("You have " + regions.size() + " region(s) in " + world.getName()); // Cross-world home list List homes = psp.getHomes(world); homes.forEach(r -> player.sendMessage("Home: " + (r.getName() != null ? r.getName() : r.getId()))); } ``` ``` -------------------------------- ### Sell a Region Source: https://context7.com/espidev/protectionstones/llms.txt Lists a region for sale and handles the sale transfer. Requires the player to own the region. ```java import dev.espi.protectionstones.PSRegion; import org.bukkit.Location; import org.bukkit.entity.Player; import java.util.UUID; public void sellRegion(Player owner, Location regionLoc, double price) { PSRegion region = PSRegion.fromLocation(regionLoc); if (region == null || !region.isOwner(owner.getUniqueId())) { owner.sendMessage("You don't own a region here."); return; } // List for sale region.setSellable(true, owner.getUniqueId(), price); owner.sendMessage("Region listed for $" + price); // Later: a buyer clicks and calls: // UUID buyerUuid = buyer.getUniqueId(); // region.sell(buyerUuid); // transfers ownership and charges buyer } ``` -------------------------------- ### PSRegion.fromName(World, String) / PSRegion.fromName(String) Source: https://context7.com/espidev/protectionstones/llms.txt Finds all PS regions whose player-set nickname matches the given string. The single-argument overload searches all loaded worlds. ```APIDOC ## PSRegion.fromName(World, String) / PSRegion.fromName(String) ### Description Finds all PS regions whose player-set nickname (from `/ps name`) matches the given string. The single-argument overload searches all loaded worlds. ### Method Signatures * `List fromName(World world, String name)` * `Map> fromName(String name)` ### Parameters * **world** (World) - The world to search within (for the two-argument version). * **name** (String) - The nickname to search for. ### Returns * **List** - A list of PSRegion objects matching the name in the specified world. * **Map>** - A map where keys are worlds and values are lists of PSRegion objects matching the name across all loaded worlds. ``` -------------------------------- ### ProtectionStones.addCommandArgument(PSCommandArg) Source: https://context7.com/espidev/protectionstones/llms.txt Adds a custom sub-command to the `/ps` command tree at runtime, allowing plugins to integrate their own commands. ```APIDOC ## `ProtectionStones.addCommandArgument(PSCommandArg)` — Extend `/ps` Adds a custom sub-command to the `/ps` command tree at runtime. ### Description This method allows developers to extend the functionality of the ProtectionStones plugin by adding new sub-commands to the existing `/ps` command. This is useful for creating integrated workflows or custom region management tools. ### Method Signature `public void addCommandArgument(PSCommandArg argument)` ### Parameters - `argument` (PSCommandArg): An instance of a class implementing the `PSCommandArg` interface, defining the new sub-command's behavior. ### `PSCommandArg` Interface Methods - `getNames()`: Returns a `List` of aliases for the command (e.g., `["mycommand", "mc"]`). - `executeArgument(Player player, String[] args)`: The main logic for the command. Returns `true` if execution was successful, `false` to display usage. - `hasPermission(Player player)`: Determines if the player has permission to execute this command. - `tabComplete(Player player, String[] args)`: Provides tab completion suggestions for the command arguments. ### Example Usage ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.commands.PSCommandArg; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.util.Arrays; import java.util.List; public class MyPlugin extends JavaPlugin { @Override public void onEnable() { ProtectionStones ps = ProtectionStones.getInstance(); ps.addCommandArgument(new PSCommandArg() { @Override public List getNames() { return Arrays.asList("mycommand", "mc"); // /ps mycommand } @Override public boolean executeArgument(Player player, String[] args) { player.sendMessage("Hello from /ps mycommand! Args: " + Arrays.toString(args)); return true; // return false to show usage } @Override public boolean hasPermission(Player player) { return player.hasPermission("myplugin.pscommand"); } @Override public List tabComplete(Player player, String[] args) { return Arrays.asList("option1", "option2"); } }); } } ``` ``` -------------------------------- ### Validate WorldGuard ProtectedRegion as ProtectionStones Region Source: https://context7.com/espidev/protectionstones/llms.txt Use `isPSRegion` to check if a `ProtectedRegion` is fully configured by ProtectionStones. Use `isPSRegionFormat` to check if it merely follows the PS naming convention and has the `ps-block-material` flag, even if not fully configured. ```java import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.utils.WGUtils; import org.bukkit.World; public void listPSRegions(World world) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(world); if (rgm == null) return; for (ProtectedRegion region : rgm.getRegions().values()) { if (ProtectionStones.isPSRegion(region)) { // Fully configured PS region System.out.println("PS region: " + region.getId()); } else if (ProtectionStones.isPSRegionFormat(region)) { // Has PS format but block type is not in config (e.g., block removed from config) System.out.println("Orphaned PS-format region: " + region.getId()); } } } ``` -------------------------------- ### Extend /ps Command with addCommandArgument Source: https://context7.com/espidev/protectionstones/llms.txt Adds a custom sub-command to the /ps command tree at runtime by implementing the PSCommandArg interface. This allows for custom command logic and tab completion. ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.commands.PSCommandArg; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.util.Arrays; import java.util.List; public class MyPlugin extends JavaPlugin { @Override public void onEnable() { ProtectionStones ps = ProtectionStones.getInstance(); ps.addCommandArgument(new PSCommandArg() { @Override public List getNames() { return Arrays.asList("mycommand", "mc"); // /ps mycommand } @Override public boolean executeArgument(Player player, String[] args) { player.sendMessage("Hello from /ps mycommand! Args: " + Arrays.toString(args)); return true; // return false to show usage } @Override public boolean hasPermission(Player player) { return player.hasPermission("myplugin.pscommand"); } @Override public List tabComplete(Player player, String[] args) { return Arrays.asList("option1", "option2"); } }); } } ``` -------------------------------- ### PSProtectBlock Source: https://context7.com/espidev/protectionstones/llms.txt Represents a block's configuration, mirroring a TOML file. All fields are public and accessible at runtime for reading block properties. ```APIDOC ## `PSProtectBlock` — Block Config Fields `PSProtectBlock` mirrors a block TOML config file. All fields are public and can be read at runtime. ### Description This class provides access to the configuration settings for each type of protection block supported by the ProtectionStones plugin. It allows developers to retrieve details such as the block's type, its region dimensions, pricing, and behavioral flags. ### Accessing Block Configuration Use `ProtectionStones.getProtectBlockFromAlias(String alias)` to get a `PSProtectBlock` instance. ### Public Fields - **Identity** - `type` (String): The Bukkit material type of the block (e.g., "GOLD_BLOCK"). - `alias` (String): The short alias used for the block (e.g., "gold"). - `description` (String): A description of the block. - **Region Geometry** - `xRadius` (int): The half-width of the region on the X-axis. - `yRadius` (int): The half-height of the region on the Y-axis. `-1` indicates world height. - `zRadius` (int): The half-width of the region on the Z-axis. - **Economy** - `price` (double): The base price to buy the protection block. - `taxAmount` (double): The amount of tax applied periodically. - `taxPeriod` (long): The period (in seconds) for tax application. `-1` indicates disabled. - `costToPlace` (double): The cost to place the protection block. - **Behavior Flags** - `autoMerge` (boolean): Whether the region automatically merges with adjacent regions. - `autoHide` (boolean): Whether the region's protection block is automatically hidden. - `noDrop` (boolean): Whether the protection block item is dropped upon breaking. ### Creating Items - `createItem()`: Creates a properly NBT-tagged `ItemStack` for this protection block. ### Example Usage ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSProtectBlock; public void dumpBlockConfig(String alias) { PSProtectBlock b = ProtectionStones.getProtectBlockFromAlias(alias); if (b == null) { System.out.println("No block with alias: " + alias); return; } // Identity System.out.println("Type: " + b.type); System.out.println("Alias: " + b.alias); System.out.println("Description: " + b.description); // Region geometry System.out.println("X radius: " + b.xRadius); System.out.println("Y radius: " + b.yRadius); System.out.println("Z radius: " + b.zRadius); // Economy System.out.println("Price (buy): " + b.price); System.out.println("Tax amount: " + b.taxAmount); System.out.println("Tax period(s): " + b.taxPeriod); System.out.println("Cost to place: " + b.costToPlace); // Behaviour flags System.out.println("Auto merge: " + b.autoMerge); System.out.println("Auto hide: " + b.autoHide); System.out.println("No drop: " + b.noDrop); // Create the item directly from config b.createItem(); // returns properly NBT-tagged ItemStack } ``` ``` -------------------------------- ### Access PSProtectBlock Configuration Fields Source: https://context7.com/espidev/protectionstones/llms.txt Retrieves and displays configuration details for a ProtectionStones protect block using its alias. This allows runtime access to block properties like radius, price, and behavior flags. ```java import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.PSProtectBlock; public void dumpBlockConfig(String alias) { PSProtectBlock b = ProtectionStones.getProtectBlockFromAlias(alias); if (b == null) { System.out.println("No block with alias: " + alias); return; } // Identity System.out.println("Type: " + b.type); // e.g. "GOLD_BLOCK" System.out.println("Alias: " + b.alias); // e.g. "gold" System.out.println("Description: " + b.description); // Region geometry System.out.println("X radius: " + b.xRadius); // half-width on X System.out.println("Y radius: " + b.yRadius); // -1 = world height System.out.println("Z radius: " + b.zRadius); // Economy System.out.println("Price (buy): " + b.price); System.out.println("Tax amount: " + b.taxAmount); System.out.println("Tax period(s): " + b.taxPeriod); // seconds, -1 = disabled System.out.println("Cost to place: " + b.costToPlace); // Behaviour flags System.out.println("Auto merge: " + b.autoMerge); System.out.println("Auto hide: " + b.autoHide); System.out.println("No drop: " + b.noDrop); // Create the item directly from config b.createItem(); // returns properly NBT-tagged ItemStack } ``` -------------------------------- ### Iterate All PS Regions in a World Source: https://context7.com/espidev/protectionstones/llms.txt Use `PSRegion.fromWGRegion` to convert WorldGuard regions to PS regions. This method returns null if the region is not a PS region, so check for null before processing. ```java import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import dev.espi.protectionstones.PSRegion; import dev.espi.protectionstones.ProtectionStones; import dev.espi.protectionstones.utils.WGUtils; import org.bukkit.World; public void processAllPSRegions(World world) { RegionManager rgm = WGUtils.getRegionManagerWithWorld(world); if (rgm == null) return; for (ProtectedRegion wgr : rgm.getRegions().values()) { PSRegion psr = PSRegion.fromWGRegion(world, wgr); if (psr == null) continue; // not a PS region System.out.println(psr.getId() + " owners=" + psr.getOwners().size() + " members=" + psr.getMembers().size() + " taxRate=" + psr.getTaxRate()); } } ``` -------------------------------- ### ProtectionStones.createProtectBlockItem Source: https://context7.com/espidev/protectionstones/llms.txt Creates an ItemStack for a given protection block configuration. ```APIDOC ## `ProtectionStones.createProtectBlockItem(PSProtectBlock)` ### Description Creates a properly NBT-tagged `ItemStack` for a given protection block config, complete with display name, lore, enchanted effect, and custom model data as configured. ### Method Signature - `createProtectBlockItem(PSProtectBlock block)`: Generates an ItemStack for the specified protection block. ### Parameters #### Path Parameters - `block` (PSProtectBlock) - Required - The protection block configuration object. ### Response #### Success Response - `ItemStack`: An ItemStack representing the protection block item. ``` -------------------------------- ### Listen for Protection Block Break with PSBreakProtectBlockEvent Source: https://context7.com/espidev/protectionstones/llms.txt Handles the PSBreakProtectBlockEvent, which is fired when the physical protection block itself is broken. This can be used to enforce specific breaking conditions or log the event. ```java import dev.espi.protectionstones.event.PSBreakProtectBlockEvent; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; public class BreakBlockListener implements Listener { @EventHandler public void onBreak(PSBreakProtectBlockEvent event) { Player player = event.getPlayer(); ItemStack psItem = event.getPSItem(); // the protection block item // Example: require a confirmation permission before breaking if (player != null && !player.hasPermission("myplugin.canbreak")) { event.setCancelled(true); player.sendMessage("You cannot break protection blocks!"); return; } System.out.println("Protection block broken in region: " + event.getRegion().getId()); } } ``` -------------------------------- ### PSRegion.fromLocation Source: https://context7.com/espidev/protectionstones/llms.txt Resolves the innermost ProtectionStones region at a given world location. ```APIDOC ## `PSRegion.fromLocation(Location)` ### Description Resolves the innermost (closest) PS region at a given world location. Returns `null` if the location is not inside any PS region. May return a `PSStandardRegion`, `PSGroupRegion`, or `PSMergedRegion` instance. ### Method Signature - `fromLocation(Location location)`: Retrieves the PSRegion at the specified location. ### Parameters #### Path Parameters - `location` (Location) - Required - The world location to check. ### Response #### Success Response - `PSRegion`: The PSRegion instance at the given location, or `null` if none exists. ``` -------------------------------- ### PSPlayer.getRegionLimits() / getGlobalRegionLimits() Source: https://context7.com/espidev/protectionstones/llms.txt Retrieves region limits set via player permissions. Supports per-block-type limits and global limits, with offline player support when LuckPerms is enabled. ```APIDOC ## `PSPlayer.getRegionLimits()` / `getGlobalRegionLimits()` Reads per-block-type and global region limits from the player's permissions (`protectionstones.limit.alias.x` / `protectionstones.limit.x`). Supports offline players when LuckPerms is enabled. ### Description This functionality allows you to query the region claim limits assigned to a player through their permissions. It distinguishes between global limits (total claims allowed) and per-type limits (claims allowed for specific block types). This is particularly useful for enforcing server-wide or player-specific claim restrictions. ### Methods - `int getGlobalRegionLimits()`: Returns the player's global region limit. A return value of -1 indicates unlimited claims. - `Map getRegionLimits()`: Returns a map where keys are `PSProtectBlock` types and values are the corresponding integer limits for those types. ### Usage Examples ```java import dev.espi.protectionstones.PSPlayer; import dev.espi.protectionstones.PSProtectBlock; import org.bukkit.entity.Player; import java.util.Map; public void showLimits(Player player) { PSPlayer psp = PSPlayer.fromPlayer(player); int globalMax = psp.getGlobalRegionLimits(); player.sendMessage("Total claim limit: " + (globalMax < 0 ? "unlimited" : globalMax)); Map perType = psp.getRegionLimits(); if (perType.isEmpty()) { player.sendMessage("No per-type limits set."); } else { perType.forEach((block, limit) -> player.sendMessage(" " + block.alias + ": " + limit)); } } ``` ``` -------------------------------- ### PSRegion Ownership and Membership Methods Source: https://context7.com/espidev/protectionstones/llms.txt Methods for reading and modifying the owner and member sets of a region. ```APIDOC ## PSRegion Ownership and Membership ### Description Methods for reading and modifying the owner/member sets of a region. ### Methods * **`ArrayList getOwners()`** * Description: Returns a list of UUIDs for the region's owners. * **`ArrayList getMembers()`** * Description: Returns a list of UUIDs for the region's members. * **`boolean isMember(UUID uuid)`** * Description: Checks if a given UUID is a member or owner of the region. * **`void addMember(UUID uuid)`** * Description: Adds a UUID to the region's members. * **`void removeOwner(UUID uuid)`** * Description: Removes a UUID from the region's owners. This also clears landlord/autopayer side-effects. * **`void removeMember(UUID uuid)`** * Description: Removes a UUID from the region's members. ```