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

    @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 + " ", "****");
                Iterator it = event.getRecipients().iterator();
                while (it.hasNext()) {
                    Player p = it.next();
                    if (usernameList.contains(p.getName())) {
                        filteredPlayers.add(p);
                        event.getRecipients().remove(p);
                    }
                }
                AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
                Bukkit.getServer().getPluginManager().callEvent(e);
                plugin.getServer().broadcastMessage(String.format(e.getFormat(),
                        event.getPlayer().getDisplayName(), newMessage));
            }
        }
    
    it.next() gives me an error for incompatible types of Player and Object. Would it be better to make p part of a for loop and initialize it in there?

    I don't think just calling the event will broadcast the message to everyone, so I added another line... am I wrong?

    EDIT: forgive me if I'm way out of line, but does casting fix the problem?
    Code:
    Player p = (Player) it.next();
     
  2. Offline

    Xerox262

    @TechBug2012 Casting will fix your problem, but so would something like
    Iterator<Player> it = event.getRecipients().iterator();

    Calling the event will broadcast the message to everyone, it'll act as if the player typed it into chat. The people with the correct permissions will see the one with bad language and the people without it will see the asterisks.
     
  3. Offline

    TechBug2012

    @Xerox262
    I fixed it, but the message still doesn't send for some reason.
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            for (String word : wordList) {
                newMessage = newMessage.replace(" " + word + " ", "****");
                Iterator<Player> it = event.getRecipients().iterator();
                while (it.hasNext()) {
                    Player p = it.next();
                    if (usernameList.contains(p.getName())) {
                        event.getRecipients().remove(p);
                        filteredPlayers.add(p);
                    }
                }
                AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
                Bukkit.getServer().getPluginManager().callEvent(e);
            }
        }
    
    I also updated my main class on Pastebin: http://pastebin.com/WKf99TAA
     
  4. Offline

    teej107

    Then don't spoonfeed. Your code is recursive.
     
  5. Offline

    TechBug2012

    @teej107
    Ok, so how do I fix the code? Should I go back to doing it the way I was doing it before?
    Also, where can I read more about recursions? I also want to prevent myself from writing recursive code in the future. I can't find anything useful on Google... maybe I'm not searching correctly.
     
  6. Offline

    teej107

    @TechBug2012 Just get rid of @Xerox262's spoonfed code. As for recursion, just look it up on Google. I'm sure there are many articles to read from.

    It's recursive code because:

    You call the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Show Spoiler

    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.
    Your event handler method gets called and the code inside runs which
    calls the event in the event handler method.

    oops. StackOverflowError!

    You can use recursive code for this but you need a way to stop it. An easy way I was thinking was just creating a class that extends from HashSet and then just checking to see if the recipient set is your custom class. Make sure you use your custom recipient Set when creating your AsyncPlayerChatEvent object.
     
  7. Offline

    Xerox262

    Or do what I said before and check that the message actually contains swear words before running the rest, I did cover this before.
     
  8. Offline

    TechBug2012

    Wouldn't that still be recursive? What am I missing?
     
  9. Offline

    Xerox262

    It would run your event handler, if there's a swear word it would carry out your code, then call the event with the message that contains no swear words. That way when it checks the second time it realizes there's no swear words and has no reason to call the event again, breaking the recursion loop.
     
  10. Offline

    TechBug2012

    @Xerox262
    Like this?
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            if (event.getMessage().contains(wordList.toString())) {
                for (String word : wordList) {
                    newMessage = newMessage.replace(" " + word + " ", "****");
                }
                Iterator<Player> it = event.getRecipients().iterator();
                while (it.hasNext()) {
                    Player p = it.next();
                    if (usernameList.contains(p.getName())) {
                        event.getRecipients().remove(p);
                        filteredPlayers.add(p);
                    }
                }
            }
            AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
            Bukkit.getServer().getPluginManager().callEvent(e);
        }
    
    I have a feeling some of my code is in the wrong place, but I moved it around a bit and the message still doesn't send. Do I call the event inside or outside of the if statement? Is the if statement even correct?
     
  11. Offline

    Xerox262

    @TechBug2012 You call the event inside the if, and your if is incorrect, you need to loop through all words, if any are contained then proceed, best to make a private method to return true if a word is there, just to save time.
     
  12. Offline

    TechBug2012

    @Xerox262
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            for (String word : wordList) {
                if (wordList.contains(word)) {
                    newMessage = newMessage.replace(" " + word + " ", "****");
                    Iterator<Player> it = event.getRecipients().iterator();
                    while (it.hasNext()) {
                        Player p = it.next();
                        if (usernameList.contains(p.getName())) {
                            event.getRecipients().remove(p);
                            filteredPlayers.add(p);
                        }
                    }
                    AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
                    Bukkit.getServer().getPluginManager().callEvent(e);
                }
                else {
                    event.setMessage(event.getMessage());
                }
            }
        }
    
    Is this right? It feels wrong to do all of this in the word : wordList loop, which is why I think it's wrong... but based on what you're telling me, everything needs to go under the loop, because I have to check for the word before proceeding, then I have to define Player p as an iterator, then I filter out the players, and then I have to call the new event. So what's wrong with this one?
     
  13. Offline

    teej107

    @TechBug2012
    1. Setting the message to itself. Do you see anything redundant about that?
    And I just realized that calling the event manually won't send the message. You will have to send the message yourself and calling an additional event is overkill and is more trouble than you need.
     
  14. Offline

    Xerox262

    @TechBug2012 You need to check if it contains the words separately, you do not need to do everything in one method, create a method that tells you if a message contains a swear word then use that to determine if you need to replace, call the event, etc. Also you do not need an else statement if you don't want anything to change.
    @teej107 What do you mean calling the event wont send the message?

    Does calling an ASyncPlayerChatEvent not actually send the messages to all the players online in the recipient list?
     
  15. Offline

    teej107

    @Xerox262
    Here is how events work.
    1. Event is called.
    2. You make changes to the event.
    3. CraftBukkit does code depending on the final outcome of the event.
    You do 1. and 2. in the manual event call but you are doing nothing on 3.
     
  16. Offline

    TechBug2012

    @Xerox262 @teej107
    I tried printing some stuff from onChat to the console, and nothing happened. The method is not even called in the first place.
    [​IMG]
    How do I "use" the method? Maybe the code works but it's not being executed.

    EDIT: the picture doesn't load for me, but it says something along the lines of "onChat is never used".
     
  17. Offline

    teej107

    @TechBug2012 Picture doesn't load for me either. The "onChat is never used" message is because nothing is directly calling it which is fine in Bukkit's case. Make sure you register your event and it the method will get called.
     
  18. Offline

    TechBug2012

    @teej107
    Awesome. I registered the events and the plugin finally works. I'm still getting the StackOverflow error because of the recursive code, as promised by you.
    Code:
    if (wordList.contains(word))
    so I think my if is incorrect.

    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            for (String word : wordList) {
                if (wordList.contains(word)) {
                    newMessage = newMessage.replace(" " + word + " ", "****");
                    Iterator<Player> it = event.getRecipients().iterator();
                    while (it.hasNext()) {
                        Player p = it.next();
                        if (usernameList.contains(p.getName())) {
                            event.getRecipients().remove(p);
                            filteredPlayers.add(p);
                        }
                    }
                    AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
                    Bukkit.getServer().getPluginManager().callEvent(e);
                }
            }
        }
    
    Is @Xerox262 right when he says I could just add an if, or should I go your way and add the custom class?
     
  19. Offline

    teej107

    @TechBug2012 Neither.
    and my custom class idea would still require an if statement but both should work, but that doesn't matter.
     
  20. Offline

    TechBug2012

    @teej107
    So, what do I do if I do neither? Right now, I'm struggling to get the if right, and I'm struggling to send the message. With this code,
    Code:
        @EventHandler
        public void onChat(AsyncPlayerChatEvent event) {
            Set<Player> filteredPlayers = new HashSet<>();
            String newMessage = event.getMessage();
            for (String word : wordList) {
                if (wordList.contains(word)) {
                    newMessage = newMessage.replace(" " + word + " ", "****");
                    Iterator<Player> it = event.getRecipients().iterator();
                    while (it.hasNext()) {
                        Player p = it.next();
                        if (usernameList.contains(p.getName())) {
                            event.getRecipients().remove(p);
                            filteredPlayers.add(p);
                        }
                    }
                    for (Player p : filteredPlayers) {
                        if (filteredPlayers.contains(p)) {
                            p.sendMessage(event.getFormat() + newMessage);
                        }
                    }
                }
            }
        }
    
    Every single chat message that goes through is printed twice as TechBug2012<%1$s> %2$swhatever message i sent

    My if is this:
    Code:
            for (String word : wordList) {
                if (wordList.contains(word)) {
    That seems right... but it doesn't work. Also, I know that this
    Code:
                    for (Player p : filteredPlayers) {
                        if (filteredPlayers.contains(p)) {
                            p.sendMessage(event.getFormat() + newMessage);
                        }
                    }
                }
    is wrong, but I don't know what to do instead.
     
  21. Offline

    teej107

    @TechBug2012 First of all, you don't need to check if the String contains the word.
    1. Loop through the banned words list.
    2. Replace the words in the String. (The reason why you don't need to check if the String has the word is because the replace method will just return a String unchanged)
    3. To check if any words are filtered in a String, just compare the String with the supposed replaced words with the original message.
    To send the replaced String with the right format, use https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#format(java.lang.String, java.lang.Object...)
     
  22. Offline

    TechBug2012

    That's comparing 3 things. How do I do that?

    You linked the docs for replace. Does that mean my replace is wrong?
    Code:
    newMessage = newMessage.replace(" " + word + " ", "****");
    How do I make this right?
    Code:
            newMessage = String.format(newMessage, event.getFormat());
            AsyncPlayerChatEvent e = new AsyncPlayerChatEvent(true, event.getPlayer(), newMessage, filteredPlayers);
            for (Player p : filteredPlayers) {
                if (e.getRecipients().contains(p)) {
                    p.sendMessage(newMessage);
                }
            }
        }
    
     
  23. Offline

    teej107

    huh? I just wrote 3 steps on how to achieve this whole thing.
    No, that is right. I just like to link to the JavaDocs when referring to methods when I can to be precise. You are replacing words just fine.
    You don't need to create an event for starters.
     
  24. Offline

    TechBug2012

    compare the String with the supposed replaced words with the original message.

    Maybe I'm just not reading it correctly.

    Removed the event. What I meant is, is the formatting done correctly? Because right now, no matter what I type, the message is sent as a string with no formatting.
    Code:
            newMessage = String.format(newMessage, event.getFormat());
            for (Player p : filteredPlayers) {
                p.sendMessage(newMessage);
            }
        }
    
    EDIT: Code updated. http://pastebin.com/WKf99TAA
     
  25. Offline

    teej107

    Compare the String containing the censored words with the String without the censored words. If both Strings are equal then that means nothing was censored.
    You mixed up the parameters. You also need to put the player's display name in the parameters of the format method since the default format is usually something like <%s> %s

    It is also good to note that it may be better to format the String in a separate EventHandler method with the MONITOR priority. That way you have all the final changes if a plugin after yours decides to change the format.
     
  26. Offline

    TechBug2012

    @teej107
    I will look into format the string in a separate method, but for now I will do it in onChat because I just want to get it to work right now.

    When I went to chat, nothing was happening. I added a line to print out newMessage right before I format it, and it prints out every message that is sent to the console, uncensored. So, basically, the data from newMessage is still seen as event.getMessage. The replace data is not accessed outside of the for loop. How do I fix this? Am I missing something basic?
     
  27. Offline

    teej107

  28. Offline

    TechBug2012

  29. Offline

    teej107

    @TechBug2012 Put this outside of the for loop replacing the words. It doesn't need to run for every word in the word list.
    Apart from that, does the code work?
     
  30. Offline

    TechBug2012

    Nope :(
    When I chat, no messages are censored. I don't think this line
    Code:
            for (String word : wordList) {
                newMessage = newMessage.replace(" " + word + " ", "****");
            }
    
    is accessed outside of the loop.
    Code:
            newMessage = String.format(event.getFormat(), event.getPlayer().getDisplayName(), newMessage);
            for (Player p : filteredPlayers) {
                p.sendMessage(newMessage);
            }
    
    newMessage is originally defined as event.getMessage on line 72.
    Code:
    String newMessage = event.getMessage();
    I think the filteredPlayers loop thinks that newMessage is still event.getMessage. Am I wrong? Why isn't the censored message sending?
     
Thread Status:
Not open for further replies.

Share This Page