Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#499 - Implement playsound command #531

Merged
merged 4 commits into from
Jul 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/net/glowstone/GlowServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ private void loadPlugins() {
commandMap.register("minecraft", new SpawnPointCommand());
commandMap.register("minecraft", new ToggleDownfallCommand());
commandMap.register("minecraft", new SetWorldSpawnCommand());
commandMap.register("minecraft", new PlaySoundCommand());

File folder = new File(config.getString(Key.PLUGIN_FOLDER));
if (!folder.isDirectory() && !folder.mkdirs()) {
Expand Down
181 changes: 181 additions & 0 deletions src/main/java/net/glowstone/command/minecraft/PlaySoundCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package net.glowstone.command.minecraft;

import net.glowstone.command.CommandTarget;
import net.glowstone.command.CommandUtils;
import net.glowstone.constants.GlowSound;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.util.SoundUtil;
import org.bukkit.*;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.VanillaCommand;
import org.bukkit.entity.Entity;
import org.bukkit.util.StringUtil;

import java.util.*;
import java.util.stream.Collectors;

public class PlaySoundCommand extends VanillaCommand {

private static final List<String> SOURCES = Arrays.stream(SoundCategory.values()).map(SoundCategory::name).map(String::toLowerCase).collect(Collectors.toList());

private static final Set<String> SOUNDS = GlowSound.getSounds().keySet();

public PlaySoundCommand() {
super("playsound", "Plays a sound.", "/playsound <sound> <source> <player> [x] [y] [z] [volume] [pitch] [minimumVolume]", Collections.emptyList());
setPermission("minecraft.command.playsound");
}

@Override
public boolean execute(CommandSender sender, String label, String[] args) {
if (!testPermission(sender)) return false;

if (args.length < 3 || args.length == 4 || args.length == 5) {
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
return false;
}

final World world = CommandUtils.getWorld(sender);

if (world == null) {
return false;
}

String stringSound = args[0], stringCategory = args[1], playerPattern = args[2];
final Sound sound = GlowSound.getVanillaSound(stringSound.startsWith("minecraft:") ? stringSound : "minecraft:" + stringSound);
final SoundCategory soundCategory = SoundUtil.buildSoundCategory(stringCategory);
List<GlowPlayer> targets;
boolean relativeLocation = false;
double volume = 1, minimumVolume = 0, pitch = 1;

if (sound == null) {
sender.sendMessage(ChatColor.RED + "'" + stringSound + "' is not a valid sound.");
return false;
}

if (soundCategory == null) {
sender.sendMessage(ChatColor.RED + "'" + stringCategory + "' is not a valid sound category.");
return false;
}

// Manage player(s)
if (playerPattern.startsWith("@") && playerPattern.length() > 1 && CommandUtils.isPhysical(sender)) { // Manage selectors
final Location senderLocation = CommandUtils.getLocation(sender);
final Entity[] entities = new CommandTarget(sender, args[0]).getMatched(senderLocation);
targets = Arrays.stream(entities).filter(GlowPlayer.class::isInstance).map(GlowPlayer.class::cast).collect(Collectors.toList());
} else {
final GlowPlayer player = (GlowPlayer) Bukkit.getPlayerExact(playerPattern);

if (player == null) {
sender.sendMessage(ChatColor.RED + "Player '" + playerPattern + "' cannot be found");
return false;
} else {
targets = Collections.singletonList(player);
}
}

if (args.length >= 9) {
try {
minimumVolume = Double.valueOf(args[8]);

if (minimumVolume < 0 || minimumVolume > 1) {
sender.sendMessage(ChatColor.RED + "Minimum volume value (" + args[8] + ") must be between 0 and 1");
return false;
}
} catch (final NumberFormatException n) {
sender.sendMessage(ChatColor.RED + "'" + args[8] + "' is not a valid number");
return false;
}
}

if (args.length >= 8) {
try {
pitch = Double.valueOf(args[7]);

if (pitch < 0 || pitch > 2) {
sender.sendMessage(ChatColor.RED + "Pitch value (" + args[7] + ") must be between 0 and 2");
return false;
} else if (pitch < 0.5) {
pitch = 0.5;
}

} catch (final NumberFormatException n) {
sender.sendMessage(ChatColor.RED + "'" + args[7] + "' is not a valid number");
return false;
}
}

if (args.length >= 7) {
try {
volume = Double.valueOf(args[6]);
} catch (final NumberFormatException n) {
sender.sendMessage(ChatColor.RED + "'" + args[6] + "' is not a valid number");
return false;
}
}

if (args.length >= 6) {
relativeLocation = args[3].startsWith("~") || args[4].startsWith("~") || args[5].startsWith("~");
}

for (final GlowPlayer target : targets) {
Location soundLocation, targetLocation = target.getLocation();
double targetVolume = volume;

try {
if (relativeLocation) {
soundLocation = CommandUtils.getLocation(targetLocation, args[3], args[4], args[5]);
} else if (args.length >= 6) {
soundLocation = CommandUtils.getLocation(new Location(world, 0, 0, 0), args[3], args[4], args[5]);
} else {
soundLocation = targetLocation;
}
} catch (final NumberFormatException n) {
sender.sendMessage(ChatColor.RED + "The position (" + args[3] + "," + args[4] + "," + args[5] + ") is invalid");
return false;
}

// If the target is outside the normal audible sphere
if (targetLocation.distanceSquared(soundLocation) > Math.pow(volume, 2)) {
if (minimumVolume <= 0) {
sender.sendMessage(ChatColor.RED + target.getName() + " is too far away to hear the sound");
return false;
} else {
final double deltaX = soundLocation.getX() - targetLocation.getX(),
deltaY = soundLocation.getX() - targetLocation.getY(),
deltaZ = soundLocation.getX() - targetLocation.getZ();
final double delta = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2) + Math.pow(deltaZ, 2));

soundLocation = targetLocation;
soundLocation.add(deltaX / delta, deltaY / delta, deltaZ / delta);
targetVolume = minimumVolume;
}
}

target.playSound(soundLocation, sound, soundCategory, (float) targetVolume, (float) pitch);
}

return true;
}

@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
if (args == null) {
return Collections.emptyList();
} else if (args.length == 1) {
String sound = args[0];

if (!sound.startsWith("minecraft:")) {
final int colonIndex = sound.indexOf(':');
sound = "minecraft:" + sound.substring(colonIndex == -1 ? 0 : (colonIndex + 1));
}

return StringUtil.copyPartialMatches(sound, SOUNDS, new ArrayList(SOUNDS.size()));
} else if (args.length == 2) {
return StringUtil.copyPartialMatches(args[1], SOURCES, new ArrayList(SOURCES.size()));
} else if (args.length == 3) {
return super.tabComplete(sender, alias, args);
} else {
return Collections.emptyList();
}
}
}
11 changes: 8 additions & 3 deletions src/main/java/net/glowstone/constants/GlowSound.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.glowstone.constants;

import com.google.common.collect.ImmutableMap;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;

Expand All @@ -8,7 +9,7 @@

public class GlowSound {

private static Map<String, SoundCategory> sounds = new HashMap<>();
private static final Map<String, SoundCategory> SOUNDS = new HashMap<>();

static {
// register vanilla sounds
Expand All @@ -20,11 +21,11 @@ public class GlowSound {
}

public static void reg(String id, SoundCategory category) {
sounds.put(id, category);
SOUNDS.put(id, category);
}

public static SoundCategory getSoundCategory(String id) {
return sounds.get(id);
return SOUNDS.get(id);
}

public static String getVanillaId(Sound sound) {
Expand All @@ -45,4 +46,8 @@ public static Sound getVanillaSound(String id) {
public static SoundCategory getCategory(int category) {
return SoundCategory.values()[category];
}

public static Map<String, SoundCategory> getSounds() {
return (Map) ImmutableMap.builder().putAll(SOUNDS).build();
}
}
20 changes: 20 additions & 0 deletions src/main/java/net/glowstone/util/SoundUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import net.glowstone.entity.GlowPlayer;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
Expand Down Expand Up @@ -36,4 +37,23 @@ public static float randomReal(float range) {
ThreadLocalRandom rand = ThreadLocalRandom.current();
return (rand.nextFloat() - rand.nextFloat()) * range;
}

/**
* Convert a string to a SoundCategory. The comparison is done on the name and is not case-sensitive.
* @param category The string name of the category
* @return The matching SoundCategory, null if none.
*/
public static SoundCategory buildSoundCategory(final String category) {
if (category == null) {
return null;
}

for (final SoundCategory soundCategory : SoundCategory.values()) {
if (category.equalsIgnoreCase(soundCategory.name())) {
return soundCategory;
}
}

return null;
}
}
Loading