diff --git a/lib/Herochat.jar b/lib/Herochat.jar new file mode 100644 index 00000000..c1f57739 Binary files /dev/null and b/lib/Herochat.jar differ diff --git a/src/com/massivecraft/factions/Conf.java b/src/com/massivecraft/factions/Conf.java index e142fd84..0e81a3c4 100644 --- a/src/com/massivecraft/factions/Conf.java +++ b/src/com/massivecraft/factions/Conf.java @@ -64,21 +64,18 @@ public class Conf // Disallow joining/leaving/kicking while power is negative public static boolean canLeaveWithNegativePower = true; - // Configuration for faction-only chat - public static boolean factionOnlyChat = true; // Configuration on the Faction tag in chat messages. - public static boolean chatTagEnabled = true; - public static transient boolean chatTagHandledByAnotherPlugin = false; - public static boolean chatTagRelationColored = true; - public static String chatTagReplaceString = "[FACTION]"; - public static String chatTagInsertAfterString = ""; - public static String chatTagInsertBeforeString = ""; - public static int chatTagInsertIndex = 1; - public static boolean chatTagPadBefore = false; - public static boolean chatTagPadAfter = true; - public static String chatTagFormat = "%s"+ChatColor.WHITE; - public static String factionChatFormat = "%s:"+ChatColor.WHITE+" %s"; - public static String allianceChatFormat = ChatColor.LIGHT_PURPLE+"%s:"+ChatColor.WHITE+" %s"; + public static boolean chatSetFormat = false; + public static String chatSetFormatTo = "<{faction_relcolor}§l{faction_roleprefix}§r{faction_relcolor}{faction_tag_pr}"+ChatColor.WHITE.toString()+"%s> %s"; + public static boolean chatParseTags = true; + public static boolean chatParseTagsColored = true; + public static Map chatSingleFormats = new HashMap(); + public static transient boolean chatTagHandledByAnotherPlugin = false; // Why do we need this? (Olof asks) + public static String chatTagFormat = "%s"+ChatColor.WHITE; // This one is almost deprecated now right? or is it? + + // Herochat + public static String herochatFactionChannelName = "Faction"; + public static String herochatAllyChannelName = "Allies"; public static double autoLeaveAfterDaysOfInactivity = 10.0; public static double autoLeaveRoutineRunsEveryXMinutes = 5.0; @@ -260,6 +257,10 @@ public class Conf factionPermDefaults.put(perm, perm.defaultDefaultValue); } + chatSingleFormats.put("pl", " %s"); + chatSingleFormats.put("pr", "%s "); + chatSingleFormats.put("pb", " %s "); + territoryEnemyDenyCommands.add("home"); territoryEnemyDenyCommands.add("sethome"); territoryEnemyDenyCommands.add("spawn"); diff --git a/src/com/massivecraft/factions/FPlayer.java b/src/com/massivecraft/factions/FPlayer.java index 89190094..4f713da6 100644 --- a/src/com/massivecraft/factions/FPlayer.java +++ b/src/com/massivecraft/factions/FPlayer.java @@ -16,7 +16,6 @@ import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.integration.LWCFeatures; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.integration.Worldguard; -import com.massivecraft.factions.struct.ChatMode; import com.massivecraft.factions.struct.FFlag; import com.massivecraft.factions.struct.FPerm; import com.massivecraft.factions.struct.Rel; @@ -99,23 +98,6 @@ public class FPlayer extends PlayerEntity implements EconomyParticipator // FIELD: loginPvpDisabled private transient boolean loginPvpDisabled; - // FIELD: chatMode - private ChatMode chatMode; - public void setChatMode(ChatMode chatMode) { this.chatMode = chatMode; } - public ChatMode getChatMode() - { - if(this.factionId.equals("0") || ! Conf.factionOnlyChat) - { - this.chatMode = ChatMode.PUBLIC; - } - return chatMode; - } - - // FIELD: chatSpy - private transient boolean spyingChat = false; - public void setSpyingChat(boolean chatSpying) { this.spyingChat = chatSpying; } - public boolean isSpyingChat() { return spyingChat; } - // FIELD: account public String getAccountId() { return this.getId(); } @@ -153,7 +135,6 @@ public class FPlayer extends PlayerEntity implements EconomyParticipator } this.factionId = "0"; // The default neutral faction - this.chatMode = ChatMode.PUBLIC; this.role = Rel.MEMBER; this.title = ""; this.autoClaimFor = null; diff --git a/src/com/massivecraft/factions/P.java b/src/com/massivecraft/factions/P.java index 0c28d636..c390c8e0 100644 --- a/src/com/massivecraft/factions/P.java +++ b/src/com/massivecraft/factions/P.java @@ -24,6 +24,7 @@ import com.massivecraft.factions.cmd.*; import com.massivecraft.factions.integration.capi.CapiFeatures; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.integration.EssentialsFeatures; +import com.massivecraft.factions.integration.HerochatFeatures; import com.massivecraft.factions.integration.LWCFeatures; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.integration.Worldguard; @@ -34,7 +35,6 @@ import com.massivecraft.factions.listeners.FactionsExploitListener; import com.massivecraft.factions.listeners.FactionsHealthBarListener; import com.massivecraft.factions.listeners.FactionsPlayerListener; import com.massivecraft.factions.listeners.FactionsServerListener; -import com.massivecraft.factions.struct.ChatMode; import com.massivecraft.factions.struct.FFlag; import com.massivecraft.factions.struct.FPerm; import com.massivecraft.factions.struct.Rel; @@ -105,6 +105,7 @@ public class P extends MPlugin SpoutFeatures.setup(); Econ.setup(); CapiFeatures.setup(); + HerochatFeatures.setup(); LWCFeatures.setup(); if(Conf.worldGuardChecking) @@ -241,13 +242,12 @@ public class P extends MPlugin // Does player have Faction Chat enabled? If so, chat plugins should preferably not do channels, // local chat, or anything else which targets individual recipients, so Faction Chat can be done + /** + * @deprecated As of release 1.8, there is no built in faction chat. + */ public boolean isPlayerFactionChatting(Player player) { - if (player == null) return false; - FPlayer me = FPlayers.i.get(player); - - if (me == null)return false; - return me.getChatMode().isAtLeast(ChatMode.ALLIANCE); + return false; } // Is this chat message actually a Factions command, and thus should be left alone by other plugins? @@ -276,7 +276,7 @@ public class P extends MPlugin return tag; // if listener isn't set, or config option is disabled, give back uncolored tag - if (listener == null || !Conf.chatTagRelationColored) { + if (listener == null || !Conf.chatParseTagsColored) { tag = me.getChatTag().trim(); } else { FPlayer you = FPlayers.i.get(listener); diff --git a/src/com/massivecraft/factions/cmd/CmdChat.java b/src/com/massivecraft/factions/cmd/CmdChat.java deleted file mode 100644 index 25b4c203..00000000 --- a/src/com/massivecraft/factions/cmd/CmdChat.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.massivecraft.factions.cmd; - -import com.massivecraft.factions.Conf; -import com.massivecraft.factions.struct.ChatMode; -import com.massivecraft.factions.struct.Permission; - -public class CmdChat extends FCommand -{ - - public CmdChat() - { - super(); - this.aliases.add("c"); - this.aliases.add("chat"); - - //this.requiredArgs.add(""); - this.optionalArgs.put("mode", "next"); - - this.permission = Permission.CHAT.node; - this.disableOnLock = false; - - senderMustBePlayer = true; - senderMustBeMember = true; - senderMustBeOfficer = false; - senderMustBeLeader = false; - } - - @Override - public void perform() - { - if ( ! Conf.factionOnlyChat ) - { - msg("The built in chat chat channels are disabled on this server."); - return; - } - - String modeString = this.argAsString(0); - ChatMode modeTarget = fme.getChatMode().getNext(); - - if (modeString != null) - { - modeString.toLowerCase(); - if(modeString.startsWith("p")) - { - modeTarget = ChatMode.PUBLIC; - } - else if (modeString.startsWith("a")) - { - modeTarget = ChatMode.ALLIANCE; - } - else if(modeString.startsWith("f")) - { - modeTarget = ChatMode.FACTION; - } - else - { - msg("Unrecognised chat mode. Please enter either 'a','f' or 'p'"); - return; - } - } - - fme.setChatMode(modeTarget); - - if(fme.getChatMode() == ChatMode.PUBLIC) - { - msg("Public chat mode."); - } - else if (fme.getChatMode() == ChatMode.ALLIANCE ) - { - msg("Alliance only chat mode."); - } - else - { - msg("Faction only chat mode."); - } - } -} diff --git a/src/com/massivecraft/factions/cmd/CmdChatSpy.java b/src/com/massivecraft/factions/cmd/CmdChatSpy.java deleted file mode 100644 index e41dddbb..00000000 --- a/src/com/massivecraft/factions/cmd/CmdChatSpy.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.massivecraft.factions.cmd; - -import com.massivecraft.factions.P; -import com.massivecraft.factions.struct.Permission; - -public class CmdChatSpy extends FCommand -{ - public CmdChatSpy() - { - super(); - this.aliases.add("chatspy"); - - this.optionalArgs.put("on/off", "flip"); - - this.permission = Permission.CHATSPY.node; - this.disableOnLock = false; - - senderMustBePlayer = true; - senderMustBeMember = false; - senderMustBeOfficer = false; - senderMustBeLeader = false; - } - - @Override - public void perform() - { - fme.setSpyingChat(this.argAsBool(0, ! fme.isSpyingChat())); - - if ( fme.isSpyingChat()) - { - fme.msg("You have enabled chat spying mode."); - P.p.log(fme.getName() + " has ENABLED chat spying mode."); - } - else - { - fme.msg("You have disabled chat spying mode."); - P.p.log(fme.getName() + " DISABLED chat spying mode."); - } - } -} diff --git a/src/com/massivecraft/factions/cmd/CmdHelp.java b/src/com/massivecraft/factions/cmd/CmdHelp.java index 1601d032..5368b6cd 100644 --- a/src/com/massivecraft/factions/cmd/CmdHelp.java +++ b/src/com/massivecraft/factions/cmd/CmdHelp.java @@ -67,7 +67,6 @@ public class CmdHelp extends FCommand pageLines.add( p.cmdBase.cmdPower.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdJoin.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdLeave.getUseageTemplate(true) ); - pageLines.add( p.cmdBase.cmdChat.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdHome.getUseageTemplate(true) ); pageLines.add( p.txt.parse("Learn how to create a faction on the next page.") ); helpPages.add(pageLines); @@ -166,7 +165,6 @@ public class CmdHelp extends FCommand pageLines = new ArrayList(); pageLines.add(p.txt.parse("More commands for server admins:")); - pageLines.add( p.cmdBase.cmdChatSpy.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdPowerBoost.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdLock.getUseageTemplate(true) ); pageLines.add( p.cmdBase.cmdReload.getUseageTemplate(true) ); diff --git a/src/com/massivecraft/factions/cmd/FCmdRoot.java b/src/com/massivecraft/factions/cmd/FCmdRoot.java index 9bf36b9c..28eac6b5 100644 --- a/src/com/massivecraft/factions/cmd/FCmdRoot.java +++ b/src/com/massivecraft/factions/cmd/FCmdRoot.java @@ -9,8 +9,6 @@ public class FCmdRoot extends FCommand public CmdLeader cmdLeader = new CmdLeader(); public CmdAutoClaim cmdAutoClaim = new CmdAutoClaim(); public CmdAdmin cmdBypass = new CmdAdmin(); - public CmdChat cmdChat = new CmdChat(); - public CmdChatSpy cmdChatSpy = new CmdChatSpy(); public CmdClaim cmdClaim = new CmdClaim(); public CmdConfig cmdConfig = new CmdConfig(); public CmdCreate cmdCreate = new CmdCreate(); @@ -73,8 +71,6 @@ public class FCmdRoot extends FCommand this.addSubCommand(this.cmdLeader); this.addSubCommand(this.cmdAutoClaim); this.addSubCommand(this.cmdBypass); - this.addSubCommand(this.cmdChat); - this.addSubCommand(this.cmdChatSpy); this.addSubCommand(this.cmdClaim); this.addSubCommand(this.cmdConfig); this.addSubCommand(this.cmdCreate); diff --git a/src/com/massivecraft/factions/integration/EssentialsFeatures.java b/src/com/massivecraft/factions/integration/EssentialsFeatures.java index 2b4b8af1..25058292 100644 --- a/src/com/massivecraft/factions/integration/EssentialsFeatures.java +++ b/src/com/massivecraft/factions/integration/EssentialsFeatures.java @@ -11,6 +11,7 @@ import org.bukkit.plugin.Plugin; import com.massivecraft.factions.Conf; import com.massivecraft.factions.P; +import com.massivecraft.factions.listeners.FactionsChatListener; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.Teleport; @@ -105,13 +106,6 @@ public class EssentialsFeatures { Bukkit.getServer().getPluginManager().registerEvents(new LocalChatListener(), P.p); P.p.log("Found and will integrate chat with newer "+essChat.getDescription().getFullName()); - - // curly braces used to be accepted by the format string EssentialsChat but no longer are, so... deal with chatTagReplaceString which might need updating - if (Conf.chatTagReplaceString.contains("{")) - { - Conf.chatTagReplaceString = Conf.chatTagReplaceString.replace("{", "[").replace("}", "]"); - P.p.log("NOTE: as of Essentials 2.8+, we've had to switch the default chat replacement tag from \"{FACTION}\" to \"[FACTION]\". This has automatically been updated for you."); - } } catch (NoSuchMethodError ex) { @@ -127,7 +121,9 @@ public class EssentialsFeatures { Player speaker = event.getPlayer(); String format = event.getFormat(); - format = format.replace(Conf.chatTagReplaceString, P.p.getPlayerFactionTag(speaker)).replace("[FACTION_TITLE]", P.p.getPlayerTitle(speaker)); + + format = FactionsChatListener.parseTags(format, speaker); + event.setFormat(format); // NOTE: above doesn't do relation coloring. if/when we can get a local recipient list from EssentialsLocalChatEvent, we'll probably // want to pass it on to FactionsPlayerListener.onPlayerChat(PlayerChatEvent event) rather than duplicating code diff --git a/src/com/massivecraft/factions/integration/EssentialsOldVersionFeatures.java b/src/com/massivecraft/factions/integration/EssentialsOldVersionFeatures.java index e867820a..232e92fc 100644 --- a/src/com/massivecraft/factions/integration/EssentialsOldVersionFeatures.java +++ b/src/com/massivecraft/factions/integration/EssentialsOldVersionFeatures.java @@ -3,8 +3,8 @@ package com.massivecraft.factions.integration; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerChatEvent; -import com.massivecraft.factions.Conf; import com.massivecraft.factions.P; +import com.massivecraft.factions.listeners.FactionsChatListener; import com.earth2me.essentials.chat.EssentialsChat; import com.earth2me.essentials.chat.IEssentialsChatListener; @@ -31,17 +31,11 @@ public class EssentialsOldVersionFeatures } public String modifyMessage(PlayerChatEvent event, Player target, String message) { - return message.replace(Conf.chatTagReplaceString, P.p.getPlayerFactionTagRelation(event.getPlayer(), target)).replace("[FACTION_TITLE]", P.p.getPlayerTitle(event.getPlayer())); + return FactionsChatListener.parseTags(message, event.getPlayer()); + //return message.replace(Conf.chatTagReplaceString, P.p.getPlayerFactionTagRelation(event.getPlayer(), target)).replace("[FACTION_TITLE]", P.p.getPlayerTitle(event.getPlayer())); } }); P.p.log("Found and will integrate chat with "+essChat.getDescription().getFullName()); - - // As of Essentials 2.8+, curly braces are not accepted and are instead replaced with square braces, so... deal with it - if (essChat.getDescription().getVersion().startsWith("2.8.") && Conf.chatTagReplaceString.contains("{")) - { - Conf.chatTagReplaceString = Conf.chatTagReplaceString.replace("{", "[").replace("}", "]"); - P.p.log("NOTE: as of Essentials 2.8+, we've had to switch the default chat replacement tag from \"{FACTION}\" to \"[FACTION]\". This has automatically been updated for you."); - } } catch (NoSuchMethodError ex) { diff --git a/src/com/massivecraft/factions/integration/HerochatFeatures.java b/src/com/massivecraft/factions/integration/HerochatFeatures.java new file mode 100644 index 00000000..6ed01a5a --- /dev/null +++ b/src/com/massivecraft/factions/integration/HerochatFeatures.java @@ -0,0 +1,71 @@ +package com.massivecraft.factions.integration; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +import com.dthielke.herochat.Channel; +import com.dthielke.herochat.ChannelChatEvent; +import com.massivecraft.factions.Conf; +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; +import com.massivecraft.factions.Faction; +import com.massivecraft.factions.P; +import com.massivecraft.factions.struct.Rel; + +public class HerochatFeatures implements Listener +{ + P p; + public HerochatFeatures(P p) + { + this.p = p; + } + + public static void setup() + { + Plugin plug = Bukkit.getServer().getPluginManager().getPlugin("Herochat"); + if (plug != null && plug.getClass().getName().equals("com.dthielke.herochat.Herochat")) + { + P.p.log("Integration with Herochat successful"); + Bukkit.getPluginManager().registerEvents(new HerochatFeatures(P.p), P.p); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onChannelChatEvent(ChannelChatEvent event) + { + Channel ch = event.getChannel(); + boolean isFactionChat = ch.getName().equals(Conf.herochatFactionChannelName); + boolean isAllyChat = ch.getName().equals(Conf.herochatAllyChannelName); + if ( ! isFactionChat && ! isAllyChat) return; + + // Do common setup + Player sender = event.getSender().getPlayer(); + FPlayer fpsender = FPlayers.i.get(sender); + event.getBukkitEvent().getRecipients().clear(); + if ( ! fpsender.hasFaction()) + { + sender.sendMessage(ChatColor.YELLOW.toString()+"You must join a faction to use the "+ch.getColor().toString()+ch.getName()+ChatColor.YELLOW.toString()+"-channel."); + event.getBukkitEvent().setCancelled(true); + return; + } + + Faction faction = fpsender.getFaction(); + event.getBukkitEvent().getRecipients().addAll(faction.getOnlinePlayers()); + + if (isAllyChat) + { + for (FPlayer fplayer : FPlayers.i.getOnline()) + { + if(faction.getRelationTo(fplayer) == Rel.ALLY) + { + event.getBukkitEvent().getRecipients().add(fplayer.getPlayer()); + } + } + } + } +} diff --git a/src/com/massivecraft/factions/integration/capi/PluginCapiListener.java b/src/com/massivecraft/factions/integration/capi/PluginCapiListener.java index c1a04273..b82be061 100644 --- a/src/com/massivecraft/factions/integration/capi/PluginCapiListener.java +++ b/src/com/massivecraft/factions/integration/capi/PluginCapiListener.java @@ -77,13 +77,6 @@ public class PluginCapiListener implements Listener if (event.getChannel().getId().equals("faction") && myFaction.isNormal()) { event.getThem().addAll(myFaction.getOnlinePlayers()); - - // Send to any players who are spying chat... could probably be implemented better than this - for (FPlayer fplayer : FPlayers.i.getOnline()) - { - if(fplayer.isSpyingChat() && fplayer.getFaction() != myFaction) - fplayer.sendMessage("[FCspy] "+myFaction.getTag()+": "+event.getMessage()); - } } else if (event.getChannel().getId().equals("allies")) { @@ -92,9 +85,6 @@ public class PluginCapiListener implements Listener FPlayer someFPlayer = FPlayers.i.get(somePlayer); if (someFPlayer.getRelationTo(fme).isAtLeast(Rel.ALLY)) event.getThem().add(somePlayer); - // Send to any players who are spying chat - else if(someFPlayer.isSpyingChat()) - someFPlayer.sendMessage("[ACspy]: " + event.getMessage()); } } } diff --git a/src/com/massivecraft/factions/listeners/FactionsChatListener.java b/src/com/massivecraft/factions/listeners/FactionsChatListener.java index 4e9454ca..e7344910 100644 --- a/src/com/massivecraft/factions/listeners/FactionsChatListener.java +++ b/src/com/massivecraft/factions/listeners/FactionsChatListener.java @@ -1,25 +1,31 @@ package com.massivecraft.factions.listeners; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.logging.Level; -import java.util.UnknownFormatConversionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.plugin.AuthorNagException; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredListener; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.FPlayers; -import com.massivecraft.factions.Faction; import com.massivecraft.factions.P; -import com.massivecraft.factions.struct.ChatMode; import com.massivecraft.factions.struct.Rel; - public class FactionsChatListener implements Listener { public P p; @@ -28,158 +34,232 @@ public class FactionsChatListener implements Listener this.p = p; } - // this is for handling slashless command usage and faction/alliance chat, set at lowest priority so Factions gets to them first - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerEarlyChat(PlayerChatEvent event) + public static Field fieldRegisteredListenerDotPriority; + public static final Pattern parsePattern; + static { - if (event.isCancelled()) return; - - Player talkingPlayer = event.getPlayer(); - String msg = event.getMessage(); - FPlayer me = FPlayers.i.get(talkingPlayer); - ChatMode chat = me.getChatMode(); - - // slashless factions commands need to be handled here if the user isn't in public chat mode - if (chat != ChatMode.PUBLIC && p.handleCommand(talkingPlayer, msg)) + try { - if (Conf.logPlayerCommands) - Bukkit.getLogger().log(Level.INFO, "[PLAYER_COMMAND] "+talkingPlayer.getName()+": "+msg); - event.setCancelled(true); - return; + fieldRegisteredListenerDotPriority = RegisteredListener.class.getDeclaredField("priority"); + fieldRegisteredListenerDotPriority.setAccessible(true); } - - // Is it a faction chat message? - if (chat == ChatMode.FACTION) + catch (Exception e) { - Faction myFaction = me.getFaction(); - - String message = String.format(Conf.factionChatFormat, me.describeTo(myFaction), msg); - myFaction.sendMessage(message); - - Bukkit.getLogger().log(Level.INFO, ChatColor.stripColor("FactionChat "+myFaction.getTag()+": "+message)); - - //Send to any players who are spying chat - for (FPlayer fplayer : FPlayers.i.getOnline()) - { - if(fplayer.isSpyingChat() && fplayer.getFaction() != myFaction) - fplayer.sendMessage("[FCspy] "+myFaction.getTag()+": "+message); - } - - event.setCancelled(true); - return; + P.p.log(Level.SEVERE, "A reflection trick is broken! This will lead to glitchy relation-colored-chat."); } - else if (chat == ChatMode.ALLIANCE) + + parsePattern = Pattern.compile("[{\\[]factions?_([a-zA-Z_]+)[}\\]]"); + } + + /** + * We offer an optional and very simple chat formating functionality. + */ + @EventHandler(priority = EventPriority.LOW, ignoreCancelled=true) + public void lowPlayerChatEvent(PlayerChatEvent event) + { + if (Conf.chatSetFormat) { - Faction myFaction = me.getFaction(); - - String message = String.format(Conf.allianceChatFormat, ChatColor.stripColor(me.getNameAndTag()), msg); - - //Send message to our own faction - myFaction.sendMessage(message); - - //Send to all our allies - for (FPlayer fplayer : FPlayers.i.getOnline()) - { - if(myFaction.getRelationTo(fplayer) == Rel.ALLY) - fplayer.sendMessage(message); - - //Send to any players who are spying chat - else if(fplayer.isSpyingChat()) - fplayer.sendMessage("[ACspy]: " + message); - } - - Bukkit.getLogger().log(Level.INFO, ChatColor.stripColor("AllianceChat: "+message)); - - event.setCancelled(true); - return; + event.setFormat(Conf.chatSetFormatTo); } } // this is for handling insertion of the player's faction tag, set at highest priority to give other plugins a chance to modify chat first - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerChat(PlayerChatEvent event) + + /** + * At the Highest event priority we apply chat formating. + * Relation colored faction tags may or may not be disabled (Conf.chatParseTagsColored) + * If color is disabled it works flawlessly. + * If however color is enabled we face a limitation in Bukkit. + * Bukkit does not support the same message looking different for each recipient. + * The method we use to get around this is a bit hacky: + * 1. We cancel the chat event on EventPriority.HIGHEST + * 2. We trigger EventPriority.MONITOR manually without relation color. + * 3. We log in console the way it's usually done (as in nms.NetServerHandler line~793). + * 4. We send out the messages to each player with relation color. + * The side effect is that other plugins at EventPriority.HIGHEST may experience the event as cancelled. + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled=true) + public synchronized void onPlayerChat(PlayerChatEvent event) { - if (event.isCancelled()) return; - - // Are we to insert the Faction tag into the format? - // If we are not to insert it - we are done. - if ( ! Conf.chatTagEnabled || Conf.chatTagHandledByAnotherPlugin) return; - - Player talkingPlayer = event.getPlayer(); - String msg = event.getMessage(); - String eventFormat = event.getFormat(); - FPlayer me = FPlayers.i.get(talkingPlayer); - int InsertIndex = 0; + // Should we even parse? + if ( ! Conf.chatParseTags) return; + if (Conf.chatTagHandledByAnotherPlugin) return; - if (!Conf.chatTagReplaceString.isEmpty() && eventFormat.contains(Conf.chatTagReplaceString)) + Player from = event.getPlayer(); + FPlayer fpfrom = FPlayers.i.get(from); + String format = event.getFormat(); + String message = event.getMessage(); + + String formatWithoutColor = parseTags(format, from, fpfrom); + + if ( ! Conf.chatParseTagsColored) { - // we're using the "replace" method of inserting the faction tags - // if they stuck "[FACTION_TITLE]" in there, go ahead and do it too - if (eventFormat.contains("[FACTION_TITLE]")) - { - eventFormat = eventFormat.replace("[FACTION_TITLE]", me.getTitle()); - } - InsertIndex = eventFormat.indexOf(Conf.chatTagReplaceString); - eventFormat = eventFormat.replace(Conf.chatTagReplaceString, ""); - Conf.chatTagPadAfter = false; - Conf.chatTagPadBefore = false; - } - else if (!Conf.chatTagInsertAfterString.isEmpty() && eventFormat.contains(Conf.chatTagInsertAfterString)) - { - // we're using the "insert after string" method - InsertIndex = eventFormat.indexOf(Conf.chatTagInsertAfterString) + Conf.chatTagInsertAfterString.length(); - } - else if (!Conf.chatTagInsertBeforeString.isEmpty() && eventFormat.contains(Conf.chatTagInsertBeforeString)) - { - // we're using the "insert before string" method - InsertIndex = eventFormat.indexOf(Conf.chatTagInsertBeforeString); - } - else - { - // we'll fall back to using the index place method - InsertIndex = Conf.chatTagInsertIndex; - if (InsertIndex > eventFormat.length()) - return; + // The case without color is really this simple (: + event.setFormat(formatWithoutColor); + return; } - String formatStart = eventFormat.substring(0, InsertIndex) + ((Conf.chatTagPadBefore && !me.getChatTag().isEmpty()) ? " " : ""); - String formatEnd = ((Conf.chatTagPadAfter && !me.getChatTag().isEmpty()) ? " " : "") + eventFormat.substring(InsertIndex); + // So you want color eh? You monster :O - String nonColoredMsgFormat = formatStart + me.getChatTag().trim() + formatEnd; + // 1. We cancel the chat event on EventPriority.HIGHEST + event.setCancelled(true); - // Relation Colored? - if (Conf.chatTagRelationColored) + // 2. We trigger EventPriority.MONITOR manually without relation color. + PlayerChatEvent monitorOnlyEvent = new PlayerChatEvent(from, message); + monitorOnlyEvent.setFormat(formatWithoutColor); + callEventAtMonitorOnly(monitorOnlyEvent); + + // 3. We log in console the way it's usually done (as in nms.NetServerHandler line~793). + Bukkit.getConsoleSender().sendMessage(String.format(monitorOnlyEvent.getFormat(), monitorOnlyEvent.getPlayer().getDisplayName(), monitorOnlyEvent.getMessage())); + + // 4. We send out the messages to each player with relation color. + for (Player to : event.getRecipients()) { - // We must choke the standard message and send out individual messages to all players - // Why? Because the relations will differ. - event.setCancelled(true); - - for (Player listeningPlayer : event.getRecipients()) - { - FPlayer you = FPlayers.i.get(listeningPlayer); - String yourFormat = formatStart + me.getChatTag(you).trim() + formatEnd; - try + FPlayer fpto = FPlayers.i.get(to); + String formatWithColor = parseTags(format, from, fpfrom, to, fpto); + to.sendMessage(String.format(formatWithColor, from.getDisplayName(), message)); + } + } + + /** + * This is some nasty woodo - I know :/ + * I should make a pull request to Bukkit and CraftBukkit to support this feature natively + */ + public static synchronized void callEventAtMonitorOnly(Event event) + { + synchronized(Bukkit.getPluginManager()) + { + HandlerList handlers = event.getHandlers(); + RegisteredListener[] listeners = handlers.getRegisteredListeners(); + + for (RegisteredListener registration : listeners) + { + try { - listeningPlayer.sendMessage(String.format(yourFormat, talkingPlayer.getDisplayName(), msg)); + EventPriority priority = (EventPriority) fieldRegisteredListenerDotPriority.get(registration); + if (priority != EventPriority.MONITOR) continue; } - catch (UnknownFormatConversionException ex) + catch (Exception e) { - Conf.chatTagInsertIndex = 0; - P.p.log(Level.SEVERE, "Critical error in chat message formatting!"); - P.p.log(Level.SEVERE, "NOTE: This has been automatically fixed right now by setting chatTagInsertIndex to 0."); - P.p.log(Level.SEVERE, "For a more proper fix, please read this regarding chat configuration: http://massivecraft.com/plugins/factions/config#Chat_configuration"); - return; + e.printStackTrace(); + continue; } - } - - // Write to the log... We will write the non colored message. - String nonColoredMsg = ChatColor.stripColor(String.format(nonColoredMsgFormat, talkingPlayer.getDisplayName(), msg)); - Bukkit.getLogger().log(Level.INFO, nonColoredMsg); - } - else - { - // No relation color. - event.setFormat(nonColoredMsgFormat); + + // This rest is almost copy pasted from SimplePluginManager in Bukkit: + + if (!registration.getPlugin().isEnabled()) { + continue; + } + + try { + registration.callEvent(event); + } catch (AuthorNagException ex) { + Plugin plugin = registration.getPlugin(); + + if (plugin.isNaggable()) { + plugin.setNaggable(false); + + String author = ""; + + if (plugin.getDescription().getAuthors().size() > 0) { + author = plugin.getDescription().getAuthors().get(0); + } + Bukkit.getServer().getLogger().log(Level.SEVERE, String.format( + "Nag author: '%s' of '%s' about the following: %s", + author, + plugin.getDescription().getName(), + ex.getMessage() + )); + } + } catch (Throwable ex) { + Bukkit.getServer().getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getName(), ex); + } + } } } + + public static String parseTags(String str, Player from) + { + FPlayer fpfrom = FPlayers.i.get(from); + return parseTags(str, from, fpfrom, null, null); + } + public static String parseTags(String str, Player from, FPlayer fpfrom) + { + return parseTags(str, from, fpfrom, null, null); + } + public static String parseTags(String str, Player from, Player to) + { + FPlayer fpfrom = FPlayers.i.get(from); + FPlayer fpto = FPlayers.i.get(to); + return parseTags(str, from, fpfrom, to, fpto); + } + public static String parseTags(String str, Player from, FPlayer fpfrom, Player to, FPlayer fpto) + { + StringBuffer ret = new StringBuffer(); + + Matcher matcher = parsePattern.matcher(str); + while (matcher.find()) + { + String[] parts = matcher.group(1).toLowerCase().split("_"); + List args = new ArrayList(Arrays.asList(parts)); + String tag = args.remove(0); + matcher.appendReplacement(ret, produceTag(tag, args, from, fpfrom, to, fpto)); + } + matcher.appendTail(ret); + + return ret.toString(); + } + public static String produceTag(String tag, List args, Player from, FPlayer fpfrom, Player to, FPlayer fpto) + { + String ret = ""; + if (tag.equals("relcolor")) + { + if (fpto == null) + { + ret = Rel.NEUTRAL.getColor().toString(); + } + else + { + ret = fpfrom.getRelationTo(fpto).getColor().toString(); + } + } + else if (tag.startsWith("roleprefix")) + { + ret = fpfrom.getRole().getPrefix(); + } + else if (tag.equals("title")) + { + ret = fpfrom.getTitle(); + } + else if (tag.equals("tag")) + { + if (fpfrom.hasFaction()) + { + ret = fpfrom.getFaction().getTag(); + } + } + else if (tag.startsWith("tagforce")) + { + ret = fpfrom.getFaction().getTag(); + } + + if (ret == null) ret = ""; + + return applyFormatsByName(ret, args); + } + public static String applyFormatsByName(String str, List formatNames) + { + if (str.length() == 0) return str; + for (String formatName : formatNames) + { + String format = Conf.chatSingleFormats.get(formatName); + try + { + str = String.format(format, str); + } + catch (Exception e) { } + } + return str; + } + } diff --git a/src/com/massivecraft/factions/util/SpiralTask.java b/src/com/massivecraft/factions/util/SpiralTask.java index 98d5927a..ee01dce5 100644 --- a/src/com/massivecraft/factions/util/SpiralTask.java +++ b/src/com/massivecraft/factions/util/SpiralTask.java @@ -39,7 +39,7 @@ public abstract class SpiralTask implements Runnable private transient int length = -1; private transient int current = 0; - @SuppressWarnings("LeakingThisInConstructor") + // @SuppressWarnings("LeakingThisInConstructor") This actually triggers a warning in Eclipse xD Could we find another way to suppress the error please? :) public SpiralTask(FLocation fLocation, int radius) { // limit is determined based on spiral leg length for given radius; see insideRadius()