This commit is contained in:
Ryan Hamshire 2012-11-07 17:36:25 -08:00
parent 6632af96df
commit e09a6732ba
12 changed files with 8839 additions and 8696 deletions

View File

@ -1,178 +1,178 @@
name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault, Multiverse-Core, My Worlds, MystCraft, Transporter]
dev-url: http://dev.bukkit.org/server-mods/grief-prevention
version: 6.7
commands:
abandonclaim:
description: Deletes a claim.
usage: /AbandonClaim
permission: griefprevention.claims
abandontoplevelclaim:
description: Deletes a claim and all its subdivisions.
usage: /AbandonTopLevelClaim
permission: griefprevention.claims
abandonallclaims:
description: Deletes ALL your claims.
usage: /AbandonAllClaims
permission: griefprevention.claims
trust:
description: Grants a player full access to your claim(s).
usage: /Trust <player> Graants a player permission to build. See also /UnTrust, /ContainerTrust, /AccessTrust, and /PermissionTrust.
aliases: tr
permission: griefprevention.claims
untrust:
description: Revokes a player's access to your claim(s).
usage: /UnTrust <player>
aliases: ut
permission: griefprevention.claims
containertrust:
description: Grants a player access to your containers.
usage: /ContainerTrust <player>. Grants a player access to your inventory, bed, and buttons/levers.
aliases: ct
permission: griefprevention.claims
accesstrust:
description: Grants a player entry to your claim(s) and use of your bed.
usage: /AccessTrust <player>. Grants a player access to your bed, buttons, and levers.
aliases: at
permission: griefprevention.claims
permissiontrust:
description: Grants a player permission to grant his level of permission to others.
usage: /PermissionTrust <player>. Permits a player to share his permission level with others.
aliases: pt
permission: griefprevention.claims
subdivideclaims:
description: Switches the shovel tool to subdivision mode, used to subdivide your claims.
usage: /SubdivideClaims
aliases: sc
permission: griefprevention.claims
adjustbonusclaimblocks:
description: Adds or subtracts bonus claim blocks for a player.
usage: /AdjustBonusClaimBlocks <player> <amount>
permission: griefprevention.adjustclaimblocks
aliases: acb
deleteclaim:
description: Deletes the claim you're standing in, even if it's not your claim.
usage: /DeleteClaim
permission: griefprevention.deleteclaims
aliases: dc
deleteallclaims:
description: Deletes all of another player's claims.
usage: /DeleteAllClaims <player>
permission: griefprevention.deleteclaims
adminclaims:
description: Switches the shovel tool to administrative claims mode.
usage: /AdminClaims
permission: griefprevention.adminclaims
aliases: ac
restorenature:
description: Switches the shovel tool to restoration mode.
usage: /RestoreNature
permission: griefprevention.restorenature
aliases: rn
restorenatureaggressive:
description: Switches the shovel tool to aggressive restoration mode.
usage: /RestoreNatureAggressive
permission: griefprevention.restorenatureaggressive
aliases: rna
restorenaturefill:
description: Switches the shovel tool to fill mode.
usage: /RestoreNatureFill <radius>
permission: griefprevention.restorenatureaggressive
aliases: rnf
basicclaims:
description: Switches the shovel tool back to basic claims mode.
usage: /BasicClaims
aliases: bc
permission: griefprevention.claims
buyclaimblocks:
description: Purchases additional claim blocks with server money. Doesn't work on servers without a Vault-compatible economy plugin.
usage: /BuyClaimBlocks <numberOfBlocks>
aliases: buyclaim
permission: griefprevention.claims
sellclaimblocks:
description: Sells your claim blocks for server money. Doesn't work on servers without a Vault-compatible economy plugin.
usage: /SellClaimBlocks <numberOfBlocks>
aliases: sellclaim
permission: griefprevention.claims
trapped:
description: Ejects you to nearby unclaimed land. Has a substantial cooldown period.
usage: /Trapped
trustlist:
description: Lists permissions for the claim you're standing in.
usage: /TrustList
permission: griefprevention.claims
siege:
description: Initiates a siege versus another player.
usage: /Siege <playerName>
ignoreclaims:
description: Toggles ignore claims mode.
usage: /IgnoreClaims
permission: griefprevention.ignoreclaims
aliases: ic
deletealladminclaims:
description: Deletes all administrative claims.
usage: /DeleteAllAdminClaims
permission: griefprevention.adminclaims
transferclaim:
description: Converts an administrative claim to a private claim.
usage: /TransferClaim <player>
permission: griefprevention.adjustclaimblocks
deathblow:
description: Kills a player, optionally giving his inventory to another player.
usage: /DeathBlow <player> [recipientPlayer]
permission: griefprevention.deathblow
claimslist:
description: Lists information about a player's claim blocks and claims.
usage: /ClaimsList <player>
permission: griefprevention.adjustclaimblocks
permissions:
griefprevention.createclaims:
description: Grants permission to create claims.
default: op
griefprevention.admin.*:
description: Grants all administrative functionality.
children:
griefprevention.restorenature: true
griefprevention.restorenatureaggressive: true
griefprevention.ignoreclaims: true
griefprevention.adminclaims: true
griefprevention.adjustclaimblocks: true
griefprevention.deleteclaims: true
griefprevention.spam: true
griefprevention.lava: true
griefprevention.eavesdrop: true
griefprevention.deathblow: true
griefprevention.restorenature:
description: Grants permission to use /RestoreNature.
default: op
griefprevention.ignoreclaims:
description: Grants permission to use /IgnoreClaims.
default: op
griefprevention.adminclaims:
description: Grants permission to create administrative claims.
default: op
griefprevention.deleteclaims:
description: Grants permission to delete other players' claims.
default: op
griefprevention.adjustclaimblocks:
description: Grants permission to add or remove bonus blocks from a player's account.
default: op
griefprevention.spam:
description: Grants permission to log in, send messages, and send commands rapidly.
default: op
griefprevention.lava:
description: Grants permission to place lava near the surface and outside of claims.
default: op
griefprevention.eavesdrop:
description: Allows a player to see whispered chat messages (/tell).
default: op
griefprevention.restorenatureaggressive:
description: Grants access to /RestoreNatureAggressive and /RestoreNatureFill.
default: op
griefprevention.deathblow:
description: Grants access to /DeathBlow.
default: op
griefprevention.claims:
description: Grants access to claim-related slash commands.
name: GriefPrevention
main: me.ryanhamshire.GriefPrevention.GriefPrevention
softdepend: [Vault, Multiverse-Core, My Worlds, MystCraft, Transporter]
dev-url: http://dev.bukkit.org/server-mods/grief-prevention
version: 6.9
commands:
abandonclaim:
description: Deletes a claim.
usage: /AbandonClaim
permission: griefprevention.claims
abandontoplevelclaim:
description: Deletes a claim and all its subdivisions.
usage: /AbandonTopLevelClaim
permission: griefprevention.claims
abandonallclaims:
description: Deletes ALL your claims.
usage: /AbandonAllClaims
permission: griefprevention.claims
trust:
description: Grants a player full access to your claim(s).
usage: /Trust <player> Graants a player permission to build. See also /UnTrust, /ContainerTrust, /AccessTrust, and /PermissionTrust.
aliases: tr
permission: griefprevention.claims
untrust:
description: Revokes a player's access to your claim(s).
usage: /UnTrust <player>
aliases: ut
permission: griefprevention.claims
containertrust:
description: Grants a player access to your containers.
usage: /ContainerTrust <player>. Grants a player access to your inventory, bed, and buttons/levers.
aliases: ct
permission: griefprevention.claims
accesstrust:
description: Grants a player entry to your claim(s) and use of your bed.
usage: /AccessTrust <player>. Grants a player access to your bed, buttons, and levers.
aliases: at
permission: griefprevention.claims
permissiontrust:
description: Grants a player permission to grant his level of permission to others.
usage: /PermissionTrust <player>. Permits a player to share his permission level with others.
aliases: pt
permission: griefprevention.claims
subdivideclaims:
description: Switches the shovel tool to subdivision mode, used to subdivide your claims.
usage: /SubdivideClaims
aliases: sc
permission: griefprevention.claims
adjustbonusclaimblocks:
description: Adds or subtracts bonus claim blocks for a player.
usage: /AdjustBonusClaimBlocks <player> <amount>
permission: griefprevention.adjustclaimblocks
aliases: acb
deleteclaim:
description: Deletes the claim you're standing in, even if it's not your claim.
usage: /DeleteClaim
permission: griefprevention.deleteclaims
aliases: dc
deleteallclaims:
description: Deletes all of another player's claims.
usage: /DeleteAllClaims <player>
permission: griefprevention.deleteclaims
adminclaims:
description: Switches the shovel tool to administrative claims mode.
usage: /AdminClaims
permission: griefprevention.adminclaims
aliases: ac
restorenature:
description: Switches the shovel tool to restoration mode.
usage: /RestoreNature
permission: griefprevention.restorenature
aliases: rn
restorenatureaggressive:
description: Switches the shovel tool to aggressive restoration mode.
usage: /RestoreNatureAggressive
permission: griefprevention.restorenatureaggressive
aliases: rna
restorenaturefill:
description: Switches the shovel tool to fill mode.
usage: /RestoreNatureFill <radius>
permission: griefprevention.restorenatureaggressive
aliases: rnf
basicclaims:
description: Switches the shovel tool back to basic claims mode.
usage: /BasicClaims
aliases: bc
permission: griefprevention.claims
buyclaimblocks:
description: Purchases additional claim blocks with server money. Doesn't work on servers without a Vault-compatible economy plugin.
usage: /BuyClaimBlocks <numberOfBlocks>
aliases: buyclaim
permission: griefprevention.claims
sellclaimblocks:
description: Sells your claim blocks for server money. Doesn't work on servers without a Vault-compatible economy plugin.
usage: /SellClaimBlocks <numberOfBlocks>
aliases: sellclaim
permission: griefprevention.claims
trapped:
description: Ejects you to nearby unclaimed land. Has a substantial cooldown period.
usage: /Trapped
trustlist:
description: Lists permissions for the claim you're standing in.
usage: /TrustList
permission: griefprevention.claims
siege:
description: Initiates a siege versus another player.
usage: /Siege <playerName>
ignoreclaims:
description: Toggles ignore claims mode.
usage: /IgnoreClaims
permission: griefprevention.ignoreclaims
aliases: ic
deletealladminclaims:
description: Deletes all administrative claims.
usage: /DeleteAllAdminClaims
permission: griefprevention.adminclaims
transferclaim:
description: Converts an administrative claim to a private claim.
usage: /TransferClaim <player>
permission: griefprevention.adjustclaimblocks
deathblow:
description: Kills a player, optionally giving his inventory to another player.
usage: /DeathBlow <player> [recipientPlayer]
permission: griefprevention.deathblow
claimslist:
description: Lists information about a player's claim blocks and claims.
usage: /ClaimsList <player>
permission: griefprevention.adjustclaimblocks
permissions:
griefprevention.createclaims:
description: Grants permission to create claims.
default: op
griefprevention.admin.*:
description: Grants all administrative functionality.
children:
griefprevention.restorenature: true
griefprevention.restorenatureaggressive: true
griefprevention.ignoreclaims: true
griefprevention.adminclaims: true
griefprevention.adjustclaimblocks: true
griefprevention.deleteclaims: true
griefprevention.spam: true
griefprevention.lava: true
griefprevention.eavesdrop: true
griefprevention.deathblow: true
griefprevention.restorenature:
description: Grants permission to use /RestoreNature.
default: op
griefprevention.ignoreclaims:
description: Grants permission to use /IgnoreClaims.
default: op
griefprevention.adminclaims:
description: Grants permission to create administrative claims.
default: op
griefprevention.deleteclaims:
description: Grants permission to delete other players' claims.
default: op
griefprevention.adjustclaimblocks:
description: Grants permission to add or remove bonus blocks from a player's account.
default: op
griefprevention.spam:
description: Grants permission to log in, send messages, and send commands rapidly.
default: op
griefprevention.lava:
description: Grants permission to place lava near the surface and outside of claims.
default: op
griefprevention.eavesdrop:
description: Allows a player to see whispered chat messages (/tell).
default: op
griefprevention.restorenatureaggressive:
description: Grants access to /RestoreNatureAggressive and /RestoreNatureFill.
default: op
griefprevention.deathblow:
description: Grants access to /DeathBlow.
default: op
griefprevention.claims:
description: Grants access to claim-related slash commands.
default: true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,189 +1,215 @@
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import java.util.Calendar;
import java.util.Random;
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;
//get data for the player, especially last login timestamp
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerName);
//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 he's been gone at least a week, if he has ONLY the new player claim, it will be removed
Calendar sevenDaysAgo = Calendar.getInstance();
sevenDaysAgo.add(Calendar.DATE, -7);
boolean newPlayerClaimsExpired = sevenDaysAgo.getTime().after(playerData.lastLogin);
//if only one claim, and the player hasn't played in a week
if(newPlayerClaimsExpired && playerData.claims.size() == 1)
{
//if that's a chest claim, delete it
if(claim.getArea() <= areaOfDefaultClaim)
{
claim.removeSurfaceFluids(null);
GriefPrevention.instance.dataStore.deleteClaim(claim);
//if in a creative mode world, delete the claim
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.");
}
}
//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(playerData.lastLogin))
{
GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.getOwnerName(), true);
GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.");
}
}
else
{
//if the player has been gone two weeks, scan claim content to assess player investment
Calendar fourteenDaysAgo = Calendar.getInstance();
fourteenDaysAgo.add(Calendar.DATE, -14);
boolean needsInvestmentScan = fourteenDaysAgo.getTime().after(playerData.lastLogin);
//avoid scanning large claims and administrative claims
if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return;
//if creative mode or the claim owner has been away a long enough time, scan the claim content
if(needsInvestmentScan || GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
int minInvestment;
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
minInvestment = 400;
}
else
{
minInvestment = 200;
}
long investmentScore = claim.getPlayerInvestmentScore();
boolean removeClaim = false;
//in creative mode, a build which is almost entirely lava above sea level will be automatically removed, even if the owner is an active player
//lava above the surface deducts 10 points per block from the investment score
//so 500 blocks of lava without anything built to offset all that potential mess would be cleaned up automatically
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) && investmentScore < -5000)
{
removeClaim = true;
}
//otherwise, the only way to get a claim automatically removed based on build investment is to be away for two weeks AND not build much of anything
else if(needsInvestmentScan && investmentScore < minInvestment)
{
removeClaim = true;
}
if(removeClaim)
{
GriefPrevention.instance.dataStore.deleteClaim(claim);
GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()));
//if in a creative mode world, restore the claim area
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
}
//toss that player data out of the cache, it's probably not needed in memory right now
if(!GriefPrevention.instance.getServer().getOfflinePlayer(claim.ownerName).isOnline())
{
GriefPrevention.instance.dataStore.clearCachedPlayerData(claim.ownerName);
}
//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
//unfortunately, java/minecraft don't do a good job of clearing unused memory, leading to out of memory errors from this type of world scanning
if(this.nextClaimIndex % 20 == 0)
{
World world = claim.getLesserBoundaryCorner().getWorld();
Chunk [] chunks = world.getLoadedChunks();
for(int i = 0; i < chunks.length; i++)
{
Chunk chunk = chunks[i];
chunk.unload(true, true);
}
System.gc();
}
}
}
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
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 = GriefPrevention.instance.dataStore.getPlayerData(claim.ownerName);
//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 he's been gone at least a week, 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.lastLogin);
//if only one claim, and the player hasn't played in a week
if(newPlayerClaimsExpired && playerData.claims.size() == 1)
{
//if that's a chest claim and those are set to expire
if(claim.getArea() <= areaOfDefaultClaim && GriefPrevention.instance.config_claims_chestClaimExpirationDays > 0)
{
claim.removeSurfaceFluids(null);
GriefPrevention.instance.dataStore.deleteClaim(claim);
cleanupChunks = true;
//if configured to do so, restore the land to natural
if((GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) && GriefPrevention.instance.config_claims_creativeAutoNatureRestoration) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
GriefPrevention.AddLogEntry(" " + claim.getOwnerName() + "'s new player claim expired.");
}
}
//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(playerData.lastLogin))
{
//make a copy of this player's claim list
Vector<Claim> claims = new Vector<Claim>();
for(int i = 0; i < playerData.claims.size(); i++)
{
claims.add(playerData.claims.get(i));
}
//delete them
GriefPrevention.instance.dataStore.deleteClaimsForPlayer(claim.getOwnerName(), true);
GriefPrevention.AddLogEntry(" All of " + claim.getOwnerName() + "'s claims have expired.");
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_creativeAutoNatureRestoration) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claims.get(i), 0);
cleanupChunks = true;
}
}
}
}
else if(GriefPrevention.instance.config_claims_unusedClaimExpirationDays > 0)
{
//if the player has been gone two weeks, scan claim content to assess player investment
Calendar earliestAllowedLoginDate = Calendar.getInstance();
earliestAllowedLoginDate.add(Calendar.DATE, -GriefPrevention.instance.config_claims_unusedClaimExpirationDays);
boolean needsInvestmentScan = earliestAllowedLoginDate.getTime().after(playerData.lastLogin);
//avoid scanning large claims and administrative claims
if(claim.isAdminClaim() || claim.getWidth() > 25 || claim.getHeight() > 25) return;
//if creative mode or the claim owner has been away a long enough time, scan the claim content
if(needsInvestmentScan || GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
int minInvestment;
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()))
{
minInvestment = 400;
}
else
{
minInvestment = 100;
}
long investmentScore = claim.getPlayerInvestmentScore();
cleanupChunks = true;
boolean removeClaim = false;
//in creative mode, a build which is almost entirely lava above sea level will be automatically removed, even if the owner is an active player
//lava above the surface deducts 10 points per block from the investment score
//so 500 blocks of lava without anything built to offset all that potential mess would be cleaned up automatically
if(GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) && investmentScore < -5000)
{
removeClaim = true;
}
//otherwise, the only way to get a claim automatically removed based on build investment is to be away for two weeks AND not build much of anything
else if(needsInvestmentScan && investmentScore < minInvestment)
{
removeClaim = true;
}
if(removeClaim)
{
GriefPrevention.instance.dataStore.deleteClaim(claim);
GriefPrevention.AddLogEntry("Removed " + claim.getOwnerName() + "'s unused claim @ " + GriefPrevention.getfriendlyLocationString(claim.getLesserBoundaryCorner()));
//if configured to do so, restore the claim area to natural state
if((GriefPrevention.instance.creativeRulesApply(claim.getLesserBoundaryCorner()) && GriefPrevention.instance.config_claims_creativeAutoNatureRestoration) || GriefPrevention.instance.config_claims_survivalAutoNatureRestoration)
{
GriefPrevention.instance.restoreClaim(claim, 0);
}
}
}
}
//toss that player data out of the cache, it's probably not needed in memory right now
if(!GriefPrevention.instance.getServer().getOfflinePlayer(claim.ownerName).isOnline())
{
GriefPrevention.instance.dataStore.clearCachedPlayerData(claim.ownerName);
}
//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 [] chunks = world.getLoadedChunks();
for(int i = 0; i < chunks.length; i++)
{
Chunk chunk = chunks[i];
chunk.unload(true, true);
}
}
//unfortunately, java/minecraft don't do a good job of clearing unused memory, leading to out of memory errors from this type of world scanning
if(this.nextClaimIndex % 10 == 0)
{
System.gc();
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
package me.ryanhamshire.GriefPrevention;
public enum Messages
{
RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionDemo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, TrappedOnCooldown, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, TrustCommandAdvertisement, GoldenShovelAdvertisement, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsDemoAdvertisement, SurvivalBasicsDemoAdvertisement, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeClaimTooSmall, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooSmall, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, UntrustOwnerOnly, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime, BuildingOutsideClaims, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured, NoTeleportPvPCombat, NoTNTDamageAboveSeaLevel, NoTNTDamageClaims
}
package me.ryanhamshire.GriefPrevention;
public enum Messages
{
RespectingClaims, IgnoringClaims, SuccessfulAbandon, RestoreNatureActivate, RestoreNatureAggressiveActivate, FillModeActive, TransferClaimPermission, TransferClaimMissing, TransferClaimAdminOnly, PlayerNotFound, TransferTopLevel, TransferSuccess, TrustListNoClaim, ClearPermsOwnerOnly, UntrustIndividualAllClaims, UntrustEveryoneAllClaims, NoPermissionTrust, ClearPermissionsOneClaim, UntrustIndividualSingleClaim, OnlySellBlocks, BlockPurchaseCost, ClaimBlockLimit, InsufficientFunds, PurchaseConfirmation, OnlyPurchaseBlocks, BlockSaleValue, NotEnoughBlocksForSale, BlockSaleConfirmation, AdminClaimsMode, BasicClaimsMode, SubdivisionMode, SubdivisionDemo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, TrappedOnCooldown, RescuePending, NonSiegeWorld, AlreadySieging, NotSiegableThere, SiegeTooFarAway, NoSiegeDefenseless, AlreadyUnderSiegePlayer, AlreadyUnderSiegeArea, NoSiegeAdminClaim, SiegeOnCooldown, SiegeAlert, SiegeConfirmed, AbandonClaimMissing, NotYourClaim, DeleteTopLevelClaim, AbandonSuccess, CantGrantThatPermission, GrantPermissionNoClaim, GrantPermissionConfirmation, ManageUniversalPermissionsInstruction, ManageOneClaimPermissionsInstruction, CollectivePublic, BuildPermission, ContainersPermission, AccessPermission, PermissionsPermission, LocationCurrentClaim, LocationAllClaims, PvPImmunityStart, SiegeNoDrop, DonateItemsInstruction, ChestFull, DonationSuccess, PlayerTooCloseForFire, TooDeepToClaim, ChestClaimConfirmation, AutomaticClaimNotification, TrustCommandAdvertisement, GoldenShovelAdvertisement, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsDemoAdvertisement, SurvivalBasicsDemoAdvertisement, TrappedChatKeyword, TrappedInstructions, PvPNoDrop, SiegeNoTeleport, BesiegedNoTeleport, SiegeNoContainers, PvPNoContainers, PvPImmunityEnd, NoBedPermission, NoWildernessBuckets, NoLavaNearOtherPlayer, TooFarAway, BlockNotClaimed, BlockClaimed, SiegeNoShovel, RestoreNaturePlayerInChunk, NoCreateClaimPermission, ResizeClaimTooSmall, ResizeNeedMoreBlocks, NoCreativeUnClaim, ClaimResizeSuccess, ResizeFailOverlap, ResizeStart, ResizeFailOverlapSubdivision, SubdivisionStart, CreateSubdivisionOverlap, SubdivisionSuccess, CreateClaimFailOverlap, CreateClaimFailOverlapOtherPlayer, ClaimsDisabledWorld, ClaimStart, NewClaimTooSmall, CreateClaimInsufficientBlocks, AbandonClaimAdvertisement, CreateClaimFailOverlapShort, CreateClaimSuccess, SiegeWinDoorsOpen, RescueAbortedMoved, SiegeDoorsLockedEjection, NoModifyDuringSiege, OnlyOwnersModifyClaims, NoBuildUnderSiege, NoBuildPvP, NoBuildPermission, NonSiegeMaterial, NoOwnerBuildUnderSiege, NoAccessPermission, NoContainersSiege, NoContainersPermission, OwnerNameForAdminClaims, ClaimTooSmallForEntities, TooManyEntitiesInClaim, YouHaveNoClaims, ConfirmFluidRemoval, AutoBanNotify, AdjustGroupBlocksSuccess, InvalidPermissionID, UntrustOwnerOnly, HowToClaimRegex, NoBuildOutsideClaims, PlayerOfflineTime, BuildingOutsideClaims, TrappedWontWorkHere, CommandBannedInPvP, UnclaimCleanupWarning, BuySellNotConfigured, NoTeleportPvPCombat, NoTNTDamageAboveSeaLevel, NoTNTDamageClaims, IgnoreClaimsAdvertisement
}

File diff suppressed because it is too large Load Diff

View File

@ -1,112 +1,120 @@
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
//this main thread task takes the output from the RestoreNatureProcessingTask\
//and updates the world accordingly
class RestoreNatureExecutionTask implements Runnable
{
//results from processing thread
//will be applied to the world
private BlockSnapshot[][][] snapshots;
//boundaries for changes
private int miny;
private Location lesserCorner;
private Location greaterCorner;
//player who should be notified about the result (will see a visualization when the restoration is complete)
private Player player;
public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player)
{
this.snapshots = snapshots;
this.miny = miny;
this.lesserCorner = lesserCorner;
this.greaterCorner = greaterCorner;
this.player = player;
}
@Override
public void run()
{
//apply changes to the world, but ONLY to unclaimed blocks
//note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk)
//those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration
Claim cachedClaim = null;
for(int x = 1; x < this.snapshots.length - 1; x++)
{
for(int z = 1; z < this.snapshots[0][0].length; z++)
{
for(int y = this.miny; y < this.snapshots[0].length; y++)
{
BlockSnapshot blockUpdate = this.snapshots[x][y][z];
Block currentBlock = blockUpdate.location.getBlock();
if(blockUpdate.typeId != currentBlock.getTypeId() || blockUpdate.data != currentBlock.getData())
{
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim);
if(claim != null)
{
cachedClaim = claim;
break;
}
currentBlock.setTypeId(blockUpdate.typeId);
currentBlock.setData(blockUpdate.data);
}
}
}
}
//clean up any entities in the chunk, ensure no players are suffocated
Chunk chunk = this.lesserCorner.getChunk();
Entity [] entities = chunk.getEntities();
for(int i = 0; i < entities.length; i++)
{
Entity entity = entities[i];
if(!(entity instanceof Player || entity instanceof Animals))
{
entity.remove();
}
else
{
Block feetBlock = entity.getLocation().getBlock();
feetBlock.setType(Material.AIR);
feetBlock.getRelative(BlockFace.UP).setType(Material.AIR);
}
}
//show visualization to player
if(player != null)
{
Claim claim = new Claim(lesserCorner, greaterCorner, "", new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}
}
}
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Hanging;
import org.bukkit.entity.Player;
//this main thread task takes the output from the RestoreNatureProcessingTask\
//and updates the world accordingly
class RestoreNatureExecutionTask implements Runnable
{
//results from processing thread
//will be applied to the world
private BlockSnapshot[][][] snapshots;
//boundaries for changes
private int miny;
private Location lesserCorner;
private Location greaterCorner;
//player who should be notified about the result (will see a visualization when the restoration is complete)
private Player player;
public RestoreNatureExecutionTask(BlockSnapshot[][][] snapshots, int miny, Location lesserCorner, Location greaterCorner, Player player)
{
this.snapshots = snapshots;
this.miny = miny;
this.lesserCorner = lesserCorner;
this.greaterCorner = greaterCorner;
this.player = player;
}
@Override
public void run()
{
//apply changes to the world, but ONLY to unclaimed blocks
//note that the edge of the results is not applied (the 1-block-wide band around the outside of the chunk)
//those data were sent to the processing thread for referernce purposes, but aren't part of the area selected for restoration
Claim cachedClaim = null;
for(int x = 1; x < this.snapshots.length - 1; x++)
{
for(int z = 1; z < this.snapshots[0][0].length; z++)
{
for(int y = this.miny; y < this.snapshots[0].length; y++)
{
BlockSnapshot blockUpdate = this.snapshots[x][y][z];
Block currentBlock = blockUpdate.location.getBlock();
if(blockUpdate.typeId != currentBlock.getTypeId() || blockUpdate.data != currentBlock.getData())
{
Claim claim = GriefPrevention.instance.dataStore.getClaimAt(blockUpdate.location, false, cachedClaim);
if(claim != null)
{
cachedClaim = claim;
break;
}
currentBlock.setTypeId(blockUpdate.typeId);
currentBlock.setData(blockUpdate.data);
}
}
}
}
//clean up any entities in the chunk, ensure no players are suffocated
Chunk chunk = this.lesserCorner.getChunk();
Entity [] entities = chunk.getEntities();
for(int i = 0; i < entities.length; i++)
{
Entity entity = entities[i];
if(!(entity instanceof Player || entity instanceof Animals))
{
//hanging entities (paintings, item frames) are protected when they're in land claims
if(!(entity instanceof Hanging) || GriefPrevention.instance.dataStore.getClaimAt(entity.getLocation(), false, null) == null)
{
//everything else is removed
entity.remove();
}
}
//for players, always ensure there's air where the player is standing
else
{
Block feetBlock = entity.getLocation().getBlock();
feetBlock.setType(Material.AIR);
feetBlock.getRelative(BlockFace.UP).setType(Material.AIR);
}
}
//show visualization to player who started the restoration
if(player != null)
{
Claim claim = new Claim(lesserCorner, greaterCorner, "", new String[] {}, new String[] {}, new String[] {}, new String[] {}, null);
Visualization visualization = Visualization.FromClaim(claim, player.getLocation().getBlockY(), VisualizationType.RestoreNature, player.getLocation());
Visualization.Apply(player, visualization);
}
}
}

View File

@ -1,225 +1,227 @@
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
//represents a visualization sent to a player
//FEATURE: to show players visually where claim boundaries are, we send them fake block change packets
//the result is that those players see new blocks, but the world hasn't been changed. other players can't see the new blocks, either.
public class Visualization
{
public ArrayList<VisualizationElement> elements = new ArrayList<VisualizationElement>();
//sends a visualization to a player
public static void Apply(Player player, Visualization visualization)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
//if he has any current visualization, clear it first
if(playerData.currentVisualization != null)
{
Visualization.Revert(player);
}
//if he's online, create a task to send him the visualization in about half a second
if(player.isOnline())
{
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 10L);
}
}
//reverts a visualization by sending another block change list, this time with the real world block values
public static void Revert(Player player)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
Visualization visualization = playerData.currentVisualization;
if(playerData.currentVisualization != null)
{
if(player.isOnline())
{
for(int i = 0; i < visualization.elements.size(); i++)
{
VisualizationElement element = visualization.elements.get(i);
Block block = element.location.getBlock();
player.sendBlockChange(element.location, block.getType(), block.getData());
}
}
playerData.currentVisualization = null;
}
}
//convenience method to build a visualization from a claim
//visualizationType determines the style (gold blocks, silver, red, diamond, etc)
public static Visualization FromClaim(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
//visualize only top level claims
if(claim.parent != null)
{
return FromClaim(claim.parent, height, visualizationType, locality);
}
Visualization visualization = new Visualization();
//add subdivisions first
for(int i = 0; i < claim.children.size(); i++)
{
visualization.addClaimElements(claim.children.get(i), height, VisualizationType.Subdivision, locality);
}
//add top level last so that it takes precedence (it shows on top when the child claim boundaries overlap with its boundaries)
visualization.addClaimElements(claim, height, visualizationType, locality);
return visualization;
}
//adds a claim's visualization to the current visualization
//handy for combining several visualizations together, as when visualization a top level claim with several subdivisions inside
//locality is a performance consideration. only create visualization blocks for around 100 blocks of the locality
private void addClaimElements(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
Location smallXsmallZ = claim.getLesserBoundaryCorner();
Location bigXbigZ = claim.getGreaterBoundaryCorner();
World world = smallXsmallZ.getWorld();
int smallx = smallXsmallZ.getBlockX();
int smallz = smallXsmallZ.getBlockZ();
int bigx = bigXbigZ.getBlockX();
int bigz = bigXbigZ.getBlockZ();
Material cornerMaterial;
Material accentMaterial;
if(visualizationType == VisualizationType.Claim)
{
cornerMaterial = Material.GLOWSTONE;
accentMaterial = Material.GOLD_BLOCK;
}
else if(visualizationType == VisualizationType.Subdivision)
{
cornerMaterial = Material.IRON_BLOCK;
accentMaterial = Material.WOOL;
}
else if(visualizationType == VisualizationType.RestoreNature)
{
cornerMaterial = Material.DIAMOND_BLOCK;
accentMaterial = Material.DIAMOND_BLOCK;
}
else
{
cornerMaterial = Material.GLOWING_REDSTONE_ORE;
accentMaterial = Material.NETHERRACK;
}
//bottom left corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, smallz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz + 1), accentMaterial, (byte)0));
//bottom right corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, smallz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz + 1), accentMaterial, (byte)0));
//top right corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, bigz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz - 1), accentMaterial, (byte)0));
//top left corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, bigz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz - 1), accentMaterial, (byte)0));
//locality
int minx = locality.getBlockX() - 100;
int minz = locality.getBlockZ() - 100;
int maxx = locality.getBlockX() + 100;
int maxz = locality.getBlockZ() + 100;
//top line
for(int x = smallx + 10; x < bigx - 10; x += 10)
{
if(x > minx && x < maxx)
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, bigz), accentMaterial, (byte)0));
}
//bottom line
for(int x = smallx + 10; x < bigx - 10; x += 10)
{
if(x > minx && x < maxx)
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, smallz), accentMaterial, (byte)0));
}
//left line
for(int z = smallz + 10; z < bigz - 10; z += 10)
{
if(z > minz && z < maxz)
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, z), accentMaterial, (byte)0));
}
//right line
for(int z = smallz + 10; z < bigz - 10; z += 10)
{
if(z > minz && z < maxz)
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, z), accentMaterial, (byte)0));
}
}
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling
private static Location getVisibleLocation(World world, int x, int y, int z)
{
Block block = world.getBlockAt(x, y, z);
BlockFace direction = (isTransparent(block)) ? BlockFace.DOWN : BlockFace.UP;
while( block.getY() >= 1 &&
block.getY() < world.getMaxHeight() - 1 &&
(!isTransparent(block.getRelative(BlockFace.UP)) || isTransparent(block)))
{
block = block.getRelative(direction);
}
return block.getLocation();
}
//helper method for above. allows visualization blocks to sit underneath partly transparent blocks like grass and fence
private static boolean isTransparent(Block block)
{
return ( block.getType() == Material.AIR ||
block.getType() == Material.LONG_GRASS ||
block.getType() == Material.FENCE ||
block.getType() == Material.LEAVES ||
block.getType() == Material.RED_ROSE ||
block.getType() == Material.CHEST ||
block.getType() == Material.YELLOW_FLOWER );
}
}
/*
GriefPrevention Server Plugin for Minecraft
Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
*/
package me.ryanhamshire.GriefPrevention;
import java.util.ArrayList;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
//represents a visualization sent to a player
//FEATURE: to show players visually where claim boundaries are, we send them fake block change packets
//the result is that those players see new blocks, but the world hasn't been changed. other players can't see the new blocks, either.
public class Visualization
{
public ArrayList<VisualizationElement> elements = new ArrayList<VisualizationElement>();
//sends a visualization to a player
public static void Apply(Player player, Visualization visualization)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
//if he has any current visualization, clear it first
if(playerData.currentVisualization != null)
{
Visualization.Revert(player);
}
//if he's online, create a task to send him the visualization in about half a second
if(player.isOnline())
{
GriefPrevention.instance.getServer().getScheduler().scheduleSyncDelayedTask(GriefPrevention.instance, new VisualizationApplicationTask(player, playerData, visualization), 10L);
}
}
//reverts a visualization by sending another block change list, this time with the real world block values
public static void Revert(Player player)
{
PlayerData playerData = GriefPrevention.instance.dataStore.getPlayerData(player.getName());
Visualization visualization = playerData.currentVisualization;
if(playerData.currentVisualization != null)
{
if(player.isOnline())
{
for(int i = 0; i < visualization.elements.size(); i++)
{
VisualizationElement element = visualization.elements.get(i);
Block block = element.location.getBlock();
player.sendBlockChange(element.location, block.getType(), block.getData());
}
}
playerData.currentVisualization = null;
}
}
//convenience method to build a visualization from a claim
//visualizationType determines the style (gold blocks, silver, red, diamond, etc)
public static Visualization FromClaim(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
//visualize only top level claims
if(claim.parent != null)
{
return FromClaim(claim.parent, height, visualizationType, locality);
}
Visualization visualization = new Visualization();
//add subdivisions first
for(int i = 0; i < claim.children.size(); i++)
{
visualization.addClaimElements(claim.children.get(i), height, VisualizationType.Subdivision, locality);
}
//add top level last so that it takes precedence (it shows on top when the child claim boundaries overlap with its boundaries)
visualization.addClaimElements(claim, height, visualizationType, locality);
return visualization;
}
//adds a claim's visualization to the current visualization
//handy for combining several visualizations together, as when visualization a top level claim with several subdivisions inside
//locality is a performance consideration. only create visualization blocks for around 100 blocks of the locality
private void addClaimElements(Claim claim, int height, VisualizationType visualizationType, Location locality)
{
Location smallXsmallZ = claim.getLesserBoundaryCorner();
Location bigXbigZ = claim.getGreaterBoundaryCorner();
World world = smallXsmallZ.getWorld();
int smallx = smallXsmallZ.getBlockX();
int smallz = smallXsmallZ.getBlockZ();
int bigx = bigXbigZ.getBlockX();
int bigz = bigXbigZ.getBlockZ();
Material cornerMaterial;
Material accentMaterial;
if(visualizationType == VisualizationType.Claim)
{
cornerMaterial = Material.GLOWSTONE;
accentMaterial = Material.GOLD_BLOCK;
}
else if(visualizationType == VisualizationType.Subdivision)
{
cornerMaterial = Material.IRON_BLOCK;
accentMaterial = Material.WOOL;
}
else if(visualizationType == VisualizationType.RestoreNature)
{
cornerMaterial = Material.DIAMOND_BLOCK;
accentMaterial = Material.DIAMOND_BLOCK;
}
else
{
cornerMaterial = Material.GLOWING_REDSTONE_ORE;
accentMaterial = Material.NETHERRACK;
}
//bottom left corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, smallz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, smallz + 1), accentMaterial, (byte)0));
//bottom right corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, smallz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, smallz + 1), accentMaterial, (byte)0));
//top right corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx - 1, height, bigz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, bigz - 1), accentMaterial, (byte)0));
//top left corner
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz), cornerMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx + 1, height, bigz), accentMaterial, (byte)0));
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, bigz - 1), accentMaterial, (byte)0));
//locality
int minx = locality.getBlockX() - 100;
int minz = locality.getBlockZ() - 100;
int maxx = locality.getBlockX() + 100;
int maxz = locality.getBlockZ() + 100;
//top line
for(int x = smallx + 10; x < bigx - 10; x += 10)
{
if(x > minx && x < maxx)
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, bigz), accentMaterial, (byte)0));
}
//bottom line
for(int x = smallx + 10; x < bigx - 10; x += 10)
{
if(x > minx && x < maxx)
this.elements.add(new VisualizationElement(getVisibleLocation(world, x, height, smallz), accentMaterial, (byte)0));
}
//left line
for(int z = smallz + 10; z < bigz - 10; z += 10)
{
if(z > minz && z < maxz)
this.elements.add(new VisualizationElement(getVisibleLocation(world, smallx, height, z), accentMaterial, (byte)0));
}
//right line
for(int z = smallz + 10; z < bigz - 10; z += 10)
{
if(z > minz && z < maxz)
this.elements.add(new VisualizationElement(getVisibleLocation(world, bigx, height, z), accentMaterial, (byte)0));
}
}
//finds a block the player can probably see. this is how visualizations "cling" to the ground or ceiling
private static Location getVisibleLocation(World world, int x, int y, int z)
{
Block block = world.getBlockAt(x, y, z);
BlockFace direction = (isTransparent(block)) ? BlockFace.DOWN : BlockFace.UP;
while( block.getY() >= 1 &&
block.getY() < world.getMaxHeight() - 1 &&
(!isTransparent(block.getRelative(BlockFace.UP)) || isTransparent(block)))
{
block = block.getRelative(direction);
}
return block.getLocation();
}
//helper method for above. allows visualization blocks to sit underneath partly transparent blocks like grass and fence
private static boolean isTransparent(Block block)
{
return ( block.getType() == Material.AIR ||
block.getType() == Material.LONG_GRASS ||
block.getType() == Material.FENCE ||
block.getType() == Material.LEAVES ||
block.getType() == Material.RED_ROSE ||
block.getType() == Material.CHEST ||
block.getType() == Material.TORCH ||
block.getType() == Material.VINE ||
block.getType() == Material.YELLOW_FLOWER );
}
}