From 0bed62739cc84c2a62c7c1be1db16b1baaac42f4 Mon Sep 17 00:00:00 2001 From: Ryan Hamshire Date: Mon, 30 Apr 2012 20:11:39 -0700 Subject: [PATCH] 3.4.1 --- plugin.yml | 10 +++- .../ryanhamshire/GriefPrevention/Claim.java | 19 +++++- .../GriefPrevention/DataStore.java | 58 +++++++++++++++++-- .../GriefPrevention/EntityEventHandler.java | 5 +- .../GriefPrevention/GriefPrevention.java | 50 ++++++++++++++++ .../GriefPrevention/PlayerEventHandler.java | 18 +++--- .../RestoreNatureProcessingTask.java | 3 +- .../GriefPrevention/SiegeCheckupTask.java | 6 +- 8 files changed, 148 insertions(+), 21 deletions(-) diff --git a/plugin.yml b/plugin.yml index 5a3abc2..e050e52 100644 --- a/plugin.yml +++ b/plugin.yml @@ -1,7 +1,7 @@ name: GriefPrevention main: me.ryanhamshire.GriefPrevention.GriefPrevention -softdepend: [Vault] -version: 3.3.2 +softdepend: [Vault, Multiverse-Core] +version: 3.4.1 commands: abandonclaim: description: Deletes a claim. @@ -89,7 +89,11 @@ commands: deletealladminclaims: description: Deletes all administrative claims. usage: /DeleteAllAdminClaims - permission: adminclaims + permission: griefprevention.adminclaims + transferclaim: + description: Converts an administrative claim to a private claim. + usage: /TransferClaim + permission: griefprevention.adjustclaimblocks permissions: griefprevention.createclaims: description: Grants permission to create claims. diff --git a/src/me/ryanhamshire/GriefPrevention/Claim.java b/src/me/ryanhamshire/GriefPrevention/Claim.java index ba90251..5417488 100644 --- a/src/me/ryanhamshire/GriefPrevention/Claim.java +++ b/src/me/ryanhamshire/GriefPrevention/Claim.java @@ -31,7 +31,7 @@ import org.bukkit.entity.Player; //represents a player claim //creating an instance doesn't make an effective claim //only claims which have been added to the datastore have any effect -public class Claim +public class Claim { //two locations, which together define the boundaries of the claim //note that the upper Y value is always ignored, because claims ALWAYS extend up to the sky @@ -553,4 +553,21 @@ public class Claim return false; } + + //implements a strict ordering of claims, used to keep the claims collection sorted for faster searching + boolean greaterThan(Claim otherClaim) + { + Location thisCorner = this.getLesserBoundaryCorner(); + Location otherCorner = otherClaim.getLesserBoundaryCorner(); + + if(thisCorner.getBlockX() > otherCorner.getBlockX()) return true; + + if(thisCorner.getBlockX() < otherCorner.getBlockX()) return false; + + if(thisCorner.getBlockZ() > otherCorner.getBlockZ()) return true; + + if(thisCorner.getBlockZ() < otherCorner.getBlockZ()) return false; + + return thisCorner.getWorld().getName().compareTo(otherCorner.getWorld().getName()) < 0; + } } diff --git a/src/me/ryanhamshire/GriefPrevention/DataStore.java b/src/me/ryanhamshire/GriefPrevention/DataStore.java index c564eb7..8ee5cb2 100644 --- a/src/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/me/ryanhamshire/GriefPrevention/DataStore.java @@ -26,6 +26,7 @@ import java.util.*; import org.bukkit.*; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; //singleton class which manages all GriefPrevention data (except for config options) public class DataStore @@ -125,8 +126,13 @@ public class DataStore else { topLevelClaim.modifiedDate = new Date(files[i].lastModified()); - this.claims.add(topLevelClaim); - topLevelClaim.inDataStore = true; + int j = 0; + while(j < this.claims.size() && !this.claims.get(j).greaterThan(topLevelClaim)) j++; + if(j < this.claims.size()) + this.claims.add(j, topLevelClaim); + else + this.claims.add(this.claims.size(), topLevelClaim); + topLevelClaim.inDataStore = true; } } @@ -277,7 +283,12 @@ public class DataStore } //add it and mark it as added - this.claims.add(newClaim); + int j = 0; + while(j < this.claims.size() && !this.claims.get(j).greaterThan(newClaim)) j++; + if(j < this.claims.size()) + this.claims.add(j, newClaim); + else + this.claims.add(this.claims.size(), newClaim); newClaim.inDataStore = true; //except for administrative claims (which have no owner), update the owner's playerData with the new claim @@ -619,11 +630,20 @@ public class DataStore //check cachedClaim guess first. if it's in the datastore and the location is inside it, we're done if(cachedClaim != null && cachedClaim.inDataStore && cachedClaim.contains(location, ignoreHeight, true)) return cachedClaim; + //the claims list is ordered by greater boundary corner + //create a temporary "fake" claim in memory for comparison purposes + Claim tempClaim = new Claim(); + tempClaim.lesserBoundaryCorner = location; + //otherwise, search all existing claims until we find the right claim for(int i = 0; i < this.claims.size(); i++) { Claim claim = this.claims.get(i); + //if we reach a claim which is greater than the temp claim created above, there's definitely no claim + //in the collection which includes our location + if(claim.greaterThan(tempClaim)) return null; + //find a top level claim if(claim.contains(location, ignoreHeight, false)) { @@ -848,7 +868,7 @@ public class DataStore //ends a siege //either winnerName or loserName can be null, but not both - public void endSiege(SiegeData siegeData, String winnerName, String loserName) + public void endSiege(SiegeData siegeData, String winnerName, String loserName, boolean death) { boolean grantAccess = false; @@ -930,6 +950,36 @@ public class DataStore GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60 * 5); } } + + //if the siege ended due to death, transfer inventory to winner + if(death) + { + Player winner = GriefPrevention.instance.getServer().getPlayer(winnerName); + Player loser = GriefPrevention.instance.getServer().getPlayer(loserName); + if(winner != null && loser != null) + { + //get loser's inventory, then clear it + ItemStack [] loserItems = loser.getInventory().getContents(); + loser.getInventory().clear(); + + //try to add it to the winner's inventory + for(int j = 0; j < loserItems.length; j++) + { + if(loserItems[j] == null || loserItems[j].getType() == Material.AIR || loserItems[j].getAmount() == 0) continue; + + HashMap wontFitItems = winner.getInventory().addItem(loserItems[j]); + + //drop any remainder on the ground at his feet + Object [] keys = wontFitItems.keySet().toArray(); + Location winnerLocation = winner.getLocation(); + for(int i = 0; i < keys.length; i++) + { + Integer key = (Integer)keys[i]; + winnerLocation.getWorld().dropItemNaturally(winnerLocation, wontFitItems.get(key)); + } + } + } + } } //timestamp for each siege cooldown to end diff --git a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java index 07f3d0d..e9858da 100644 --- a/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/EntityEventHandler.java @@ -112,8 +112,11 @@ class EntityEventHandler implements Listener //if involved in a siege if(playerData.siegeData != null) { + //don't drop items as usual, they will be sent to the siege winner + event.getDrops().clear(); + //end it, with the dieing player being the loser - this.dataStore.endSiege(playerData.siegeData, null, player.getName()); + this.dataStore.endSiege(playerData.siegeData, null, player.getName(), true /*ended due to death*/); } } diff --git a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 96e25aa..866d88f 100644 --- a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -469,6 +469,56 @@ public class GriefPrevention extends JavaPlugin return true; } + //transferclaim + else if(cmd.getName().equalsIgnoreCase("transferclaim") && player != null) + { + //requires exactly one parameter, the other player's name + if(args.length != 1) return false; + + //check additional permission + if(!player.hasPermission("griefprevention.adminclaims")) + { + GriefPrevention.sendMessage(player, TextMode.Err, "That command requires the administrative claims permission."); + return true; + } + + //which claim is the user in? + Claim claim = this.dataStore.getClaimAt(player.getLocation(), true, null); + if(claim == null) + { + GriefPrevention.sendMessage(player, TextMode.Instr, "There's no claim here. Stand in the administrative claim you want to transfer."); + return true; + } + else if(!claim.isAdminClaim()) + { + GriefPrevention.sendMessage(player, TextMode.Err, "Only administrative claims may be transferred to a player."); + return true; + } + + OfflinePlayer targetPlayer = this.resolvePlayer(args[0]); + if(targetPlayer == null) + { + GriefPrevention.sendMessage(player, TextMode.Err, "Player not found."); + return true; + } + + //change ownerhsip + try + { + this.dataStore.changeClaimOwner(claim, targetPlayer.getName()); + } + catch(Exception e) + { + GriefPrevention.sendMessage(player, TextMode.Instr, "Only top level claims (not subdivisions) may be transferred. Stand outside of the subdivision and try again."); + return true; + } + + //confirm + GriefPrevention.sendMessage(player, TextMode.Success, "Claim transferred."); + + return true; + } + //trustlist else if(cmd.getName().equalsIgnoreCase("trustlist") && player != null) { diff --git a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java index cbc24b3..8012e93 100644 --- a/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java +++ b/src/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java @@ -73,6 +73,12 @@ class PlayerEventHandler implements Listener if(!GriefPrevention.instance.config_spam_enabled) return; + //remedy any CAPS SPAM without bothering to fault the player for it + if(message.length() > 4 && !player.hasPermission("griefprevention.spam") && message.toUpperCase().equals(message)) + { + event.setMessage(message.toLowerCase()); + } + PlayerData playerData = this.dataStore.getPlayerData(player.getName()); boolean spam = false; @@ -166,7 +172,8 @@ class PlayerEventHandler implements Listener { GriefPrevention.sendMessage(player, TextMode.Warn, GriefPrevention.instance.config_spam_warningMessage); event.setCancelled(true); - GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + "."); + GriefPrevention.AddLogEntry("Warned " + player.getName() + " about spam penalties."); + GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message); } } @@ -273,17 +280,12 @@ class PlayerEventHandler implements Listener player.setHealth(0); } - //FEATURE: on logout or kick, give player the any claim blocks he may have earned for this play session - //NOTE: not all kicks are bad, for example an AFK kick or a kick to make room for an admin to log in - //that's why even kicked players get their claim blocks - //FEATURE: during a siege, any player who logs out dies and forfeits the siege //if player was involved in a siege, he forfeits if(playerData.siegeData != null) { - player.setHealth(0); - this.dataStore.endSiege(playerData.siegeData, null, player.getName()); + if(player.getHealth() > 0) player.setHealth(0); //might already be zero from above, this avoids a double death message } //disable ignore claims mode @@ -773,7 +775,7 @@ class PlayerEventHandler implements Listener if(playerData.shovelMode == ShovelMode.RestoreNature) { //if the clicked block is in a claim, visualize that claim and deliver an error message - Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), true, playerData.lastClaim); + Claim claim = this.dataStore.getClaimAt(clickedBlock.getLocation(), false, playerData.lastClaim); if(claim != null) { GriefPrevention.sendMessage(player, TextMode.Err, claim.getOwnerName() + " claimed that block."); diff --git a/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java b/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java index 9721627..a88662b 100644 --- a/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java +++ b/src/me/ryanhamshire/GriefPrevention/RestoreNatureProcessingTask.java @@ -62,6 +62,7 @@ class RestoreNatureProcessingTask implements Runnable this.notAllowedToHang = new ArrayList(); this.notAllowedToHang.add(Material.DIRT.getId()); this.notAllowedToHang.add(Material.GRASS.getId()); + this.notAllowedToHang.add(Material.LONG_GRASS.getId()); this.notAllowedToHang.add(Material.SNOW.getId()); this.notAllowedToHang.add(Material.LOG.getId()); @@ -454,7 +455,7 @@ class RestoreNatureProcessingTask implements Runnable private int highestY(int x, int z) { int y; - for(y = snapshots[0].length - 1; y >= 0; y--) + for(y = snapshots[0].length - 1; y > 0; y--) { BlockSnapshot block = this.snapshots[x][y][z]; if(block.typeId != Material.AIR.getId() && diff --git a/src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java b/src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java index d680417..efed065 100644 --- a/src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java +++ b/src/me/ryanhamshire/GriefPrevention/SiegeCheckupTask.java @@ -65,13 +65,13 @@ class SiegeCheckupTask implements Runnable //otherwise attacker wins if the defender runs away else if(attackerRemains && !defenderRemains) { - dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName()); + dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), false); } //or defender wins if the attacker leaves else if(!attackerRemains && defenderRemains) { - dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName()); + dataStore.endSiege(this.siegeData, defender.getName(), attacker.getName(), false); } //if they both left, but are still close together, the battle continues (check again later) @@ -83,7 +83,7 @@ class SiegeCheckupTask implements Runnable //otherwise they both left and aren't close to each other, so call the attacker the winner (defender escaped, possibly after a chase) else { - dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName()); + dataStore.endSiege(this.siegeData, attacker.getName(), defender.getName(), false); } }