1.12.2 Chat distance without sendMessage

Discussion in 'Spigot Plugin Development' started by FutureMcServer, Jan 1, 2021.

  1. I would like to chat away without sending a message with p.sendmessage because, for other features, I would need the message to be deleted, which is impossible with p.sendmessage, advice?
  2. To me it is extremely unclear what you want to do here. Could you explain it in a little more detail? Also please explain what your end goal is (instead of your attempted solution).
    • Agree Agree x 1
  3. You should try AsyncPlayerChatEvent#getRecipients(). It returns all players who will see the message in the chat (Set<Player>). I think if you remove some players, they won't see the message
    • Agree Agree x 1
  4. Deleting a message is impossible (except of course if you code your own client)
  5. I wanted to make a chatdistance without p.sendmessage, only with the event, to be able to e.setcancelled in other chat events
  6. Sorry for double message, I meant that in this way I should remove all the players then do a get of all the players that are not within 5 blocks, if in the server there are 150 players, this action I think is very heavy
  7. You cannot remove chat messages. You can clear the chat by sending a lot of empty lines but that will also clear all other chat. I suggest using the actionbar, bossbar or titles instead.

    As for your concern about performance. You can use the scheduler to create a repeating task that loopt through all online players and sends the message when needed.
  8. You don't need to empty the set and add the players back, you can use an iterator and remove them accordingly based on the distance.

    The performance impact should be negligible in this case, even you have several hundreds of players on the server. You're essentially iterating through a collection and performing one single operation with the element (a very lightweight one, especially if you consider the squared distance instead). Finally, this will not run on the server's main thread, but on several separate threads exclusive for chat.
    • Like Like x 1
    • Agree Agree x 1
  9. This is not good, you do not send the player's message, in doing so you only send a p.sendmessage, which is not expressly requested by me
  10. Where did I even mention anything about p.sendMessage? You don't send the message, the server does it for you after every listener has processed the event. Is this not what you wanted, to be able to cancel the event in other listeners?

    We don't quite get what you're trying to do, give us more details like @3ricL mentioned above.
  11. I don’t think it is possible to intercept messages by other plugins (when another plugins uses Player#sendMessage(...)) Whether or not this is what you mean, I cannot guess. You are still extremely unclear, so if you were to explain it properly we might be able to help you find another way to achieve this.
  12. For example, for the staffchat, when I write @Hello I write hello in the staffchat via sendmessage, in the chat there is a custom name and surname and I wanted to make sure that it is also remotely, when I do @Hello so obviously he sends me both hello in staffchat than in normal chat. So what I want to do is send a message to nearby players avoiding this thing from conflicting with any other event. So summary: I need to do both chat distance and AVOID THIS CHAT CONFLICTS WITH OTHER EVENTS THAT EXPLOIT THIS CHAT! If I put that if an arraylist who checks if the player is inside and then when he writes in chat sets anything, I would like him to cancel the normal chat message !!!

    Normal chat = ChatDistance

    I hope you understand, I'm not English, if I explain myself wrong I don't know, I'm doing my best, with this I think I have given the maximum of examples
  13. there are no alternatives. in any case, this is an asynchronous event
  14. Untested but should work. I don't know a way you can manipulate who get's the message after it is sent, you have to cancel it and send a new one.
    Code (Java):
        public void onAsyncPlayerChat(AsyncPlayerChatEvent event) {
            // Make sure they are in the same world, filter by distance, then send a message
            event.getRecipients().stream().filter(recipient -> recipient.getWorld().equals(event.getPlayer().getWorld()))
            .filter(recipient -> recipient.getLocation().toVector().distance(event.getPlayer().getLocation().toVector()) >= 5)
            .forEach(recipient -> recipient.sendMessage(event.getMessage()));
    EDIT: changed getWorld == to .equals
    #14 ForbiddenSoul, Jan 1, 2021
    Last edited: Jan 1, 2021
  15. he DOESN'T WANT to use SendMessage
  16. Packets? Calling send message just does that for you though... My bad.
  17. No, you seem to miss the point as to why he doesn’t want to use it. With your approach, you will lose any formatting added by additional plugins, like Essentials for example. Those are not inside the message itself. And instead of cancelling, then manually sending it, why don’t you just edit the recipients directly? The API allows you to do this for precisely this occasion.

    Also, as stated before, using Location#distance(..) is a bad idea. Always use the squared distance when possible, as explained in the respective Javadoc.

    For reference, here is how you could set the recipients to only those close-by.
    Code (Java):
    event.getRecipients().removeIf(p ->
       p.getWorld() != event.getPlayer().getWorld() ||
       p.getLocation().distanceSquared(event.getPlayer().getLocation()) > 25
    Btw in this case you can compare the worlds using != as the Spigot API re-uses its objects properly, so if the players are in the same world, those worlds will be the exact same object in memory.

    Keep in mind that (according to Javadoc, couldn’t replicate it) the recipients can potentially be unmodifiable. So in a real plugin, you might want to catch UnsupportedOperationException.I never ran into situation where this was the case, but can’t hurt to be cautious
    #17 3ricL, Jan 1, 2021
    Last edited: Jan 1, 2021
  18. That is so weird, never have I seen the square root function been referred to as so costly to avoid it (more costly than addition, sure).
    I don't see anywhere saying no to use the power function, and it's the exact same thing. Math.pow(number, 1/2) == Math.sqrt(number).
    Good call on just modifying the recipients, I assumed getRecipients() returned a copy.
  19. I'm sorry if I'm wrong, but aren't you able to check if the player is in the list of nearby entities (Player#getNearbyEntities(..)) instead of checking the distance between the players using Location#getDistanceSquared(..)

    edited grammar.
    • Like Like x 1
  20. It isn’t that costly, but it is of course a lot more complex than just taking the squared distance. And given we calculate this for every player in the same world, you do want to optimise it. If you look at the implementation, the distance method internally calls the squared distance one and then takes the root of the result. This means it does an extra (relatively costly) operation. As we can instead just square 5 ourselfs, we can eliminate that.

    What you say about powers is only partially true. Taking a square for example is orders of magnitude more efficient (it is internally only a multiplication). Multiplications can be implemented on the binary level, by shifting bits. I have done this in C as a practice once. Sure taking the 1/2 power is the same as taking the square root. But taking integer powers is thus a lot simpler, as that is just a couple of multiplications. In case you were wondering: a single square root is hundreds of iterations. Most implementations use Newton’s algorithm, which iteratively tries to approach the result. Due to the high precision in a float, this is quite computationally heavy.

    Manually squaring by multiplying, which is what the squared distance does, keeps everything so simpler.