Add claim access permissions

This commit is contained in:
CoalGov Deploy
2026-05-04 18:00:51 +00:00
parent e65097ed2c
commit 8b5ff1f92f
13 changed files with 527 additions and 5 deletions

View File

@@ -9,13 +9,16 @@ import com.librewiki.coalgov.command.MarketCommand;
import com.librewiki.coalgov.command.NpcCommand;
import com.librewiki.coalgov.command.PermitCommand;
import com.librewiki.coalgov.command.PoliceCommand;
import com.librewiki.coalgov.command.ShowPropertyLinesCommand;
import com.librewiki.coalgov.listener.BlockBreakListener;
import com.librewiki.coalgov.listener.BlockPlaceListener;
import com.librewiki.coalgov.listener.AnimalProtectionListener;
import com.librewiki.coalgov.listener.ClaimWandListener;
import com.librewiki.coalgov.listener.DiviningRodListener;
import com.librewiki.coalgov.listener.MarketListener;
import com.librewiki.coalgov.listener.NpcListener;
import com.librewiki.coalgov.listener.PlayerJoinListener;
import com.librewiki.coalgov.listener.PropertyInteractListener;
import com.librewiki.coalgov.listener.SuperFurnaceListener;
import com.librewiki.coalgov.service.BuildProtectionService;
import com.librewiki.coalgov.service.ClaimMarkerService;
@@ -125,12 +128,15 @@ public final class CoalGovPlugin extends JavaPlugin {
Objects.requireNonNull(getCommand("land")).setExecutor(new LandCommand(this));
Objects.requireNonNull(getCommand("market")).setExecutor(new MarketCommand(this));
Objects.requireNonNull(getCommand("diviningrod")).setExecutor(new DiviningRodCommand(this));
Objects.requireNonNull(getCommand("showpropertylines")).setExecutor(new ShowPropertyLinesCommand(this));
Objects.requireNonNull(getCommand("police")).setExecutor(new PoliceCommand(this));
Objects.requireNonNull(getCommand("cgnpc")).setExecutor(new NpcCommand(this));
Objects.requireNonNull(getCommand("coalgov")).setExecutor(new AdminCommand(this));
getServer().getPluginManager().registerEvents(new BlockBreakListener(this), this);
getServer().getPluginManager().registerEvents(new BlockPlaceListener(this), this);
getServer().getPluginManager().registerEvents(new PropertyInteractListener(this), this);
getServer().getPluginManager().registerEvents(new AnimalProtectionListener(this), this);
getServer().getPluginManager().registerEvents(new ClaimWandListener(this), this);
getServer().getPluginManager().registerEvents(new DiviningRodListener(this), this);
getServer().getPluginManager().registerEvents(new MarketListener(this), this);

View File

@@ -3,6 +3,8 @@ package com.librewiki.coalgov.command;
import com.librewiki.coalgov.CoalGovPlugin;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPoint;
import com.librewiki.coalgov.model.ClaimPermission;
import com.librewiki.coalgov.model.ClaimPermissionGrant;
import com.librewiki.coalgov.model.LandRegion;
import com.librewiki.coalgov.service.ClaimService;
import com.librewiki.coalgov.service.LandAppraisalService;
@@ -16,8 +18,11 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public final class ClaimCommand implements CommandExecutor {
private final CoalGovPlugin plugin;
@@ -33,7 +38,7 @@ public final class ClaimCommand implements CommandExecutor {
return true;
}
if (args.length == 0) {
player.sendMessage(plugin.messages().text("&eUsage: /claim <wand|buy|info|list|show|appraise|sell|transfer|abandon>"));
usage(player);
return true;
}
try {
@@ -48,7 +53,10 @@ public final class ClaimCommand implements CommandExecutor {
case "sell" -> sell(player, args);
case "transfer" -> transfer(player, args);
case "abandon" -> abandon(player, args);
default -> player.sendMessage(plugin.messages().text("&eUsage: /claim <wand|buy|info|list|show|appraise|sell|transfer|abandon>"));
case "trust" -> trust(player, args);
case "untrust" -> untrust(player, args);
case "permissions" -> permissions(player, args);
default -> usage(player);
}
} catch (SQLException exception) {
plugin.getLogger().warning("Claim command failed: " + exception.getMessage());
@@ -57,6 +65,10 @@ public final class ClaimCommand implements CommandExecutor {
return true;
}
private void usage(Player player) {
player.sendMessage(plugin.messages().text("&eUsage: /claim <wand|buy|info|list|show|appraise|sell|transfer|abandon|trust|untrust|permissions>"));
}
private void buy(Player player, String[] args) throws SQLException {
if (args.length != 2 && args.length != 3) {
player.sendMessage(plugin.messages().text("&eUsage: /claim buy <homestead|industrial> [radius]"));
@@ -200,6 +212,96 @@ public final class ClaimCommand implements CommandExecutor {
}
}
private void trust(Player player, String[] args) throws SQLException {
if (args.length != 5) {
player.sendMessage(plugin.messages().text("&eUsage: /claim trust <id> <player> <build|interact|container|animal|manage|all>"));
return;
}
long id = parseLong(args[1]);
List<ClaimPermission> permissions = ClaimPermission.grantSet(args[4]);
if (id <= 0L || permissions.isEmpty()) {
player.sendMessage(plugin.messages().text("&cInvalid claim id or permission."));
return;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[2]);
plugin.economyService().ensurePlayer(target);
for (ClaimPermission permission : permissions) {
ClaimService.PermissionResult result = plugin.claimService()
.grantPermission(player.getUniqueId(), id, target.getUniqueId(), permission);
if (!result.success()) {
player.sendMessage(plugin.messages().text("&c" + result.message()));
return;
}
}
player.sendMessage(plugin.messages().text("&aGranted &f" + args[4].toLowerCase() + " &aaccess on claim #" + id + " to &f" + targetName(target) + "&a."));
}
private void untrust(Player player, String[] args) throws SQLException {
if (args.length != 5) {
player.sendMessage(plugin.messages().text("&eUsage: /claim untrust <id> <player> <build|interact|container|animal|manage|all>"));
return;
}
long id = parseLong(args[1]);
if (id <= 0L) {
player.sendMessage(plugin.messages().text("&cInvalid claim id."));
return;
}
OfflinePlayer target = Bukkit.getOfflinePlayer(args[2]);
if (args[4].equalsIgnoreCase("all")) {
ClaimService.PermissionResult result = plugin.claimService()
.revokeAllPermissions(player.getUniqueId(), id, target.getUniqueId());
if (!result.success()) {
player.sendMessage(plugin.messages().text("&c" + result.message()));
return;
}
player.sendMessage(plugin.messages().text("&aRemoved all claim access for &f" + targetName(target) + "&a."));
return;
}
Optional<ClaimPermission> permission = ClaimPermission.parse(args[4]);
if (permission.isEmpty()) {
player.sendMessage(plugin.messages().text("&cInvalid permission."));
return;
}
ClaimService.PermissionResult result = plugin.claimService()
.revokePermission(player.getUniqueId(), id, target.getUniqueId(), permission.get());
if (!result.success()) {
player.sendMessage(plugin.messages().text("&c" + result.message()));
return;
}
player.sendMessage(plugin.messages().text("&aRemoved &f" + permission.get().label() + " &aaccess for &f" + targetName(target) + "&a."));
}
private void permissions(Player player, String[] args) throws SQLException {
if (args.length != 2) {
player.sendMessage(plugin.messages().text("&eUsage: /claim permissions <id>"));
return;
}
long id = parseLong(args[1]);
if (id <= 0L) {
player.sendMessage(plugin.messages().text("&cInvalid claim id."));
return;
}
List<ClaimPermissionGrant> grants = plugin.claimService().permissions(player.getUniqueId(), id);
if (grants.isEmpty()) {
player.sendMessage(plugin.messages().text("&eNo permissions listed for claim #" + id + ", or you cannot manage that claim."));
return;
}
Map<UUID, StringBuilder> byPlayer = new LinkedHashMap<>();
for (ClaimPermissionGrant grant : grants) {
byPlayer.computeIfAbsent(grant.playerUuid(), ignored -> new StringBuilder());
StringBuilder builder = byPlayer.get(grant.playerUuid());
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(grant.permission().label());
}
player.sendMessage(plugin.messages().text("&eClaim #" + id + " permissions:"));
for (var entry : byPlayer.entrySet()) {
OfflinePlayer target = Bukkit.getOfflinePlayer(entry.getKey());
player.sendMessage(plugin.messages().raw("&7" + targetName(target) + ": &f" + entry.getValue()));
}
}
private Optional<Claim> resolveOwnedClaim(Player player, String[] args, String action) throws SQLException {
if (args.length > 2) {
player.sendMessage(plugin.messages().text("&eUsage: /claim " + action + " [id]"));
@@ -261,6 +363,10 @@ public final class ClaimCommand implements CommandExecutor {
return material.name().toLowerCase().replace('_', ' ');
}
private String targetName(OfflinePlayer target) {
return target.getName() == null ? target.getUniqueId().toString() : target.getName();
}
private void abandon(Player player, String[] args) throws SQLException {
if (args.length != 2) {
player.sendMessage(plugin.messages().text("&eUsage: /claim abandon <id>"));

View File

@@ -0,0 +1,40 @@
package com.librewiki.coalgov.command;
import com.librewiki.coalgov.CoalGovPlugin;
import com.librewiki.coalgov.model.Claim;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.sql.SQLException;
import java.util.Optional;
public final class ShowPropertyLinesCommand implements CommandExecutor {
private final CoalGovPlugin plugin;
public ShowPropertyLinesCommand(CoalGovPlugin plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("Players only.");
return true;
}
try {
Optional<Claim> claim = plugin.claimService().claimAt(player.getLocation());
if (claim.isEmpty()) {
player.sendMessage(plugin.messages().text("&eYou are not standing inside a claim."));
return true;
}
plugin.claimVisualService().show(player, claim.get());
player.sendMessage(plugin.messages().text("&aShowing property lines for claim #" + claim.get().id() + "."));
} catch (SQLException exception) {
plugin.getLogger().warning("Show property lines failed: " + exception.getMessage());
player.sendMessage(plugin.messages().text("&cClaim registry unavailable."));
}
return true;
}
}

View File

@@ -0,0 +1,55 @@
package com.librewiki.coalgov.listener;
import com.librewiki.coalgov.CoalGovPlugin;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPermission;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import java.sql.SQLException;
import java.util.Optional;
public final class AnimalProtectionListener implements Listener {
private final CoalGovPlugin plugin;
public AnimalProtectionListener(CoalGovPlugin plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onEntityDamage(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof Animals)) {
return;
}
Player player = attackingPlayer(event.getDamager());
if (player == null || plugin.hasAdminBypass(player)) {
return;
}
try {
Optional<Claim> claim = plugin.claimService().claimAt(event.getEntity().getLocation());
if (claim.isEmpty() || plugin.claimService().canUse(player.getUniqueId(), claim.get(), ClaimPermission.ANIMAL)) {
return;
}
event.setCancelled(true);
player.sendMessage(plugin.messages().text("&cYou do not have animal access on this claim."));
} catch (SQLException exception) {
event.setCancelled(true);
plugin.getLogger().warning("Animal protection check failed: " + exception.getMessage());
player.sendMessage(plugin.messages().text("&cLand registry unavailable. Try again later."));
}
}
private Player attackingPlayer(Entity damager) {
if (damager instanceof Player player) {
return player;
}
if (damager instanceof org.bukkit.entity.Projectile projectile && projectile.getShooter() instanceof Player player) {
return player;
}
return null;
}
}

View File

@@ -0,0 +1,53 @@
package com.librewiki.coalgov.listener;
import com.librewiki.coalgov.CoalGovPlugin;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPermission;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.InventoryHolder;
import java.sql.SQLException;
import java.util.Optional;
public final class PropertyInteractListener implements Listener {
private final CoalGovPlugin plugin;
public PropertyInteractListener(CoalGovPlugin plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
if (plugin.hasAdminBypass(event.getPlayer()) || event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Block block = event.getClickedBlock();
if (block == null || !block.getType().isInteractable()) {
return;
}
try {
Optional<Claim> claim = plugin.claimService().claimAt(block.getLocation());
if (claim.isEmpty()) {
return;
}
ClaimPermission permission = isContainer(block) ? ClaimPermission.CONTAINER : ClaimPermission.INTERACT;
if (plugin.claimService().canUse(event.getPlayer().getUniqueId(), claim.get(), permission)) {
return;
}
event.setCancelled(true);
event.getPlayer().sendMessage(plugin.messages().text("&cYou do not have " + permission.label() + " access on this claim."));
} catch (SQLException exception) {
event.setCancelled(true);
plugin.getLogger().warning("Property interaction check failed: " + exception.getMessage());
event.getPlayer().sendMessage(plugin.messages().text("&cLand registry unavailable. Try again later."));
}
}
private boolean isContainer(Block block) {
return block.getState() instanceof InventoryHolder;
}
}

View File

@@ -0,0 +1,30 @@
package com.librewiki.coalgov.model;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public enum ClaimPermission {
BUILD,
INTERACT,
CONTAINER,
ANIMAL,
MANAGE;
public static Optional<ClaimPermission> parse(String raw) {
return Arrays.stream(values())
.filter(permission -> permission.name().equalsIgnoreCase(raw))
.findFirst();
}
public static List<ClaimPermission> grantSet(String raw) {
if (raw.equalsIgnoreCase("all")) {
return List.of(values());
}
return parse(raw).map(List::of).orElse(List.of());
}
public String label() {
return name().toLowerCase();
}
}

View File

@@ -0,0 +1,11 @@
package com.librewiki.coalgov.model;
import java.util.UUID;
public record ClaimPermissionGrant(
long claimId,
UUID playerUuid,
ClaimPermission permission,
long grantedAt
) {
}

View File

@@ -2,6 +2,7 @@ package com.librewiki.coalgov.service;
import com.librewiki.coalgov.CoalGovPlugin;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPermission;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -25,7 +26,7 @@ public final class BuildProtectionService {
if (claim.isEmpty()) {
return BuildDecision.deny("This land is unclaimed and protected by the Ministry.");
}
if (!claim.get().ownerUuid().equals(player.getUniqueId())) {
if (!claimService.canUse(player.getUniqueId(), claim.get(), ClaimPermission.BUILD)) {
return BuildDecision.deny("This claim belongs to another citizen.");
}
return BuildDecision.allow();

View File

@@ -2,6 +2,8 @@ package com.librewiki.coalgov.service;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPoint;
import com.librewiki.coalgov.model.ClaimPermission;
import com.librewiki.coalgov.model.ClaimPermissionGrant;
import com.librewiki.coalgov.storage.ClaimRepository;
import com.librewiki.coalgov.util.CoalMoney;
import com.librewiki.coalgov.util.Cuboid2D;
@@ -142,6 +144,66 @@ public final class ClaimService {
return TransferResult.success(claim.get());
}
public boolean canUse(UUID playerUuid, Claim claim, ClaimPermission permission) throws SQLException {
if (claim.ownerUuid().equals(playerUuid)) {
return true;
}
return repository.hasPermission(claim.id(), playerUuid, permission)
|| repository.hasPermission(claim.id(), playerUuid, ClaimPermission.MANAGE);
}
public PermissionResult grantPermission(UUID actor, long claimId, UUID target, ClaimPermission permission) throws SQLException {
Optional<Claim> claim = findManageableClaim(actor, claimId);
if (claim.isEmpty()) {
return PermissionResult.fail("No owned or manageable claim with that id.");
}
if (claim.get().ownerUuid().equals(target)) {
return PermissionResult.fail("The owner already has all permissions.");
}
repository.grantPermission(claimId, target, permission);
return PermissionResult.ok();
}
public PermissionResult revokePermission(UUID actor, long claimId, UUID target, ClaimPermission permission) throws SQLException {
Optional<Claim> claim = findManageableClaim(actor, claimId);
if (claim.isEmpty()) {
return PermissionResult.fail("No owned or manageable claim with that id.");
}
return repository.revokePermission(claimId, target, permission)
? PermissionResult.ok()
: PermissionResult.fail("That player did not have that permission.");
}
public PermissionResult revokeAllPermissions(UUID actor, long claimId, UUID target) throws SQLException {
Optional<Claim> claim = findManageableClaim(actor, claimId);
if (claim.isEmpty()) {
return PermissionResult.fail("No owned or manageable claim with that id.");
}
return repository.revokeAllPermissions(claimId, target)
? PermissionResult.ok()
: PermissionResult.fail("That player had no permissions on that claim.");
}
public List<ClaimPermissionGrant> permissions(UUID actor, long claimId) throws SQLException {
Optional<Claim> claim = findManageableClaim(actor, claimId);
if (claim.isEmpty()) {
return List.of();
}
return repository.permissions(claimId);
}
private Optional<Claim> findManageableClaim(UUID actor, long claimId) throws SQLException {
Optional<Claim> owned = findOwnedById(actor, claimId);
if (owned.isPresent()) {
return owned;
}
Optional<Claim> claim = repository.findById(claimId);
if (claim.isPresent() && repository.hasPermission(claimId, actor, ClaimPermission.MANAGE)) {
return claim;
}
return Optional.empty();
}
public long claimPurchaseCost(Claim claim) {
if (claim.purchaseCost() >= 0L) {
return claim.purchaseCost();
@@ -222,4 +284,14 @@ public final class ClaimService {
return new TransferResult(true, "", claim);
}
}
public record PermissionResult(boolean success, String message) {
public static PermissionResult fail(String message) {
return new PermissionResult(false, message);
}
public static PermissionResult ok() {
return new PermissionResult(true, "");
}
}
}

View File

@@ -2,6 +2,8 @@ package com.librewiki.coalgov.storage;
import com.librewiki.coalgov.model.Claim;
import com.librewiki.coalgov.model.ClaimPoint;
import com.librewiki.coalgov.model.ClaimPermission;
import com.librewiki.coalgov.model.ClaimPermissionGrant;
import com.librewiki.coalgov.util.Cuboid2D;
import java.sql.PreparedStatement;
@@ -69,6 +71,11 @@ public final class ClaimRepository {
deleteVertices.setLong(1, id);
deleteVertices.executeUpdate();
}
try (PreparedStatement deletePermissions = database.connection().prepareStatement(
"DELETE FROM claim_permissions WHERE claim_id = ?")) {
deletePermissions.setLong(1, id);
deletePermissions.executeUpdate();
}
try (PreparedStatement delete = database.connection().prepareStatement(
"DELETE FROM claims WHERE id = ? AND owner_uuid = ?")) {
delete.setLong(1, id);
@@ -134,13 +141,109 @@ public final class ClaimRepository {
}
}
public Optional<Claim> findById(long id) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement(
"SELECT * FROM claims WHERE id = ?")) {
statement.setLong(1, id);
try (ResultSet result = statement.executeQuery()) {
return result.next() ? Optional.of(map(result)) : Optional.empty();
}
}
}
public boolean transferOwned(UUID owner, long id, UUID newOwner) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement(
"UPDATE claims SET owner_uuid = ? WHERE id = ? AND owner_uuid = ?")) {
statement.setString(1, newOwner.toString());
statement.setLong(2, id);
statement.setString(3, owner.toString());
return statement.executeUpdate() == 1;
boolean transferred = statement.executeUpdate() == 1;
if (transferred) {
clearPermissions(id);
}
return transferred;
}
}
public void grantPermission(long claimId, UUID playerUuid, ClaimPermission permission) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement("""
INSERT INTO claim_permissions(claim_id, player_uuid, permission, granted_at)
VALUES (?, ?, ?, ?)
ON CONFLICT(claim_id, player_uuid, permission) DO UPDATE SET granted_at = excluded.granted_at
""")) {
statement.setLong(1, claimId);
statement.setString(2, playerUuid.toString());
statement.setString(3, permission.name());
statement.setLong(4, System.currentTimeMillis());
statement.executeUpdate();
}
}
public boolean revokePermission(long claimId, UUID playerUuid, ClaimPermission permission) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement("""
DELETE FROM claim_permissions
WHERE claim_id = ? AND player_uuid = ? AND permission = ?
""")) {
statement.setLong(1, claimId);
statement.setString(2, playerUuid.toString());
statement.setString(3, permission.name());
return statement.executeUpdate() > 0;
}
}
public boolean revokeAllPermissions(long claimId, UUID playerUuid) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement("""
DELETE FROM claim_permissions
WHERE claim_id = ? AND player_uuid = ?
""")) {
statement.setLong(1, claimId);
statement.setString(2, playerUuid.toString());
return statement.executeUpdate() > 0;
}
}
public boolean hasPermission(long claimId, UUID playerUuid, ClaimPermission permission) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement("""
SELECT 1 FROM claim_permissions
WHERE claim_id = ? AND player_uuid = ? AND permission = ?
LIMIT 1
""")) {
statement.setLong(1, claimId);
statement.setString(2, playerUuid.toString());
statement.setString(3, permission.name());
try (ResultSet result = statement.executeQuery()) {
return result.next();
}
}
}
public List<ClaimPermissionGrant> permissions(long claimId) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement("""
SELECT * FROM claim_permissions
WHERE claim_id = ?
ORDER BY player_uuid, permission
""")) {
statement.setLong(1, claimId);
List<ClaimPermissionGrant> grants = new ArrayList<>();
try (ResultSet result = statement.executeQuery()) {
while (result.next()) {
grants.add(new ClaimPermissionGrant(
result.getLong("claim_id"),
UUID.fromString(result.getString("player_uuid")),
ClaimPermission.valueOf(result.getString("permission")),
result.getLong("granted_at")
));
}
}
return grants;
}
}
private void clearPermissions(long claimId) throws SQLException {
try (PreparedStatement statement = database.connection().prepareStatement(
"DELETE FROM claim_permissions WHERE claim_id = ?")) {
statement.setLong(1, claimId);
statement.executeUpdate();
}
}

View File

@@ -84,6 +84,15 @@ public final class Database {
PRIMARY KEY (claim_id, vertex_order)
)
""");
statement.execute("""
CREATE TABLE IF NOT EXISTS claim_permissions (
claim_id INTEGER NOT NULL,
player_uuid TEXT NOT NULL,
permission TEXT NOT NULL,
granted_at INTEGER NOT NULL,
PRIMARY KEY (claim_id, player_uuid, permission)
)
""");
statement.execute("""
CREATE TABLE IF NOT EXISTS land_regions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -204,6 +213,8 @@ public final class Database {
""");
statement.execute("CREATE INDEX IF NOT EXISTS idx_claims_location ON claims(world, x1, z1, x2, z2)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_claim_vertices_claim ON claim_vertices(claim_id)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_claim_permissions_claim ON claim_permissions(claim_id)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_claim_permissions_player ON claim_permissions(player_uuid)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_land_location ON land_regions(world, x1, z1, x2, z2)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_permits_owner ON permits(owner_uuid, permit_type, region_name)");
statement.execute("CREATE INDEX IF NOT EXISTS idx_fines_player ON fines(player_uuid, paid_at)");

View File

@@ -13,7 +13,7 @@ commands:
permission: coalgov.coal
claim:
description: CoalGov claim commands.
usage: /claim <wand|buy|info|list|show|appraise|sell|transfer|abandon>
usage: /claim <wand|buy|info|list|show|appraise|sell|transfer|abandon|trust|untrust|permissions>
permission: coalgov.claim
permit:
description: CoalGov permit commands.
@@ -30,6 +30,10 @@ commands:
description: Receive a CoalGov divining rod.
usage: /diviningrod
permission: coalgov.diviningrod
showpropertylines:
description: Show the property lines for the claim you are standing in.
usage: /showpropertylines
permission: coalgov.claim
police:
description: CoalGov police commands.
usage: /police <fine>