aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authoryoungcoder45 <youngcoder45@gmail.com>2026-03-09 23:51:01 +0530
committerElis Eriksson <spelis.loves.rust@gmail.com>2026-03-12 07:11:01 +0100
commitfce932eff7f77e3f5a9fa3f6886df8f642d66bd6 (patch)
tree08aa12c7ca12656ebaf69abe14e0cc1630218738 /src/main/java
parent65a247a7d0900fd105d5e9d48a6571cd480fc155 (diff)
downloadsvc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar.gz
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar.bz2
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar.lz
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar.xz
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.tar.zst
svc_intercom-fce932eff7f77e3f5a9fa3f6886df8f642d66bd6.zip
update
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/eu/projnull/spelis/svci/Intercom.java4
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java1
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java46
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java204
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/Speaker.java74
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/SpeakerManager.java164
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java55
7 files changed, 537 insertions, 11 deletions
diff --git a/src/main/java/eu/projnull/spelis/svci/Intercom.java b/src/main/java/eu/projnull/spelis/svci/Intercom.java
index 4f40019..b7808ef 100644
--- a/src/main/java/eu/projnull/spelis/svci/Intercom.java
+++ b/src/main/java/eu/projnull/spelis/svci/Intercom.java
@@ -3,6 +3,7 @@ package eu.projnull.spelis.svci;
import de.maxhenkel.voicechat.api.BukkitVoicechatService;
import eu.projnull.spelis.svci.commands.IntercomCommand;
import eu.projnull.spelis.svci.misc.BroadcastHudTask;
+import eu.projnull.spelis.svci.voice.SpeakerManager;
import eu.projnull.spelis.svci.voice.VoicePlugin;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import org.apache.logging.log4j.LogManager;
@@ -23,6 +24,9 @@ public final class Intercom extends JavaPlugin {
@Override
public void onEnable() {
+ // Initialize speaker system
+ SpeakerManager.inst().initialize(this.getDataFolder());
+
BukkitVoicechatService service = getServer().getServicesManager().load(BukkitVoicechatService.class);
if (service != null) {
voicechatPlugin = new VoicePlugin();
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java
index 418408e..cb1972d 100644
--- a/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java
+++ b/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java
@@ -19,6 +19,7 @@ public class IntercomCommand {
registerHandler(new LiveCommand());
registerHandler(new FileCommand());
registerHandler(new AboutCommand());
+ registerHandler(new SpeakerCommand());
}
public void registerHandler(Handler handler) {
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java
index 3d7fb4c..5a871a3 100644
--- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java
@@ -83,18 +83,46 @@ public class FileCommand implements Handler {
BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster(null, world.getUID(), BroadcasterState.Broadcaster.BroadcastType.FILE, durationMillis, filename);
BroadcasterState.inst().startBroadcast(broadcaster);
- for (Player p : world.getPlayers()) {
- VoicechatConnection connection = api.getConnectionOf(p.getUniqueId());
- if (connection == null) continue;
+ // Get all speakers in this world
+ java.util.List<eu.projnull.spelis.svci.voice.Speaker> speakers = eu.projnull.spelis.svci.voice.SpeakerManager.inst().getSpeakers(world.getUID());
+
+ if (speakers.isEmpty()) {
+ // Fallback to old behavior if no speakers are defined
+ for (Player p : world.getPlayers()) {
+ VoicechatConnection connection = api.getConnectionOf(p.getUniqueId());
+ if (connection == null) continue;
- StaticAudioChannel channel = api.createStaticAudioChannel(UUID.randomUUID(), level, connection);
- if (channel == null) continue;
+ StaticAudioChannel channel = api.createStaticAudioChannel(UUID.randomUUID(), level, connection);
+ if (channel == null) continue;
- AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm);
- audioPlayer.startPlaying();
- }
+ AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm);
+ audioPlayer.startPlaying();
+ }
+ sender.sendMessage("§aFile broadcast started: §f" + filename + " §ain §f" + world.getName() + " §7(no speakers, using global audio)");
+ } else {
+ // Use positional audio from speakers
+ for (eu.projnull.spelis.svci.voice.Speaker speaker : speakers) {
+ org.bukkit.Location speakerLoc = speaker.getLocation();
+ if (speakerLoc == null) continue;
+
+ de.maxhenkel.voicechat.api.Position position = api.createPosition(
+ speakerLoc.getX(),
+ speakerLoc.getY(),
+ speakerLoc.getZ()
+ );
+
+ de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel channel =
+ api.createLocationalAudioChannel(UUID.randomUUID(), level, position);
+
+ if (channel == null) continue;
- sender.sendMessage("§aFile broadcast started: §f" + filename + " §ain §f" + world.getName());
+ channel.setDistance((float) speaker.getRange());
+
+ AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm);
+ audioPlayer.startPlaying();
+ }
+ sender.sendMessage("§aFile broadcast started: §f" + filename + " §ain §f" + world.getName() + " §7(" + speakers.size() + " speakers)");
+ }
Bukkit.getScheduler().runTaskLaterAsynchronously(Intercom.getPlugin(Intercom.class), () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has ended."), durationMillis / 50L);
});
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java
new file mode 100644
index 0000000..b175f4c
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java
@@ -0,0 +1,204 @@
+package eu.projnull.spelis.svci.commands.handlers;
+
+import com.mojang.brigadier.arguments.DoubleArgumentType;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import eu.projnull.spelis.svci.commands.Handler;
+import eu.projnull.spelis.svci.commands.Helpers;
+import eu.projnull.spelis.svci.voice.Speaker;
+import eu.projnull.spelis.svci.voice.SpeakerManager;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.List;
+import java.util.UUID;
+
+public class SpeakerCommand implements Handler {
+
+ @Override
+ public ArgumentBuilder<CommandSourceStack, ?> buildCommand() {
+ return Commands.literal("speaker")
+ .requires(cs -> cs.getSender().hasPermission("svcintercom.speaker"))
+ .then(buildAddCommand())
+ .then(buildRemoveCommand())
+ .then(buildListCommand());
+ }
+
+ private ArgumentBuilder<CommandSourceStack, ?> buildAddCommand() {
+ return Commands.literal("add")
+ .requires(cs -> cs.getSender().hasPermission("svcintercom.speaker.add"))
+ .then(Commands.argument("name", StringArgumentType.word())
+ .then(Commands.argument("world", StringArgumentType.word())
+ .suggests((ctx, builder) -> {
+ Helpers.getAllWorldsSuggestion(builder);
+ return builder.buildFuture();
+ })
+ .then(Commands.argument("x", DoubleArgumentType.doubleArg())
+ .then(Commands.argument("y", DoubleArgumentType.doubleArg())
+ .then(Commands.argument("z", DoubleArgumentType.doubleArg())
+ .then(Commands.argument("range", DoubleArgumentType.doubleArg(1.0, 1000.0))
+ .executes(ctx -> {
+ CommandSender sender = ctx.getSource().getSender();
+ String name = StringArgumentType.getString(ctx, "name");
+ String worldName = StringArgumentType.getString(ctx, "world");
+ double x = DoubleArgumentType.getDouble(ctx, "x");
+ double y = DoubleArgumentType.getDouble(ctx, "y");
+ double z = DoubleArgumentType.getDouble(ctx, "z");
+ double range = DoubleArgumentType.getDouble(ctx, "range");
+
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ sender.sendMessage("§cWorld not found: " + worldName);
+ return 0;
+ }
+
+ UUID worldId = world.getUID();
+ if (SpeakerManager.inst().speakerExists(worldId, name)) {
+ sender.sendMessage("§cA speaker with that name already exists in this world");
+ return 0;
+ }
+
+ Speaker speaker = new Speaker(worldId, x, y, z, range, name);
+ SpeakerManager.inst().addSpeaker(speaker);
+
+ sender.sendMessage("§aAdded speaker: §f" + speaker.toString());
+ return 1;
+ })
+ )
+ )
+ )
+ )
+ )
+ // Add shorthand for current location (player only)
+ .then(Commands.argument("range", DoubleArgumentType.doubleArg(1.0, 1000.0))
+ .executes(ctx -> {
+ CommandSender sender = ctx.getSource().getSender();
+ if (!(sender instanceof Player player)) {
+ sender.sendMessage("§cYou must be a player to use this command without coordinates");
+ return 0;
+ }
+
+ String name = StringArgumentType.getString(ctx, "name");
+ double range = DoubleArgumentType.getDouble(ctx, "range");
+
+ Location loc = player.getLocation();
+ World world = loc.getWorld();
+ UUID worldId = world.getUID();
+
+ if (SpeakerManager.inst().speakerExists(worldId, name)) {
+ sender.sendMessage("§cA speaker with that name already exists in this world");
+ return 0;
+ }
+
+ Speaker speaker = new Speaker(worldId, loc.getX(), loc.getY(), loc.getZ(), range, name);
+ SpeakerManager.inst().addSpeaker(speaker);
+
+ sender.sendMessage("§aAdded speaker: §f" + speaker.toString());
+ return 1;
+ })
+ )
+ );
+ }
+
+ private ArgumentBuilder<CommandSourceStack, ?> buildRemoveCommand() {
+ return Commands.literal("remove")
+ .requires(cs -> cs.getSender().hasPermission("svcintercom.speaker.remove"))
+ .then(Commands.argument("world", StringArgumentType.word())
+ .suggests((ctx, builder) -> {
+ Helpers.getAllWorldsSuggestion(builder);
+ return builder.buildFuture();
+ })
+ .then(Commands.argument("name", StringArgumentType.word())
+ .suggests((ctx, builder) -> {
+ String worldName = StringArgumentType.getString(ctx, "world");
+ World world = Bukkit.getWorld(worldName);
+ if (world != null) {
+ List<Speaker> speakers = SpeakerManager.inst().getSpeakers(world.getUID());
+ for (Speaker speaker : speakers) {
+ builder.suggest(speaker.getName());
+ }
+ }
+ return builder.buildFuture();
+ })
+ .executes(ctx -> {
+ CommandSender sender = ctx.getSource().getSender();
+ String worldName = StringArgumentType.getString(ctx, "world");
+ String name = StringArgumentType.getString(ctx, "name");
+
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ sender.sendMessage("§cWorld not found: " + worldName);
+ return 0;
+ }
+
+ boolean removed = SpeakerManager.inst().removeSpeaker(world.getUID(), name);
+ if (removed) {
+ sender.sendMessage("§aRemoved speaker: §f" + name + " §afrom §f" + worldName);
+ return 1;
+ } else {
+ sender.sendMessage("§cSpeaker not found: " + name);
+ return 0;
+ }
+ })
+ )
+ );
+ }
+
+ private ArgumentBuilder<CommandSourceStack, ?> buildListCommand() {
+ return Commands.literal("list")
+ .requires(cs -> cs.getSender().hasPermission("svcintercom.speaker.list"))
+ .then(Commands.argument("world", StringArgumentType.word())
+ .suggests((ctx, builder) -> {
+ Helpers.getAllWorldsSuggestion(builder);
+ return builder.buildFuture();
+ })
+ .executes(ctx -> {
+ CommandSender sender = ctx.getSource().getSender();
+ String worldName = StringArgumentType.getString(ctx, "world");
+
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ sender.sendMessage("§cWorld not found: " + worldName);
+ return 0;
+ }
+
+ List<Speaker> speakers = SpeakerManager.inst().getSpeakers(world.getUID());
+ if (speakers.isEmpty()) {
+ sender.sendMessage("§cNo speakers in world: " + worldName);
+ return 0;
+ }
+
+ sender.sendMessage("§aSpeakers in §f" + worldName + "§a:");
+ for (Speaker speaker : speakers) {
+ sender.sendMessage(" §f- " + speaker.toString());
+ }
+ return 1;
+ })
+ )
+ .executes(ctx -> {
+ CommandSender sender = ctx.getSource().getSender();
+ if (!(sender instanceof Player player)) {
+ sender.sendMessage("§cYou must specify a world or be a player");
+ return 0;
+ }
+
+ World world = player.getWorld();
+ List<Speaker> speakers = SpeakerManager.inst().getSpeakers(world.getUID());
+ if (speakers.isEmpty()) {
+ sender.sendMessage("§cNo speakers in your current world");
+ return 0;
+ }
+
+ sender.sendMessage("§aSpeakers in §f" + world.getName() + "§a:");
+ for (Speaker speaker : speakers) {
+ sender.sendMessage(" §f- " + speaker.toString());
+ }
+ return 1;
+ });
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/voice/Speaker.java b/src/main/java/eu/projnull/spelis/svci/voice/Speaker.java
new file mode 100644
index 0000000..0791831
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/voice/Speaker.java
@@ -0,0 +1,74 @@
+package eu.projnull.spelis.svci.voice;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+
+import java.util.UUID;
+
+public class Speaker {
+ private final UUID worldId;
+ private final double x;
+ private final double y;
+ private final double z;
+ private final double range;
+ private final String name;
+
+ public Speaker(UUID worldId, double x, double y, double z, double range, String name) {
+ this.worldId = worldId;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.range = range;
+ this.name = name;
+ }
+
+ public UUID getWorldId() {
+ return worldId;
+ }
+
+ public double getX() {
+ return x;
+ }
+
+ public double getY() {
+ return y;
+ }
+
+ public double getZ() {
+ return z;
+ }
+
+ public double getRange() {
+ return range;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Location getLocation() {
+ World world = Bukkit.getWorld(worldId);
+ if (world == null) return null;
+ return new Location(world, x, y, z);
+ }
+
+ public boolean isInRange(Location playerLocation) {
+ if (playerLocation == null || playerLocation.getWorld() == null) return false;
+ if (!playerLocation.getWorld().getUID().equals(worldId)) return false;
+
+ double dx = playerLocation.getX() - x;
+ double dy = playerLocation.getY() - y;
+ double dz = playerLocation.getZ() - z;
+ double distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
+
+ return distance <= range;
+ }
+
+ @Override
+ public String toString() {
+ World world = Bukkit.getWorld(worldId);
+ String worldName = world != null ? world.getName() : worldId.toString();
+ return String.format("%s @ %s (%.1f, %.1f, %.1f) range: %.1f", name, worldName, x, y, z, range);
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/voice/SpeakerManager.java b/src/main/java/eu/projnull/spelis/svci/voice/SpeakerManager.java
new file mode 100644
index 0000000..113639f
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/voice/SpeakerManager.java
@@ -0,0 +1,164 @@
+package eu.projnull.spelis.svci.voice;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import eu.projnull.spelis.svci.Intercom;
+import org.bukkit.Location;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+public class SpeakerManager {
+ private static final SpeakerManager INSTANCE = new SpeakerManager();
+ private final Map<UUID, List<Speaker>> speakers = new ConcurrentHashMap<>();
+ private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private File dataFile;
+
+ private SpeakerManager() {
+ }
+
+ public static SpeakerManager inst() {
+ return INSTANCE;
+ }
+
+ public void initialize(File dataFolder) {
+ this.dataFile = new File(dataFolder, "speakers.json");
+ load();
+ }
+
+ public void addSpeaker(Speaker speaker) {
+ speakers.computeIfAbsent(speaker.getWorldId(), k -> new ArrayList<>()).add(speaker);
+ save();
+ }
+
+ public boolean removeSpeaker(UUID worldId, String name) {
+ List<Speaker> worldSpeakers = speakers.get(worldId);
+ if (worldSpeakers == null) return false;
+
+ boolean removed = worldSpeakers.removeIf(s -> s.getName().equalsIgnoreCase(name));
+ if (removed) {
+ if (worldSpeakers.isEmpty()) {
+ speakers.remove(worldId);
+ }
+ save();
+ }
+ return removed;
+ }
+
+ public List<Speaker> getSpeakers(UUID worldId) {
+ return speakers.getOrDefault(worldId, Collections.emptyList());
+ }
+
+ public List<Speaker> getSpeakersInRange(Location location) {
+ if (location == null || location.getWorld() == null) return Collections.emptyList();
+
+ List<Speaker> worldSpeakers = speakers.get(location.getWorld().getUID());
+ if (worldSpeakers == null) return Collections.emptyList();
+
+ return worldSpeakers.stream()
+ .filter(speaker -> speaker.isInRange(location))
+ .collect(Collectors.toList());
+ }
+
+ public Speaker getSpeaker(UUID worldId, String name) {
+ List<Speaker> worldSpeakers = speakers.get(worldId);
+ if (worldSpeakers == null) return null;
+
+ return worldSpeakers.stream()
+ .filter(s -> s.getName().equalsIgnoreCase(name))
+ .findFirst()
+ .orElse(null);
+ }
+
+ public boolean speakerExists(UUID worldId, String name) {
+ return getSpeaker(worldId, name) != null;
+ }
+
+ private void save() {
+ try {
+ dataFile.getParentFile().mkdirs();
+
+ List<SpeakerData> data = new ArrayList<>();
+ for (Map.Entry<UUID, List<Speaker>> entry : speakers.entrySet()) {
+ for (Speaker speaker : entry.getValue()) {
+ data.add(new SpeakerData(
+ speaker.getWorldId().toString(),
+ speaker.getX(),
+ speaker.getY(),
+ speaker.getZ(),
+ speaker.getRange(),
+ speaker.getName()
+ ));
+ }
+ }
+
+ try (FileWriter writer = new FileWriter(dataFile)) {
+ gson.toJson(data, writer);
+ }
+
+ Intercom.LOGGER.info("Saved {} speakers to disk", data.size());
+ } catch (IOException e) {
+ Intercom.LOGGER.error("Failed to save speakers", e);
+ }
+ }
+
+ private void load() {
+ if (!dataFile.exists()) {
+ Intercom.LOGGER.info("No speakers file found, starting fresh");
+ return;
+ }
+
+ try (FileReader reader = new FileReader(dataFile)) {
+ Type listType = new TypeToken<List<SpeakerData>>() {}.getType();
+ List<SpeakerData> data = gson.fromJson(reader, listType);
+
+ if (data == null) {
+ Intercom.LOGGER.warn("Speakers file is empty or corrupted");
+ return;
+ }
+
+ speakers.clear();
+ for (SpeakerData speakerData : data) {
+ UUID worldId = UUID.fromString(speakerData.worldId);
+ Speaker speaker = new Speaker(
+ worldId,
+ speakerData.x,
+ speakerData.y,
+ speakerData.z,
+ speakerData.range,
+ speakerData.name
+ );
+ speakers.computeIfAbsent(worldId, k -> new ArrayList<>()).add(speaker);
+ }
+
+ Intercom.LOGGER.info("Loaded {} speakers from disk", data.size());
+ } catch (IOException e) {
+ Intercom.LOGGER.error("Failed to load speakers", e);
+ }
+ }
+
+ private static class SpeakerData {
+ String worldId;
+ double x;
+ double y;
+ double z;
+ double range;
+ String name;
+
+ SpeakerData(String worldId, double x, double y, double z, double range, String name) {
+ this.worldId = worldId;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.range = range;
+ this.name = name;
+ }
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java b/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java
index 55b19ca..ff6575e 100644
--- a/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java
+++ b/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java
@@ -59,16 +59,67 @@ public class VoicePlugin implements VoicechatPlugin {
VoicechatServerApi api = event.getVoicechat();
+ // Get all speakers in this world
+ java.util.List<Speaker> speakers = SpeakerManager.inst().getSpeakers(worldId);
+
+ if (speakers.isEmpty()) {
+ // Fallback to old behavior if no speakers are defined
+ for (org.bukkit.entity.Player online : player.getWorld().getPlayers()) {
+ if (online.getUniqueId().equals(player.getUniqueId())) {
+ continue;
+ }
+
+ VoicechatConnection connection = api.getConnectionOf(online.getUniqueId());
+
+ if (connection == null) continue;
+
+ api.sendStaticSoundPacketTo(connection, event.getPacket().toStaticSoundPacket());
+ }
+ return;
+ }
+
+ // Use positional audio from speakers
for (org.bukkit.entity.Player online : player.getWorld().getPlayers()) {
if (online.getUniqueId().equals(player.getUniqueId())) {
continue;
}
VoicechatConnection connection = api.getConnectionOf(online.getUniqueId());
-
if (connection == null) continue;
- api.sendStaticSoundPacketTo(connection, event.getPacket().toStaticSoundPacket());
+ // Find nearest speaker to the player
+ Speaker nearestSpeaker = null;
+ double nearestDistance = Double.MAX_VALUE;
+
+ for (Speaker speaker : speakers) {
+ if (speaker.isInRange(online.getLocation())) {
+ org.bukkit.Location speakerLoc = speaker.getLocation();
+ if (speakerLoc == null) continue;
+
+ double distance = online.getLocation().distance(speakerLoc);
+ if (distance < nearestDistance) {
+ nearestDistance = distance;
+ nearestSpeaker = speaker;
+ }
+ }
+ }
+
+ // Only send audio if player is in range of at least one speaker
+ if (nearestSpeaker != null) {
+ org.bukkit.Location speakerLoc = nearestSpeaker.getLocation();
+
+ // Create a locational sound packet from the speaker position
+ de.maxhenkel.voicechat.api.Position position = api.createPosition(
+ speakerLoc.getX(),
+ speakerLoc.getY(),
+ speakerLoc.getZ()
+ );
+
+ api.sendLocationalSoundPacketTo(
+ connection,
+ event.getPacket().toLocationalSoundPacket(position)
+ );
+ }
}
}
}