Claim protection from a local index: revision-polled panel endpoint, no per-block network checks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
10 files changed
+442
-127
No files matched your search
@@ -36,6 +36,8 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
});
|
||||
});
|
||||
Bukkit.getScheduler().runTaskTimer(this, integration::tick, 1, 1);
|
||||
// Claim protection runs from a local copy of the panel's claims, refreshed in the background.
|
||||
integration.startClaimSync();
|
||||
|
||||
// Central Live Map: mirror chunks and player positions to the panel.
|
||||
if (getConfig().getBoolean("livemap.enabled", true)) {
|
||||
@@ -171,22 +173,12 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
public void join(PlayerJoinEvent event) {
|
||||
if (integration != null) {
|
||||
integration.activity.join(event.getPlayer().getUniqueId(), event.getPlayer().getName());
|
||||
Chunk chunk = event.getPlayer().getLocation().getChunk();
|
||||
integration.prefetchClaims(dimension(chunk.getWorld()), chunk.getX(), chunk.getZ(), event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void move(PlayerMoveEvent event) {
|
||||
if (integration == null || event.getTo() == null) return;
|
||||
org.bukkit.Location from = event.getFrom(), to = event.getTo();
|
||||
if (from.getWorld() == to.getWorld() && (from.getBlockX() >> 4) == (to.getBlockX() >> 4)
|
||||
&& (from.getBlockZ() >> 4) == (to.getBlockZ() >> 4)) return;
|
||||
integration.prefetchClaims(dimension(to.getWorld()), to.getBlockX() >> 4, to.getBlockZ() >> 4, event.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void leave(PlayerQuitEvent event) {
|
||||
deniedAt.remove(event.getPlayer().getUniqueId());
|
||||
if (integration != null) integration.activity.leave(event.getPlayer().getUniqueId(), event.getPlayer().getName());
|
||||
}
|
||||
|
||||
@@ -200,33 +192,45 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
return integration != null && integration.settings().guildsEnabled() && integration.settings().landClaimingEnabled();
|
||||
}
|
||||
|
||||
private final java.util.Map<UUID, Long> deniedAt = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Would {@code player} be stopped from changing {@code block}? Answers from the
|
||||
* local claim index (no network, and block.getX() >> 4 rather than getChunk()
|
||||
* so a check never loads a chunk). Staff with scopenet.claims.bypass are exempt.
|
||||
*/
|
||||
private ChunkCheckResult denial(Player player, Block block) {
|
||||
if (!claimsEnabled() || player.hasPermission("scopenet.claims.bypass")) return null;
|
||||
ChunkCheckResult check = integration.checkChunk(dimension(block.getWorld()), block.getX() >> 4, block.getZ() >> 4, player.getUniqueId());
|
||||
return check.claimed() && !check.allowed() ? check : null;
|
||||
}
|
||||
|
||||
/** One "claimed" notice per second per player, however many events fire. */
|
||||
private void tell(Player player, ChunkCheckResult check) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = deniedAt.get(player.getUniqueId());
|
||||
if (last != null && now - last < 1000) return;
|
||||
deniedAt.put(player.getUniqueId(), now);
|
||||
player.sendMessage(ChatColor.RED + "This chunk is claimed by [" + check.guildTag() + "] " + check.guildName() + "!");
|
||||
}
|
||||
|
||||
private boolean protectedFrom(Player player, Block block) {
|
||||
if (!claimsEnabled()) return false;
|
||||
Chunk chunk = block.getChunk();
|
||||
ChunkCheckResult check = integration.checkChunk(dimension(block.getWorld()), chunk.getX(), chunk.getZ(), player.getUniqueId());
|
||||
if (check.allowed()) return false;
|
||||
player.sendMessage(ChatColor.RED + "This chunk is protected by a guild or the claim check is unavailable.");
|
||||
ChunkCheckResult check = denial(player, block);
|
||||
if (check == null) return false;
|
||||
tell(player, check);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Environmental changes (fire, fluids, pistons, explosions) never touch claimed land. */
|
||||
private boolean claimed(Block block) {
|
||||
if (!claimsEnabled()) return false;
|
||||
Chunk chunk = block.getChunk();
|
||||
// A non-member identity protects environmental changes, including
|
||||
// while the panel is unavailable and the lookup is pending.
|
||||
return integration.checkChunk(dimension(block.getWorld()), chunk.getX(), chunk.getZ(), new UUID(0, 0)).claimed();
|
||||
return claimsEnabled() && integration.isClaimed(dimension(block.getWorld()), block.getX() >> 4, block.getZ() >> 4);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void broken(BlockBreakEvent event) {
|
||||
if (integration != null && integration.settings().guildsEnabled() && integration.settings().landClaimingEnabled()) {
|
||||
Chunk chunk = event.getBlock().getChunk();
|
||||
ChunkCheckResult check = integration.checkChunk(dimension(event.getBlock().getWorld()), chunk.getX(), chunk.getZ(), event.getPlayer().getUniqueId());
|
||||
if (check.claimed() && !check.allowed()) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage(ChatColor.RED + "This chunk is claimed by [" + check.guildTag() + "] " + check.guildName() + "!");
|
||||
return;
|
||||
}
|
||||
if (protectedFrom(event.getPlayer(), event.getBlock())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
add(event.getPlayer(), "blocks_broken");
|
||||
if (integration != null) integration.activity.action(event.getPlayer().getUniqueId(), event.getPlayer().getName(),
|
||||
@@ -235,14 +239,9 @@ public final class ScopenetPlugin extends JavaPlugin implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void placed(BlockPlaceEvent event) {
|
||||
if (integration != null && integration.settings().guildsEnabled() && integration.settings().landClaimingEnabled()) {
|
||||
Chunk chunk = event.getBlock().getChunk();
|
||||
ChunkCheckResult check = integration.checkChunk(dimension(event.getBlock().getWorld()), chunk.getX(), chunk.getZ(), event.getPlayer().getUniqueId());
|
||||
if (check.claimed() && !check.allowed()) {
|
||||
event.setCancelled(true);
|
||||
event.getPlayer().sendMessage(ChatColor.RED + "This chunk is claimed by [" + check.guildTag() + "] " + check.guildName() + "!");
|
||||
return;
|
||||
}
|
||||
if (protectedFrom(event.getPlayer(), event.getBlock())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
add(event.getPlayer(), "blocks_placed");
|
||||
if (integration != null) integration.activity.action(event.getPlayer().getUniqueId(), event.getPlayer().getName(),
|
||||
|
||||
Reference in new issue
Block a user