How do you broadcast a message on a specific distance?

Hi there guys !

Quite a long time ago, I posted a request here where I asked if it would be possible to create a Dice Roll plugin. Since that time, I began to code and today I tried to do this plugin by myself. Everything’s fine, except for a little detail…

I wanted to broadcast the result 50 blocks around the player who entered the command, but I can’t even seem to find how to broadcast it on the server. I arrived to send it to the player, but there’s no point if only him/her can see it !

So, I would like to know how do you guys can do it ^x^’ ! I’m quite new to this, so I’m sorry if it seems a little silly for a question.

Hope one of you will answer me ^^ !

To those who’d need to see my code, it actually looks like this (I give you here the “disfunctional one”, which needs to send a message to everyone 50 blocks around .x. Please don’t mind the french !) :

The command class :

package Salveria;
import org.spongepowered.api.Server;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.text.format.TextStyles;

public class SalveRollExecutor implements CommandExecutor {

public static double random() {
    double random;
    random = Math.random() * 20 + 1;
    if (random == 21)
        random = Math.random() * 20 + 1;

    return random;
}

@Override
public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
    if(!(src instanceof Player)) {
        src.sendMessage(Text.of("C'est un joueur qui doit lancer la commande !"));
        return CommandResult.success();
    }
    Player player = (Player)src;
    Server server = (Server)src;
    int tirage = (int) random();
    server.get().broadcastMessage(Text.of(TextColors.WHITE, server.getPlayer(player.getUniqueId()), " a Roll un ", TextColors.RED, TextStyles.BOLD, tirage, TextStyles.NONE, TextColors.WHITE, " !"));
    server.get
    return CommandResult.success();
}

}

The main class :

package Salveria;

import org.spongepowered.api.Game;
import org.spongepowered.api.command.spec.CommandSpec;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameInitializationEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.event.command.SendCommandEvent;

import javax.inject.Inject;

@Plugin(id = “rollplugin”, name=“SalveRollia”, version = “1.0”)
public class SalveRoll {

@Inject
Game game;

@Listener
public void onInit(GameInitializationEvent e) {
    CommandSpec RollCmd = CommandSpec.builder()
            .description(Text.of("Commande de Roll pour Salveria"))
            .executor(new SalveRollExecutor())
            .build();
    game.getCommandManager().register(this, RollCmd, "roll");
}

}

There are multiple issues that need to be fixed in your code and improvements that should be made. Let me address your original question first:

You have to iterate through all players and send the message to those who are within range. This can be done in two ways - a classic for loop or using Streams.

Vector3d center; //The position of your player, gotten from their location.

//Enchanced for loop
for (Player player : Sponge.getServer().getOnlinePlayers()) {
    if (player.getLocation().getPosition().distance(center) <= 50) {
        player.sendMessage(...);
    }
}

//Streams
Sponge.getServer().getOnlinePlayers().stream()
        .filter(p -> p.getLocation().getPosition().distance(center) <= 50)
        .forEach(p -> p.sendMessage(...));

With that having been said, let’s address some of the other things you have going on.

  • Your random function should be using Random#nextInt. You should then save the Random instance as a static variable; do not create it every time.
  • Your if (random == 21) check does not make sense. Not only is it impossible for that to be true (Math.random() returns a double in range [0,1)), but you then use the same code to replace the value.
  • Your instanceof check is good - if you expect the source to be a Player, you need to verify that before casting. However, you should not return CommandResult.success() - it is more appropriate to throw a CommandException instead.
  • Why do you cast the source to Server? This makes no sense, and any reasonably IDE should be identifying that as an error like no tomorrow.
  • Server#get is not a method.
  • Why do you get the player again from the server? You already have it from the CommandSource that you verified was a Player.
  • Your @Plugin class should store your PluginContainer and Logger, as well as have methods to access them from outside of your class - you may not need them now, but soon.
  • @Injecting the Game isn’t worth it. The majority of it’s methods can be done just as easily through Sponge and there’s no reason to save it. Call it when you need it
  • Your CommandSpec should have a permission attached to it. See the Permissions page on the Sponge Docs for info on this.
  • As a specific example of the Game thing about, you can also use Sponge.getCommandManage() - same effect, no additional variable.
  • As a final tip, many of the people here use Semantic Versioning - if you’re considering releasing this plugin to Ore, I would recommend using it.

Hopefully you’ll be able to make use of that to start working your way around things. However, I would also recommend reviewing some of your core Java knowledge - some of the things like your random() method and casting the source to a Server imply a shaky understanding of Java. Sponge is an advanced system, and the more you understand Java the easier it’ll be to work with it. Good luck!

1 Like

Hi, and thank you for your advice ^^ !

I admit the for loop was a good idea. Sometimes, the obvious stays the best solution, but I often forget it, heh…
I’ll try it when I’ll have some time to do so. I also thank you for all the points you talked about and I will do my best to apply all of your advice as good as I can. You must have understood that Java is not my most successful domain, indeed ^x^’ but, well, we’ve all been beginners !

Thank you again four the help you gave me !
If someone else has something to say, I’d still be happy to hear what you’d have to add !

Just as a side note, most IDEs won’t check this since both Player and Server are interfaces, and there’s always the chance that someone will implement both. No one does, but an IDE can’t know that.

Hello @Say!

I have actually created a plugin doing this, and I discovered a built in function (in SpongeAPI 7) which you can use to find all players in a certain radius around another player:

player.getNearbyEntities(radius);

This will return all the nearby entities, and then you just need to check who of them are players, so here would be the complete code:

Collection<Entity> nearbyEntities = player.getNearbyEntities(50);
Iterator<Entity> iterator = nearbyEntities.iterator();
Entity targetEntity;
Player targetPlayer;
while(iterator.hasNext()){
  targetEntity = iterator.next();
  if(targetEntity.getType().equals(EntityTypes.PLAYER)){
    targetPlayer = (Player) targetEntity;
    messageChannel.addMember(targetPlayer);
  }
}

Of course, messageChannel will be the MessageChannel containing all the receivers of the message, and you still need to add all of the imports. But then this code will work!


Just let me know if you need help with anything or if I did something wrong.