Can you make a second recipient list for chat events?

Discussion in 'Plugin Development' started by TechBug2012, Jan 30, 2016.

Thread Status:
Not open for further replies.
  1. Offline

    TechBug2012

    I'm working on a SwearToggle plugin for practice. The way I want it to work is if someone types "/swear on", every swear word in a message will be replaced with stars (****). If they type "/swear off", chat is left unfiltered. I have a particular problem with the chat listener. I understand how to check if someone has swearing filtered, and I understand how to remove the message, but I don't understand how to send a new, filtered message. Right now, I am removing the players with filtering enabled from the getRecipients() list, but I don't know where to go from there. Is there a way to make a new recipient list under the same chat event where players with filtering enabled are moved to? Also, is there a way to do with while still keeping the chat format set in another plugin like Essentials?

    Main class: http://pastebin.com/WKf99TAA
    words.yml: http://pastebin.com/F5qG7jGN
    players.yml: http://pastebin.com/3Nu3S3Nj

    Thanks for your help! :)
     
  2. Offline

    timtower Administrator Administrator Moderator

    @TechBug2012 Throw a new chat event and then send the message to the recipients by hand. Make sure that this won't create a recursive loop though.
     
  3. Offline

    TechBug2012

    Won't it mess with the chat formatting if I send the message by hand?
     
  4. Offline

    timtower Administrator Administrator Moderator

    That is why you throw the event first and then you send the message using the format.
     
  5. Offline

    TechBug2012

    But how do I get the chat format from Essentials? Is it even possible, or do I have to make my own chat format within the plugin?

    Also, wouldn't this work?
    Code:
    ArrayList<Player> filteredPlayers = new ArrayList<Player>();
      for (Player p : event.getRecipients()) {
        if (usernameList.contains(p)) {
          event.getRecipients().remove(p);
          filteredPlayers.add(p);
          for (Player newPlayer : filteredPlayers) {
            newPlayer = p;
            newPlayer.sendMessage(newMessage);
          }
        }
      }
    
    I can figure out the chat format later, but why can't something like this work?
     
    Last edited: Jan 30, 2016
  6. Offline

    Konato_K

    @TechBug2012 If you listen on higher technically all plugins should have already listened to the event and applied to the changes, so calling getFormat on the event might get you the final format (unless another plugin is also on higher)

    Your code has a loop inside a loop, I don't think it does what you want to do.

    You can send the message manually, but it's probably a bad idea.
     
  7. Offline

    TechBug2012

    I made a mistake in my code. I have it in my actual code as .setMessage. Does that work better?

    What do you mean it won't do what I want it to do? What does it do? My first for loop loops each Player p through each name in getRecipients(). If they have swearing filtered, it then removes them from getRecipients() and adds them to a new ArrayList filteredPlayers containing all the players with swearing filtered. Then for each newPlayer that is in filteredPlayers, it sets newPlayer equal to p and sends the filtered message newMessage. If they have swearing disabled, it sends them the original message. Am I missing something?
     
  8. Offline

    Coopah

    @TechBug2012
    Your best way to go about this, is by looping through the recipients and checking if they are in the config. If so, send them a message (player.sendMessage();) with the new format (swear words erased), else you can just return.

    event.getFormat(); and event.getMessage(); may be useful.
     
  9. Offline

    shades161

    Send the unfiltered message like you normally would. Then, for all of the filtered messages, make a new string that is set as the message, filter out all of the swears in said string. Then apply the format, and then send the message to all of the players inside of the Set of filtered players.
    Example:
    Code:
    //Method start above.
        for (Player p : event.getRecipients() {
            if (usernameList.contains(p.getName()) {
                filtered.add(p);
                event.getRecipients.Remove(p);
            }
        }
        String filteredMessage = event.getMessage();
        //filter the filteredMessage string of all swears.
        filteredMessage = event.getFormat() + filteredMessage;
        for (Player p : filtered) {
            p.sendMessage(filteredMessage); //Manually send filtered message to players but have it appear as real chat message.
        }
        event.setMessage(event.getMessage()); //send message to all recipients that did not filter swears.
    //End method here, or do other stuff.
    This should work, though there may be syntax errors cuz I did this really quickly on Notepad++ since i'm on my laptop without an IDE open.
     
    Last edited: Jan 30, 2016
  10. Offline

    TechBug2012

    @shades161 Awesome! This looks great.

    I tried loading the plugin, but I'm getting a NullPointer exception on this line:
    Code:
    List<String> usernameList = this.playerAccessor.getConfig().getStringList("usernames");
    
    My main class has been updated on the original post. I put my error on Pastebin: http://pastebin.com/G21umXE5
     
  11. Offline

    shades161

    @TechBug2012 You need to create the file first, then create the list, and then you can get it without issue (Though I would suggest getting the list in the chat method or where you are using it rather than loading it at start).
     
  12. Offline

    TechBug2012

    @shades161 usernameList is used in more than just the onChat() method, though, so that's why I loaded it at start.

    I think that if I had taken the time to read the error properly I would have figured out that I didn't create the file, but I jumped on the forums instead. Thanks for your help though :p
     
  13. Offline

    TechBug2012

    @shades161
    I have another problem. The plugin loads and works fine, but the messages aren't actually censored. And, the PlayerJoinEvent doesn't do what it's supposed to do. I've read through my code but I can't seem to find the problem!

    I updated my main class on Pastebin. http://pastebin.com/WKf99TAA
     
  14. Offline

    shades161

    You're sending the message inside of the word censor loop... Issue with that is that after it does the first word, it sends the message. You are also doing that inside of another loop. Try having it done outside of the first loop, and then after the message has been censored, do another loop of only sending it to players. The example I showed above filtered the messages before sending it to players.
     
  15. Offline

    TechBug2012

    @shades161
    Why doesn't this work?
    Code:
    @EventHandler
    public void onChat(AsyncPlayerChatEvent event) {
      String newMessage = event.getMessage();
       for (String word : wordList) {
        newMessage = newMessage.replaceAll(" " + word + " ", "****");
      }
    
      ArrayList<Player> filteredPlayers = new ArrayList<>();
      for (Player p : event.getRecipients()) {
        if (usernameList.contains(p.getName())) {
          event.getRecipients().remove(p);
          filteredPlayers.add(p);
        }
        else {
          event.setMessage(event.getMessage());
        }
      }
      for (Player filteredPlayer : filteredPlayers) {
        filteredPlayer.sendMessage(event.getFormat() + newMessage);
      }
    }
    
     
    Last edited: Feb 3, 2016
  16. Offline

    WolfMage1

    @TechBug2012
    you messed up your code tag :p
    it's actually code=java not code(java)

    and instead of pasting it then surrounding it in code tags, just press the page kinda button thing right next to the "floppy disk" button and then hit Code then paste it in (optional) select type java

    try this, it should work (haven't tested it but it should):
    Code:
           
    String[] words = event.getMessage().split(" ");
    StringBuilder sb = new StringBuilder();
    for(String word : getConfig().getStringList("banned-words")) {
        for (int i = 0; i < words.length; i++) {
            sb.append(words[i].replace(word, "***")).append(" ");
        }
    }
    event.setMessage(sb.toString());
     
  17. Offline

    TechBug2012

    @WolfMage1
    I had to edit it a couple times in frustration before I got the code tag right. :p
    I'll check out your way of doing it if all else fails, but I feel like the way I'm doing it is just missing a few things that I need to figure out, so I want to focus on that first.
     
  18. Offline

    WolfMage1

    I'd also remove the spaces in empty strings from your .replace, as if someone swears at the end of a sentence, it wont get replaced because there's no space after it.

    But the one advantage of yours, if someone goes fjroijrioewjriewjriejrrwoht9ow4j02o43o (just pretend there;s swears in there) then it would remove them.

    And don't store Player objects unless you handle them properly.
     
  19. Offline

    shades161

    @TechBug2012 You can't have event.setMessage() inside of the loop, it has to be after it or else it will either send the message multiple times, or it will stop the loop as thats the end of the event

    You should end up having 3 for loops.
    first loop: filters the event recipients, removes players who want chat censored, keeps ones that dont. Dont do anything else but that.

    Second loop: Set a String censored, outside of the loop, above it (set it equal to the event.getMessage()) .Filters the message of all swears. loop through your swear list, if that swear is in the message, censor it, set the censored string as that new string that was just filtered. It will loop through and if done right, the message will then be censored.

    Third loop: Send the new censored message to all of the players who want censored messages. Do nothing else.

    After: use event.setMessage() to send the message to all of the players who do not want censored messages, no loops needed as the Bukkit api will have this sent to all players on the recipient Set.

    Sorry I don't have a code example.
     
    Last edited: Feb 4, 2016
  20. Offline

    TechBug2012

    I think I'm gonna do that and then make a pardoned-words list so that words like assessment aren't censored. For now, I just want to get this working.

    @shades161
    What's wrong with this one?
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            ArrayList<Player> filteredPlayers = new ArrayList<>();
            for (Player p : event.getRecipients()) {
                if (usernameList.contains(p.getName())) {
                    event.getRecipients().remove(p);
                    filteredPlayers.add(p);
                }
            }
            String newMessage = event.getMessage();
            for (String word : wordList) {
                newMessage = newMessage.replaceAll(" " + word + " ", "****");
            }
            for (Player filteredPlayer : filteredPlayers) {
                filteredPlayer.sendMessage(event.getFormat() + newMessage);
            }
            event.setMessage(event.getMessage());
        }
    
     
  21. Offline

    Xerox262

    You didn't check that the message contained the swear words in the first place, your causing a CME by not using an iterator, you're using replace all instead of replace, and instead of having to just send the message you can just call a new AsyncPlayerChatEvent with the new message and recipients.
     
  22. Offline

    TechBug2012

    I thought I was.
    Code:
            for (String word : wordList) {
                newMessage = newMessage.replace(" " + word + " ", "****");
            }
    
    For each word in the wordList, set newMessage to replace the word with ****s.
    What do you mean?
    Why can't I just do it in the one that I'm using now?
     
  23. Offline

    Xerox262

    You're not, if you were then you wouldn't loop through every word when there is a possibility that it's not doing anything
    I mean that
    Code:
           for (Player p : event.getRecipients()) {
               if (usernameList.contains(p.getName())) {
                    event.getRecipients().remove(p);
                    filteredPlayers.add(p);
               }
           }
    Should throw an error on error on line 3 since you're removing them while you're looping through, an iterator would fix this.
    You can, however you're creating unneccessary work for yourself if installing it on a server with say Factions, calling the event will allow factions to insert information before the name, like faction name, rank name, etc. Which might not already be there, It also means that if you have another plugin that controls chat then the information will go through that too, like a chat color plugin or whatever. Even if you have none of these plugins would you not rather base it around the fact that you might install it in the future and since you currently know how to fix it would it not be better to add the one line now?
     
  24. Offline

    TechBug2012

    What should I do instead?
    I don't have much experience with iterators, but is this what you mean? If not, can you provide me with a good place to learn more about iterators?
    Code:
               if (usernameList.contains(p.getName())) {
                    ListIterator fPlayers = filteredPlayers.listIterator();
                    while (fPlayers.hasNext()) {
                        event.getRecipients().remove(p);
                        filteredPlayers.add(p);
                    }
                }
    
    I apologize if my examples and questions sound noob-ish. I have only been practicing Bukkit plugin development for a little over a month, and I'm still trying to get used to everything. :)
     
  25. Offline

    TechBug2012

    @Xerox262
    I've been looking over my code, and I realized that the MOTD doesn't send.
    Code:
        @EventHandler
        public void onPlayerJoin(PlayerJoinEvent e) { // The code looks fine, but the MOTD doesn't send.
            if (usernameList.contains(e.getPlayer().getName())) {
                e.getPlayer().sendMessage(ChatColor.GRAY + "Swearing is filtered.");
            }
            else {
                e.getPlayer().sendMessage(ChatColor.GRAY + "Swearing is unfiltered.");
            }
        }
    
    Perhaps the problem could be with an incorrect usage of contains. This is just me speculating, but if there's something wrong with contains in the onPlayerJoin method, maybe I'm using it wrong and the code in onChat doesn't know what it means. Can you take a look and see if I'm right or wrong? Thanks :D
     
  26. Offline

    teej107

  27. Offline

    TechBug2012

    How do I do that? Do I make a new method with a new AsyncPlayerChatEvent?
     
    Last edited: Feb 9, 2016
  28. Offline

    Xerox262

    He means use a bukkit runnable to delay the message by a tick.
     
  29. Offline

    TechBug2012

    @Xerox262
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            for (String word : wordList) {
                newMessage = newMessage.replace(" " + word + " ", "****");
            }
            for (Player p : event.getRecipients()) {
                if (usernameList.contains(p.getName())) {
                    filteredPlayers.add(p);
                    event.setCancelled(true);
                    plugin.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
                        @Override
                        public void run() {
                            AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, p, newMessage, filteredPlayers); // <--
                            plugin.getServer().getPluginManager().callEvent(e);
    
                            if (e.isCancelled()) {
                                return;
                            }
                            plugin.getServer().broadcastMessage(String.format
                                    (e.getFormat(), e.getPlayer().getDisplayName(), e.getMessage()));
                        }
                    });
                }
                else {
                    event.setMessage(event.getMessage());
                }
            }
        }
    
    Are these parameters correct? How do I define them all as final?
     
  30. Offline

    Xerox262

    You do not need to create a task to send call a new Event.

    I was more talking something like
    Code:
    @EventHandler
    public void chat(ASyncPlayerChatEvent e) {
        Set<Player> secondRecipients = new HashSet<Player>();
        String new message = e.getMessage();
        for (String word : wordList)
            new message = newMessage.replace(" " + word +  " ", "****");
        Iterator it = e.getRecipients().iterator();
        while (it.hasNext()) {
            Player player = it.next();
            if (usernameList.contains(player.getName()) {
                secondRecipients.add(player);
                e.getRecipients().remove(player);
            }
        }
        ASyncPlayerChatEvent event = new ASyncPlayerChatEvent(true, e.getPlayer(), newMessage, secondRecipients);
        Bukkit.callEvent(event);
    }
    Don't copy and paste this, it will most likely not work, I done this IDE-less so there's bound to be mistakes.
     
Thread Status:
Not open for further replies.

Share This Page