Added /SoftMute

This commit is contained in:
ryanhamshire 2014-11-01 19:18:27 -07:00
parent 5865dc354b
commit 9c1094b95b
5 changed files with 189 additions and 10 deletions

View File

@ -129,6 +129,10 @@ commands:
claimexplosions: claimexplosions:
description: Toggles whether explosives may be used in a specific land claim. description: Toggles whether explosives may be used in a specific land claim.
usage: /ClaimExplosions usage: /ClaimExplosions
softmute:
description: Toggles whether a player's messages will only reach other soft-muted players.
usage: /SoftMute <player>
permission: griefprevention.softmute
permissions: permissions:
griefprevention.createclaims: griefprevention.createclaims:
description: Grants permission to create claims. description: Grants permission to create claims.
@ -146,6 +150,7 @@ permissions:
griefprevention.lava: true griefprevention.lava: true
griefprevention.eavesdrop: true griefprevention.eavesdrop: true
griefprevention.deathblow: true griefprevention.deathblow: true
griefprevention.softmute: true
griefprevention.restorenature: griefprevention.restorenature:
description: Grants permission to use /RestoreNature. description: Grants permission to use /RestoreNature.
default: op default: op
@ -168,7 +173,7 @@ permissions:
description: Grants permission to place lava near the surface and outside of claims. description: Grants permission to place lava near the surface and outside of claims.
default: op default: op
griefprevention.eavesdrop: griefprevention.eavesdrop:
description: Allows a player to see whispered chat messages (/tell). description: Allows a player to see whispered chat messages (/tell) and softmuted messages.
default: op default: op
griefprevention.restorenatureaggressive: griefprevention.restorenatureaggressive:
description: Grants access to /RestoreNatureAggressive and /RestoreNatureFill. description: Grants access to /RestoreNatureAggressive and /RestoreNatureFill.
@ -176,6 +181,9 @@ permissions:
griefprevention.deathblow: griefprevention.deathblow:
description: Grants access to /DeathBlow. description: Grants access to /DeathBlow.
default: op default: op
griefprevention.softmute:
description: Grants access to /SoftMute.
default: op
griefprevention.claims: griefprevention.claims:
description: Grants access to claim-related slash commands. description: Grants access to claim-related slash commands.
default: true default: true

View File

@ -55,6 +55,7 @@ public abstract class DataStore
protected final static String dataLayerFolderPath = "plugins" + File.separator + "GriefPreventionData"; protected final static String dataLayerFolderPath = "plugins" + File.separator + "GriefPreventionData";
final static String configFilePath = dataLayerFolderPath + File.separator + "config.yml"; final static String configFilePath = dataLayerFolderPath + File.separator + "config.yml";
final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml"; final static String messagesFilePath = dataLayerFolderPath + File.separator + "messages.yml";
final static String softMuteFilePath = dataLayerFolderPath + File.separator + "softMute.txt";
//the latest version of the data schema implemented here //the latest version of the data schema implemented here
protected static final int latestSchemaVersion = 1; protected static final int latestSchemaVersion = 1;
@ -71,6 +72,9 @@ public abstract class DataStore
static final String CREATIVE_VIDEO_URL = "http://bit.ly/mcgpcrea"; static final String CREATIVE_VIDEO_URL = "http://bit.ly/mcgpcrea";
static final String SUBDIVISION_VIDEO_URL = "http://bit.ly/mcgpsub"; static final String SUBDIVISION_VIDEO_URL = "http://bit.ly/mcgpsub";
//list of UUIDs which are soft-muted
ConcurrentHashMap<UUID, Boolean> softMuteMap = new ConcurrentHashMap<UUID, Boolean>();
protected int getSchemaVersion() protected int getSchemaVersion()
{ {
if(this.currentSchemaVersion >= 0) if(this.currentSchemaVersion >= 0)
@ -118,11 +122,124 @@ public abstract class DataStore
GriefPrevention.AddLogEntry("Update finished."); GriefPrevention.AddLogEntry("Update finished.");
} }
//make a note of the data store schema version //load list of soft mutes
this.loadSoftMutes();
//make a note of the data store schema version
this.setSchemaVersion(latestSchemaVersion); this.setSchemaVersion(latestSchemaVersion);
} }
//removes cached player data from memory private void loadSoftMutes()
{
File softMuteFile = new File(softMuteFilePath);
if(softMuteFile.exists())
{
BufferedReader inStream = null;
try
{
//open the file
inStream = new BufferedReader(new FileReader(softMuteFile.getAbsolutePath()));
//while there are lines left
String nextID = inStream.readLine();
while(nextID != null)
{
//parse line into a UUID
UUID playerID;
try
{
playerID = UUID.fromString(nextID);
}
catch(Exception e)
{
playerID = null;
GriefPrevention.AddLogEntry("Failed to parse soft mute entry as a UUID: " + nextID);
}
//push it into the map
if(playerID != null)
{
this.softMuteMap.put(playerID, true);
}
//move to the next
nextID = inStream.readLine();
}
}
catch(Exception e)
{
GriefPrevention.AddLogEntry("Failed to read from the soft mute data file: " + e.toString());
e.printStackTrace();
}
try
{
if(inStream != null) inStream.close();
}
catch(IOException exception) {}
}
}
//updates soft mute map and data file
boolean toggleSoftMute(UUID playerID)
{
boolean newValue = !this.isSoftMuted(playerID);
this.softMuteMap.put(playerID, newValue);
this.saveSoftMutes();
return newValue;
}
boolean isSoftMuted(UUID playerID)
{
Boolean mapEntry = this.softMuteMap.get(playerID);
if(mapEntry == null || mapEntry == Boolean.FALSE)
{
return false;
}
return true;
}
private void saveSoftMutes()
{
BufferedWriter outStream = null;
try
{
//open the file and write the new value
File softMuteFile = new File(softMuteFilePath);
softMuteFile.createNewFile();
outStream = new BufferedWriter(new FileWriter(softMuteFile));
for(Map.Entry<UUID, Boolean> entry : softMuteMap.entrySet())
{
if(entry.getValue().booleanValue())
{
outStream.write(entry.getKey().toString());
outStream.newLine();
}
}
}
//if any problem, log it
catch(Exception e)
{
GriefPrevention.AddLogEntry("Unexpected exception saving soft mute data: " + e.getMessage());
e.printStackTrace();
}
//close the file
try
{
if(outStream != null) outStream.close();
}
catch(IOException exception) {}
}
//removes cached player data from memory
synchronized void clearCachedPlayerData(UUID playerID) synchronized void clearCachedPlayerData(UUID playerID)
{ {
this.playerNameToPlayerDataMap.remove(playerID); this.playerNameToPlayerDataMap.remove(playerID);
@ -557,13 +674,15 @@ public abstract class DataStore
//saves changes to player data to secondary storage. MUST be called after you're done making changes, otherwise a reload will lose them //saves changes to player data to secondary storage. MUST be called after you're done making changes, otherwise a reload will lose them
public void savePlayerDataSync(UUID playerID, PlayerData playerData) public void savePlayerDataSync(UUID playerID, PlayerData playerData)
{ {
//ensure player data is already read from file before trying to save
playerData.getAccruedClaimBlocks();
playerData.getClaims();
this.asyncSavePlayerData(playerID, playerData); this.asyncSavePlayerData(playerID, playerData);
} }
//saves changes to player data to secondary storage. MUST be called after you're done making changes, otherwise a reload will lose them //saves changes to player data to secondary storage. MUST be called after you're done making changes, otherwise a reload will lose them
public void savePlayerData(UUID playerID, PlayerData playerData) public void savePlayerData(UUID playerID, PlayerData playerData)
{ {
//thread won't have access to read from files (silent failure - all readlines() return null)
//ensure player data is already read from file before trying to save //ensure player data is already read from file before trying to save
playerData.getAccruedClaimBlocks(); playerData.getAccruedClaimBlocks();
playerData.getClaims(); playerData.getClaims();
@ -1048,6 +1167,8 @@ public abstract class DataStore
this.addDefault(defaults, Messages.ClaimExplosivesAdvertisement, "To allow explosives to destroy blocks in this land claim, use /ClaimExplosions.", null); this.addDefault(defaults, Messages.ClaimExplosivesAdvertisement, "To allow explosives to destroy blocks in this land claim, use /ClaimExplosions.", null);
this.addDefault(defaults, Messages.PlayerInPvPSafeZone, "That player is in a PvP safe zone.", null); this.addDefault(defaults, Messages.PlayerInPvPSafeZone, "That player is in a PvP safe zone.", null);
this.addDefault(defaults, Messages.NoPistonsOutsideClaims, "Warning: Pistons won't move blocks outside land claims.", null); this.addDefault(defaults, Messages.NoPistonsOutsideClaims, "Warning: Pistons won't move blocks outside land claims.", null);
this.addDefault(defaults, Messages.SoftMuted, "Soft-muted {0}.", "The changed player's name.");
this.addDefault(defaults, Messages.UnSoftMuted, "Un-soft-muted {0}.", "The changed player's name.");
//load the config file //load the config file
FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath)); FileConfiguration config = YamlConfiguration.loadConfiguration(new File(messagesFilePath));

View File

@ -1910,6 +1910,32 @@ public class GriefPrevention extends JavaPlugin
GriefPrevention.sendMessage(defender, TextMode.Warn, Messages.SiegeAlert, attacker.getName()); GriefPrevention.sendMessage(defender, TextMode.Warn, Messages.SiegeAlert, attacker.getName());
GriefPrevention.sendMessage(player, TextMode.Success, Messages.SiegeConfirmed, defender.getName()); GriefPrevention.sendMessage(player, TextMode.Success, Messages.SiegeConfirmed, defender.getName());
} }
else if(cmd.getName().equalsIgnoreCase("softmute"))
{
//requires one parameter
if(args.length != 1) return false;
//find the specified player
OfflinePlayer targetPlayer = this.resolvePlayerByName(args[0], true);
if(targetPlayer == null)
{
GriefPrevention.sendMessage(player, TextMode.Err, Messages.PlayerNotFound);
return true;
}
//toggle mute for player
boolean isMuted = this.dataStore.toggleSoftMute(targetPlayer.getUniqueId());
if(isMuted)
{
GriefPrevention.sendMessage(player, TextMode.Success, Messages.SoftMuted, targetPlayer.getName());
}
else
{
GriefPrevention.sendMessage(player, TextMode.Success, Messages.UnSoftMuted, targetPlayer.getName());
}
return true;
}
return false; return false;
} }

View File

@ -20,5 +20,5 @@ package me.ryanhamshire.GriefPrevention;
public enum Messages 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, SubdivisionVideo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, 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, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsVideo, SurvivalBasicsVideo, 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, NoPermissionForCommand, ClaimsListNoPermission, ExplosivesDisabled, ExplosivesEnabled, ClaimExplosivesAdvertisement, PlayerInPvPSafeZone, NoPistonsOutsideClaims 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, SubdivisionVideo, DeleteClaimMissing, DeletionSubdivisionWarning, DeleteSuccess, CantDeleteAdminClaim, DeleteAllSuccess, NoDeletePermission, AllAdminDeleted, AdjustBlocksSuccess, NotTrappedHere, 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, UnprotectedChestWarning, ThatPlayerPvPImmune, CantFightWhileImmune, NoDamageClaimedEntity, ShovelBasicClaimMode, RemainingBlocks, CreativeBasicsVideo, SurvivalBasicsVideo, 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, NoPermissionForCommand, ClaimsListNoPermission, ExplosivesDisabled, ExplosivesEnabled, ClaimExplosivesAdvertisement, PlayerInPvPSafeZone, NoPistonsOutsideClaims, SoftMuted, UnSoftMuted
} }

View File

@ -23,6 +23,7 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -92,7 +93,34 @@ class PlayerEventHandler implements Listener
String message = event.getMessage(); String message = event.getMessage();
event.setCancelled(this.handlePlayerChat(player, message, event)); boolean muted = this.handlePlayerChat(player, message, event);
Set<Player> recipients = event.getRecipients();
//muted messages go out to only the sender
if(muted)
{
recipients.clear();
recipients.add(player);
}
//soft muted messages go out to all soft muted players
else if(this.dataStore.isSoftMuted(player.getUniqueId()))
{
Set<Player> recipientsToKeep = new HashSet<Player>();
for(Player recipient : recipients)
{
if(this.dataStore.isSoftMuted(recipient.getUniqueId()))
{
recipientsToKeep.add(recipient);
}
else if(recipient.hasPermission("griefprevention.eavesdrop"))
{
recipient.sendMessage("(Muted)" + player.getName() + ": " + message);
}
}
recipients.clear();
recipients.addAll(recipientsToKeep);
}
} }
//last chat message shown, regardless of who sent it //last chat message shown, regardless of who sent it
@ -292,10 +320,6 @@ class PlayerEventHandler implements Listener
//make a log entry //make a log entry
GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message); GriefPrevention.AddLogEntry("Muted spam from " + player.getName() + ": " + message);
//send a fake message so the player doesn't realize he's muted
//less information for spammers = less effective spam filter dodging
player.sendMessage("<" + player.getName() + "> " + message);
//cancelling the event guarantees other players don't receive the message //cancelling the event guarantees other players don't receive the message
return true; return true;
} }