diff --git a/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimPreTask.java b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimPreTask.java new file mode 100644 index 0000000..14c1981 --- /dev/null +++ b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimPreTask.java @@ -0,0 +1,49 @@ +/* + GriefPrevention Server Plugin for Minecraft + Copyright (C) 2012 Ryan Hamshire + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + + package me.ryanhamshire.GriefPrevention; + +import org.bukkit.Bukkit; + +//asynchronously loads player data without caching it in the datastore, then +//passes those data to a claim cleanup task which might decide to delete a claim for inactivity + +class CleanupUnusedClaimPreTask implements Runnable +{ + private Claim claim = null; + + CleanupUnusedClaimPreTask(Claim claim) + { + this.claim = claim; + } + + @Override + public void run() + { + //get the data + PlayerData ownerData = GriefPrevention.instance.dataStore.getPlayerDataFromStorage(claim.ownerID); + + //skip claims belonging to exempted players based on block totals in config + int bonusBlocks = ownerData.getBonusClaimBlocks(); + if(bonusBlocks >= GriefPrevention.instance.config_claims_expirationExemptionBonusBlocks) return; + if(bonusBlocks + ownerData.getAccruedClaimBlocks() >= GriefPrevention.instance.config_claims_expirationExemptionTotalBlocks) return; + + //pass it back to the main server thread, where it's safe to delete a claim if needed + Bukkit.getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new CleanupUnusedClaimTask(claim, ownerData), 1L); + } +} \ No newline at end of file diff --git a/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java similarity index 57% rename from src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java rename to src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java index 780e855..c62b386 100644 --- a/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimsTask.java +++ b/src/me/ryanhamshire/GriefPrevention/CleanupUnusedClaimTask.java @@ -1,188 +1,124 @@ -/* - GriefPrevention Server Plugin for Minecraft - Copyright (C) 2012 Ryan Hamshire - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - */ - - package me.ryanhamshire.GriefPrevention; - -import java.util.Calendar; -import java.util.Random; -import java.util.Vector; - -import org.bukkit.Chunk; -import org.bukkit.World; - -//FEATURE: automatically remove claims owned by inactive players which: -//...aren't protecting much OR -//...are a free new player claim (and the player has no other claims) OR -//...because the player has been gone a REALLY long time, and that expiration has been configured in config.yml - -//runs every 1 minute in the main thread -class CleanupUnusedClaimsTask implements Runnable -{ - int nextClaimIndex; - - CleanupUnusedClaimsTask() - { - //start scanning in a random spot - if(GriefPrevention.instance.dataStore.claims.size() == 0) - { - this.nextClaimIndex = 0; - } - else - { - Random randomNumberGenerator = new Random(); - this.nextClaimIndex = randomNumberGenerator.nextInt(GriefPrevention.instance.dataStore.claims.size()); - } - } - - @Override - public void run() - { - //don't do anything when there are no claims - if(GriefPrevention.instance.dataStore.claims.size() == 0) return; - - //wrap search around to beginning - if(this.nextClaimIndex >= GriefPrevention.instance.dataStore.claims.size()) this.nextClaimIndex = 0; - - //decide which claim to check next - Claim claim = GriefPrevention.instance.dataStore.claims.get(this.nextClaimIndex++); - - //skip administrative claims - if(claim.isAdminClaim()) return; - - //track whether we do any important work which would require cleanup afterward - boolean cleanupChunks = false; - - //get data for the player, especially last login timestamp - PlayerData playerData = null; - - //determine area of the default chest claim - int areaOfDefaultClaim = 0; - if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0) - { - areaOfDefaultClaim = (int)Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2); - } - - //if this claim is a chest claim and those are set to expire - if(claim.getArea() <= areaOfDefaultClaim && GriefPrevention.instance.config_claims_chestClaimExpirationDays > 0) - { - playerData = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerID); - - //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed - Calendar sevenDaysAgo = Calendar.getInstance(); - sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_chestClaimExpirationDays); - boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(playerData.getLastLogin()); - if(newPlayerClaimsExpired && playerData.getClaims().size() == 1) - { - claim.removeSurfaceFluids(null); - GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); - cleanupChunks = true; - - //if configured to do so, restore the land to natural - if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) - { - GriefPrevention.instance.restoreClaim(claim, 0); - } - - GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.", CustomLogEntryTypes.AdminActivity); - } - } - - //if configured to always remove claims after some inactivity period without exceptions... - else if(GriefPrevention.instance.config_claims_expirationDays > 0) - { - if(playerData == null) playerData = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerID); - Calendar earliestPermissibleLastLogin = Calendar.getInstance(); - earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays); - - if(earliestPermissibleLastLogin.getTime().after(playerData.getLastLogin())) - { - //make a copy of this player's claim list - Vector claims = new Vector(); - for(int i = 0; i < playerData.getClaims().size(); i++) - { - claims.add(playerData.getClaims().get(i)); - } - - //delete them - GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.ownerID, true); - GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.", CustomLogEntryTypes.AdminActivity); - - for(int i = 0; i < claims.size(); i++) - { - //if configured to do so, restore the land to natural - if(GriefPrevention.instance.creativeRulesApply(claims.get(i).getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) - { - GriefPrevention.instance.restoreClaim(claims.get(i), 0); - cleanupChunks = true; - } - } - } - } - - else if(GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0 && GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) - { - //avoid scanning large claims and administrative claims - if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return; - - //otherwise scan the claim content - int minInvestment = 400; - - long investmentScore = claim.getPlayerInvestmentScore(); - cleanupChunks = true; - - if(investmentScore < minInvestment) - { - playerData = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerID); - - //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed - Calendar sevenDaysAgo = Calendar.getInstance(); - sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays); - boolean claimExpired = sevenDaysAgo.getTime().after(playerData.getLastLogin()); - if(claimExpired) - { - GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); - GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity); - - //restore the claim area to natural state - GriefPrevention.instance.restoreClaim(claim, 0); - } - } - } - - if(playerData != null) GriefPrevention.instance.dataStore.clearCachedPlayerData(claim.ownerID); - - //since we're potentially loading a lot of chunks to scan parts of the world where there are no players currently playing, be mindful of memory usage - if(cleanupChunks) - { - World world = claim.getLesserBoundaryCorner().getWorld(); - Chunk lesserChunk = world.getChunkAt(claim.getLesserBoundaryCorner()); - Chunk greaterChunk = world.getChunkAt(claim.getGreaterBoundaryCorner()); - 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.isLoaded()) - { - chunk.unload(true, true); - } - } - } - } - } -} +/* + GriefPrevention Server Plugin for Minecraft + Copyright (C) 2012 Ryan Hamshire + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + + package me.ryanhamshire.GriefPrevention; + +import java.util.Calendar; +import java.util.Vector; + +class CleanupUnusedClaimTask implements Runnable +{ + Claim claim; + PlayerData ownerData; + + CleanupUnusedClaimTask(Claim claim, PlayerData ownerData) + { + this.claim = claim; + this.ownerData = ownerData; + } + + @Override + public void run() + { + //determine area of the default chest claim + int areaOfDefaultClaim = 0; + if(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius >= 0) + { + areaOfDefaultClaim = (int)Math.pow(GriefPrevention.instance.config_claims_automaticClaimsForNewPlayersRadius * 2 + 1, 2); + } + + //if this claim is a chest claim and those are set to expire + if(claim.getArea() <= areaOfDefaultClaim && GriefPrevention.instance.config_claims_chestClaimExpirationDays > 0) + { + //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed + Calendar sevenDaysAgo = Calendar.getInstance(); + sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_chestClaimExpirationDays); + boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(ownerData.getLastLogin()); + if(newPlayerClaimsExpired && ownerData.getClaims().size() == 1) + { + claim.removeSurfaceFluids(null); + GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); + + //if configured to do so, restore the land to natural + if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) + { + GriefPrevention.instance.restoreClaim(claim, 0); + } + + GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.", CustomLogEntryTypes.AdminActivity); + } + } + + //if configured to always remove claims after some inactivity period without exceptions... + else if(GriefPrevention.instance.config_claims_expirationDays > 0) + { + Calendar earliestPermissibleLastLogin = Calendar.getInstance(); + earliestPermissibleLastLogin.add(Calendar.DATE, -GriefPrevention.instance.config_claims_expirationDays); + + if(earliestPermissibleLastLogin.getTime().after(ownerData.getLastLogin())) + { + //make a copy of this player's claim list + Vector claims = new Vector(); + for(int i = 0; i < ownerData.getClaims().size(); i++) + { + claims.add(ownerData.getClaims().get(i)); + } + + //delete them + GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.ownerID, true); + GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.", CustomLogEntryTypes.AdminActivity); + + for(int i = 0; i < claims.size(); i++) + { + //if configured to do so, restore the land to natural + if(GriefPrevention.instance.creativeRulesApply(claims.get(i).getLesserBoundaryCorner()) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration) + { + GriefPrevention.instance.restoreClaim(claims.get(i), 0); + } + } + } + } + + else if(GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0 && GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner())) + { + //avoid scanning large claims and administrative claims + if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return; + + //otherwise scan the claim content + int minInvestment = 400; + + long investmentScore = claim.getPlayerInvestmentScore(); + + if(investmentScore < minInvestment) + { + //if the owner has been gone at least a week, and if he has ONLY the new player claim, it will be removed + Calendar sevenDaysAgo = Calendar.getInstance(); + sevenDaysAgo.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays); + boolean claimExpired = sevenDaysAgo.getTime().after(ownerData.getLastLogin()); + if(claimExpired) + { + GriefPrevention.instance.dataStore.deleteClaim(claim, true, true); + GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()), CustomLogEntryTypes.AdminActivity); + + //restore the claim area to natural state + GriefPrevention.instance.restoreClaim(claim, 0); + } + } + } + } +} diff --git a/src/me/ryanhamshire/GriefPrevention/DataStore.java b/src/me/ryanhamshire/GriefPrevention/DataStore.java index c1cbbae..d470672 100644 --- a/src/me/ryanhamshire/GriefPrevention/DataStore.java +++ b/src/me/ryanhamshire/GriefPrevention/DataStore.java @@ -730,6 +730,9 @@ public abstract class DataStore CreateClaimResult result = new CreateClaimResult(); int smallx, bigx, smally, bigy, smallz, bigz; + + if(y1 < GriefPrevention.instance.config_claims_maxDepth) y1 = GriefPrevention.instance.config_claims_maxDepth; + if(y2 < GriefPrevention.instance.config_claims_maxDepth) y2 = GriefPrevention.instance.config_claims_maxDepth; //determine small versus big inputs if(x1 < x2) diff --git a/src/me/ryanhamshire/GriefPrevention/FindUnusedClaimsTask.java b/src/me/ryanhamshire/GriefPrevention/FindUnusedClaimsTask.java new file mode 100644 index 0000000..c1135b0 --- /dev/null +++ b/src/me/ryanhamshire/GriefPrevention/FindUnusedClaimsTask.java @@ -0,0 +1,66 @@ +/* + GriefPrevention Server Plugin for Minecraft + Copyright (C) 2012 Ryan Hamshire + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + + package me.ryanhamshire.GriefPrevention; + +import java.util.Random; + +import org.bukkit.Bukkit; + +//FEATURE: automatically remove claims owned by inactive players which: +//...aren't protecting much OR +//...are a free new player claim (and the player has no other claims) OR +//...because the player has been gone a REALLY long time, and that expiration has been configured in config.yml + +//runs every 1 minute in the main thread +class FindUnusedClaimsTask implements Runnable +{ + int nextClaimIndex; + + FindUnusedClaimsTask() + { + //start scanning in a random spot + if(GriefPrevention.instance.dataStore.claims.size() == 0) + { + this.nextClaimIndex = 0; + } + else + { + Random randomNumberGenerator = new Random(); + this.nextClaimIndex = randomNumberGenerator.nextInt(GriefPrevention.instance.dataStore.claims.size()); + } + } + + @Override + public void run() + { + //don't do anything when there are no claims + if(GriefPrevention.instance.dataStore.claims.size() == 0) return; + + //wrap search around to beginning + if(this.nextClaimIndex >= GriefPrevention.instance.dataStore.claims.size()) this.nextClaimIndex = 0; + + //decide which claim to check next + Claim claim = GriefPrevention.instance.dataStore.claims.get(this.nextClaimIndex++); + + //skip administrative claims + if(claim.isAdminClaim()) return; + + Bukkit.getScheduler().runTaskAsynchronously(GriefPrevention.instance, new CleanupUnusedClaimPreTask(claim)); + } +} diff --git a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java index 824a298..3609b2a 100644 --- a/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java +++ b/src/me/ryanhamshire/GriefPrevention/GriefPrevention.java @@ -103,6 +103,8 @@ public class GriefPrevention extends JavaPlugin public int config_claims_maxAccruedBlocks; //the limit on accrued blocks (over time). doesn't limit purchased or admin-gifted blocks public int config_claims_maxDepth; //limit on how deep claims can go public int config_claims_expirationDays; //how many days of inactivity before a player loses his claims + public int config_claims_expirationExemptionTotalBlocks; //total claim blocks amount which will exempt a player from claim expiration + public int config_claims_expirationExemptionBonusBlocks; //bonus claim blocks amount which will exempt a player from claim expiration public int config_claims_automaticClaimsForNewPlayersRadius; //how big automatic new player claims (when they place a chest) should be. 0 to disable public int config_claims_claimsExtendIntoGroundDistance; //how far below the shoveled block a new claim will reach @@ -311,8 +313,8 @@ public class GriefPrevention extends JavaPlugin this.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, task, 20L * 60 * 2); //start recurring cleanup scan for unused claims belonging to inactive players - CleanupUnusedClaimsTask task2 = new CleanupUnusedClaimsTask(); - this.getServer().getScheduler().scheduleSyncRepeatingTask(this, task2, 20L * 60 * 2, 20L * 60 * 5); + FindUnusedClaimsTask task2 = new FindUnusedClaimsTask(); + this.getServer().getScheduler().scheduleSyncRepeatingTask(this, task2, 20L * 60, 20L * 60); //register for events PluginManager pluginManager = this.getServer().getPluginManager(); @@ -525,7 +527,9 @@ public class GriefPrevention extends JavaPlugin this.config_claims_maxDepth = config.getInt("GriefPrevention.Claims.MaximumDepth", 0); this.config_claims_chestClaimExpirationDays = config.getInt("GriefPrevention.Claims.Expiration.ChestClaimDays", 7); this.config_claims_unusedClaimExpirationDays = config.getInt("GriefPrevention.Claims.Expiration.UnusedClaimDays", 14); - this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.Expiration.AllClaimDays", 0); + this.config_claims_expirationDays = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.DaysInactive", 60); + this.config_claims_expirationExemptionTotalBlocks = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasTotalClaimBlocks", 10000); + this.config_claims_expirationExemptionBonusBlocks = config.getInt("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasBonusClaimBlocks", 5000); this.config_claims_survivalAutoNatureRestoration = config.getBoolean("GriefPrevention.Claims.Expiration.AutomaticNatureRestoration.SurvivalWorlds", false); this.config_claims_maxClaimsPerPlayer = config.getInt("GriefPrevention.Claims.MaximumNumberOfClaimsPerPlayer", 0); this.config_claims_respectWorldGuard = config.getBoolean("GriefPrevention.Claims.CreationRequiresWorldGuardBuildPermission", true); @@ -760,7 +764,9 @@ public class GriefPrevention extends JavaPlugin outConfig.set("GriefPrevention.Claims.ModificationTool", this.config_claims_modificationTool.name()); outConfig.set("GriefPrevention.Claims.Expiration.ChestClaimDays", this.config_claims_chestClaimExpirationDays); outConfig.set("GriefPrevention.Claims.Expiration.UnusedClaimDays", this.config_claims_unusedClaimExpirationDays); - outConfig.set("GriefPrevention.Claims.Expiration.AllClaimDays", this.config_claims_expirationDays); + outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.DaysInactive", this.config_claims_expirationDays); + outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasTotalClaimBlocks", this.config_claims_expirationExemptionTotalBlocks); + outConfig.set("GriefPrevention.Claims.Expiration.AllClaims.ExceptWhenOwnerHasBonusClaimBlocks", this.config_claims_expirationExemptionBonusBlocks); outConfig.set("GriefPrevention.Claims.Expiration.AutomaticNatureRestoration.SurvivalWorlds", this.config_claims_survivalAutoNatureRestoration); outConfig.set("GriefPrevention.Claims.MaximumNumberOfClaimsPerPlayer", this.config_claims_maxClaimsPerPlayer); outConfig.set("GriefPrevention.Claims.CreationRequiresWorldGuardBuildPermission", this.config_claims_respectWorldGuard);