diff options
| author | youngcoder45 <youngcoder45@gmail.com> | 2026-03-10 16:52:56 +0530 |
|---|---|---|
| committer | Elis Eriksson <spelis.loves.rust@gmail.com> | 2026-03-12 07:11:01 +0100 |
| commit | da8563af76c9b83d14005dbb1cb753ad0ae290b1 (patch) | |
| tree | ca00ef1e647e2f658aff89b9f2df0a22699f40fd /src/main/java | |
| parent | fce932eff7f77e3f5a9fa3f6886df8f642d66bd6 (diff) | |
| download | svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar.gz svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar.bz2 svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar.lz svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar.xz svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.tar.zst svc_intercom-da8563af76c9b83d14005dbb1cb753ad0ae290b1.zip | |
optional cords and broadcast are separate now
Diffstat (limited to 'src/main/java')
5 files changed, 329 insertions, 142 deletions
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 5a871a3..b172423 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 @@ -2,6 +2,7 @@ package eu.projnull.spelis.svci.commands.handlers; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; import de.maxhenkel.voicechat.api.VoicechatConnection; import de.maxhenkel.voicechat.api.VoicechatServerApi; import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer; @@ -11,6 +12,7 @@ import eu.projnull.spelis.svci.commands.Handler; import eu.projnull.spelis.svci.commands.Helpers; import eu.projnull.spelis.svci.misc.OggDecoder; import eu.projnull.spelis.svci.voice.BroadcasterState; +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; @@ -25,110 +27,167 @@ import java.util.UUID; public class FileCommand implements Handler { @Override public ArgumentBuilder<CommandSourceStack, ?> buildCommand() { - return Commands.literal("file").requires(cs->cs.getSender().hasPermission("svcintercom.broadcast.start")).then(Commands.argument("filename", StringArgumentType.string()).suggests((ctx, builder) -> { - Helpers.getAllSoundsSuggestion(builder); - return builder.buildFuture(); - }).then(Commands.argument("world", StringArgumentType.word()).suggests((ctx, builder) -> { - Helpers.getAllWorldsSuggestion(builder); - return builder.buildFuture(); - }).executes(ctx -> { - CommandSourceStack source = ctx.getSource(); - CommandSender sender = source.getSender(); - String filename = StringArgumentType.getString(ctx, "filename"); - World world = Bukkit.getWorld(StringArgumentType.getString(ctx, "world")); + return Commands.literal("file") + .requires(cs -> cs.getSender().hasPermission("svcintercom.broadcast.start")) + .then(Commands.argument("filename", StringArgumentType.string()) + .suggests((ctx, builder) -> { + Helpers.getAllSoundsSuggestion(builder); + return builder.buildFuture(); + }) + .then(Commands.argument("world", StringArgumentType.word()) + .suggests((ctx, builder) -> { + Helpers.getAllWorldsSuggestion(builder); + return builder.buildFuture(); + }) + // With optional mode parameter + .then(Commands.argument("mode", StringArgumentType.word()) + .suggests((ctx, builder) -> { + builder.suggest("global"); + builder.suggest("speaker"); + return builder.buildFuture(); + }) + .executes(ctx -> executeFileBroadcast(ctx, true)) + ) + // Without mode parameter (auto-detect) + .executes(ctx -> executeFileBroadcast(ctx, false)) + ) + ); + } + + private int executeFileBroadcast(CommandContext<CommandSourceStack> ctx, boolean hasMode) { + CommandSourceStack source = ctx.getSource(); + CommandSender sender = source.getSender(); + String filename = StringArgumentType.getString(ctx, "filename"); + World world = Bukkit.getWorld(StringArgumentType.getString(ctx, "world")); + + if (world == null) { + sender.sendMessage("§cWorld not found."); + return 0; + } + + if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { + sender.sendMessage("§cA broadcast is already active in this world."); + return 0; + } + + File soundFile = new File(Intercom.getPlugin(Intercom.class).getDataFolder(), "sounds/" + filename); + if (!soundFile.exists()) { + Intercom.LOGGER.warn("File not found: {}", soundFile.getAbsoluteFile()); + sender.sendMessage("§cFile not found."); + return 0; + } - if (world == null) { - sender.sendMessage("§cWorld not found."); + // Determine broadcast mode + BroadcasterState.Broadcaster.BroadcastMode mode; + if (hasMode) { + String modeStr = StringArgumentType.getString(ctx, "mode").toLowerCase(); + if (modeStr.equals("global")) { + mode = BroadcasterState.Broadcaster.BroadcastMode.GLOBAL; + } else if (modeStr.equals("speaker")) { + mode = BroadcasterState.Broadcaster.BroadcastMode.SPEAKER; + } else { + sender.sendMessage("§cInvalid mode. Use 'global' or 'speaker'"); return 0; } + } else { + // Auto-detect: use speaker mode if speakers exist, otherwise global + boolean hasSpeakers = !SpeakerManager.inst().getSpeakers(world.getUID()).isEmpty(); + mode = hasSpeakers ? BroadcasterState.Broadcaster.BroadcastMode.SPEAKER : BroadcasterState.Broadcaster.BroadcastMode.GLOBAL; + } - if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { - sender.sendMessage("§cA broadcast is already active in this world."); - return 0; + // Warn if speaker mode selected but no speakers exist + if (mode == BroadcasterState.Broadcaster.BroadcastMode.SPEAKER) { + if (SpeakerManager.inst().getSpeakers(world.getUID()).isEmpty()) { + sender.sendMessage("§eWarning: Speaker mode selected but no speakers exist in this world. No one will hear the broadcast!"); } + } - File soundFile = new File(Intercom.getPlugin(Intercom.class).getDataFolder(), "sounds/" + filename); - if (!soundFile.exists()) { - Intercom.LOGGER.warn("File not found: {}", soundFile.getAbsoluteFile()); - sender.sendMessage("§cFile not found."); - return 0; + final BroadcasterState.Broadcaster.BroadcastMode finalMode = mode; + + Bukkit.getScheduler().runTaskAsynchronously(Intercom.getPlugin(Intercom.class), () -> { + short[] pcm; + try { + pcm = OggDecoder.decode(soundFile); + } catch (Exception e) { + sender.sendMessage("§cFailed to decode file."); + Intercom.LOGGER.warn("Failed to decode file: {}", e.getMessage()); + return; } - Bukkit.getScheduler().runTaskAsynchronously(Intercom.getPlugin(Intercom.class), () -> { - short[] pcm; - try { - pcm = OggDecoder.decode(soundFile); - } catch (Exception e) { - sender.sendMessage("§cFailed to decode file."); - Intercom.LOGGER.warn("File not found: {}", e.getMessage()); + long durationMillis = pcm.length / 48L; + + Bukkit.getScheduler().runTask(Intercom.getPlugin(Intercom.class), () -> { + if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { + sender.sendMessage("§cA broadcast is already active!"); return; } - long durationMillis = pcm.length / 48L; + VoicechatServerApi api = Objects.requireNonNull(Intercom.getPlugin(Intercom.class).getVoicechatPlugin()).getVoicechatServerApi(); + if (api == null) { + sender.sendMessage("§cSimple Voice Chat API not available."); + return; + } - Bukkit.getScheduler().runTask(Intercom.getPlugin(Intercom.class), () -> { - if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { - sender.sendMessage("§cA broadcast is already active!"); - return; - } + de.maxhenkel.voicechat.api.ServerLevel level = api.fromServerLevel(world); - VoicechatServerApi api = Objects.requireNonNull(Intercom.getPlugin(Intercom.class).getVoicechatPlugin()).getVoicechatServerApi(); - if (api == null) { - sender.sendMessage("§cSimple Voice Chat API not available."); - return; - } + BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster( + null, + world.getUID(), + BroadcasterState.Broadcaster.BroadcastType.FILE, + finalMode, + durationMillis, + filename + ); + BroadcasterState.inst().startBroadcast(broadcaster); - de.maxhenkel.voicechat.api.ServerLevel level = api.fromServerLevel(world); + if (finalMode == BroadcasterState.Broadcaster.BroadcastMode.GLOBAL) { + // Global broadcast - everyone hears equally + for (Player p : world.getPlayers()) { + VoicechatConnection connection = api.getConnectionOf(p.getUniqueId()); + if (connection == null) continue; - BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster(null, world.getUID(), BroadcasterState.Broadcaster.BroadcastType.FILE, durationMillis, filename); - BroadcasterState.inst().startBroadcast(broadcaster); + StaticAudioChannel channel = api.createStaticAudioChannel(UUID.randomUUID(), level, connection); + if (channel == 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()); + AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm); + audioPlayer.startPlaying(); + } + sender.sendMessage("§aFile broadcast started: §f" + filename + " §ain §f" + world.getName() + " §7(global)"); + } else { + // Speaker broadcast - positional audio from speakers + java.util.List<eu.projnull.spelis.svci.voice.Speaker> speakers = 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; + for (eu.projnull.spelis.svci.voice.Speaker speaker : speakers) { + org.bukkit.Location speakerLoc = speaker.getLocation(); + if (speakerLoc == null) continue; - 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.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; - de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel channel = - api.createLocationalAudioChannel(UUID.randomUUID(), level, position); - - if (channel == null) continue; + channel.setDistance((float) speaker.getRange()); - 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)"); + 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); - }); + Bukkit.getScheduler().runTaskLaterAsynchronously( + Intercom.getPlugin(Intercom.class), + () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has ended."), + durationMillis / 50L + ); }); + }); - return 1; - }))); + return 1; } } diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java index a220b11..aab00f9 100644 --- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java +++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java @@ -3,10 +3,12 @@ package eu.projnull.spelis.svci.commands.handlers; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; import eu.projnull.spelis.svci.Intercom; import eu.projnull.spelis.svci.commands.Handler; import eu.projnull.spelis.svci.commands.Helpers; import eu.projnull.spelis.svci.voice.BroadcasterState; +import eu.projnull.spelis.svci.voice.SpeakerManager; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.argument.ArgumentTypes; @@ -20,40 +22,106 @@ public class LiveCommand implements Handler { @Override public ArgumentBuilder<CommandSourceStack, ?> buildCommand() { - return Commands.literal("live").requires(cs->cs.getSender().hasPermission("svcintercom.broadcast.start")).then(Commands.argument("player", ArgumentTypes.player()).then(Commands.argument("world", StringArgumentType.word()).suggests((ctx, builder) -> { - Helpers.getAllWorldsSuggestion(builder); - return builder.buildFuture(); - }).then(Commands.argument("duration", IntegerArgumentType.integer(1, 300)).executes(ctx -> { - CommandSourceStack source = ctx.getSource(); - CommandSender sender = source.getSender(); - Player player = ctx.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(ctx.getSource()).getFirst(); - World world = Bukkit.getWorld(StringArgumentType.getString(ctx, "world")); - int seconds = ctx.getArgument("duration", int.class); + return Commands.literal("live") + .requires(cs -> cs.getSender().hasPermission("svcintercom.broadcast.start")) + .then(Commands.argument("player", ArgumentTypes.player()) + .then(Commands.argument("world", StringArgumentType.word()) + .suggests((ctx, builder) -> { + Helpers.getAllWorldsSuggestion(builder); + return builder.buildFuture(); + }) + .then(Commands.argument("duration", IntegerArgumentType.integer(1, 300)) + // With optional mode parameter + .then(Commands.argument("mode", StringArgumentType.word()) + .suggests((ctx, builder) -> { + builder.suggest("global"); + builder.suggest("speaker"); + return builder.buildFuture(); + }) + .executes(ctx -> executeLiveBroadcast(ctx, true)) + ) + // Without mode parameter (defaults to speaker if speakers exist, otherwise global) + .executes(ctx -> executeLiveBroadcast(ctx, false)) + ) + ) + ); + } - if (world == null) { - sender.sendMessage("§cWorld not found."); - return 0; - } - if (seconds <= 0 || seconds > 300) { - sender.sendMessage("§cInvalid duration (must be 1-300 seconds)"); - return 0; - } + private int executeLiveBroadcast(CommandContext<CommandSourceStack> ctx, boolean hasMode) { + CommandSourceStack source = ctx.getSource(); + CommandSender sender = source.getSender(); + + Player player; + try { + player = ctx.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(ctx.getSource()).getFirst(); + } catch (Exception e) { + sender.sendMessage("§cCould not resolve player"); + return 0; + } + + World world = Bukkit.getWorld(StringArgumentType.getString(ctx, "world")); + int seconds = ctx.getArgument("duration", int.class); + + if (world == null) { + sender.sendMessage("§cWorld not found."); + return 0; + } + if (seconds <= 0 || seconds > 300) { + sender.sendMessage("§cInvalid duration (must be 1-300 seconds)"); + return 0; + } - if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { - sender.sendMessage("§cA broadcast is already active in this world."); + if (BroadcasterState.inst().isBroadcastActive(world.getUID())) { + sender.sendMessage("§cA broadcast is already active in this world."); + return 0; + } + + // Determine broadcast mode + BroadcasterState.Broadcaster.BroadcastMode mode; + if (hasMode) { + String modeStr = StringArgumentType.getString(ctx, "mode").toLowerCase(); + if (modeStr.equals("global")) { + mode = BroadcasterState.Broadcaster.BroadcastMode.GLOBAL; + } else if (modeStr.equals("speaker")) { + mode = BroadcasterState.Broadcaster.BroadcastMode.SPEAKER; + } else { + sender.sendMessage("§cInvalid mode. Use 'global' or 'speaker'"); return 0; } + } else { + // Auto-detect: use speaker mode if speakers exist, otherwise global + boolean hasSpeakers = !SpeakerManager.inst().getSpeakers(world.getUID()).isEmpty(); + mode = hasSpeakers ? BroadcasterState.Broadcaster.BroadcastMode.SPEAKER : BroadcasterState.Broadcaster.BroadcastMode.GLOBAL; + } - BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster(player.getUniqueId(), world.getUID(), BroadcasterState.Broadcaster.BroadcastType.LIVE, seconds * 1000L, null); + // Warn if speaker mode selected but no speakers exist + if (mode == BroadcasterState.Broadcaster.BroadcastMode.SPEAKER) { + if (SpeakerManager.inst().getSpeakers(world.getUID()).isEmpty()) { + sender.sendMessage("§eWarning: Speaker mode selected but no speakers exist in this world. No one will hear the broadcast!"); + } + } - BroadcasterState.inst().startBroadcast(broadcaster); - sender.sendMessage("§aLive broadcast started for " + player.getName() + " in world " + world.getName()); + BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster( + player.getUniqueId(), + world.getUID(), + BroadcasterState.Broadcaster.BroadcastType.LIVE, + mode, + seconds * 1000L, + null + ); - // Schedule auto-stop - Bukkit.getScheduler().runTaskLaterAsynchronously(Intercom.getPlugin(Intercom.class), () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has expired"), seconds * 20L); + BroadcasterState.inst().startBroadcast(broadcaster); + + String modeText = mode == BroadcasterState.Broadcaster.BroadcastMode.GLOBAL ? "§7(global)" : "§7(speaker)"; + sender.sendMessage("§aLive broadcast started for §f" + player.getName() + " §ain world §f" + world.getName() + " " + modeText); + // Schedule auto-stop + Bukkit.getScheduler().runTaskLaterAsynchronously( + Intercom.getPlugin(Intercom.class), + () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has expired"), + seconds * 20L + ); - return 1; - })))); + return 1; } } 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 index b175f4c..b17d857 100644 --- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java +++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/SpeakerCommand.java @@ -33,6 +33,74 @@ public class SpeakerCommand implements Handler { return Commands.literal("add") .requires(cs -> cs.getSender().hasPermission("svcintercom.speaker.add")) .then(Commands.argument("name", StringArgumentType.word()) + // Option 1: Just name and range - uses player's current location + .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 add a speaker at your location"); + 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 named '§f" + name + "§c' 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 at your location: §f" + speaker.toString()); + return 1; + }) + // Option 2: name, range, and world - uses player coords in specified world + .then(Commands.argument("world", StringArgumentType.word()) + .suggests((ctx, builder) -> { + Helpers.getAllWorldsSuggestion(builder); + return builder.buildFuture(); + }) + .executes(ctx -> { + CommandSender sender = ctx.getSource().getSender(); + if (!(sender instanceof Player player)) { + sender.sendMessage("§cYou must be a player to use coordinates from your location"); + return 0; + } + + String name = StringArgumentType.getString(ctx, "name"); + double range = DoubleArgumentType.getDouble(ctx, "range"); + String worldName = StringArgumentType.getString(ctx, "world"); + + World world = Bukkit.getWorld(worldName); + if (world == null) { + sender.sendMessage("§cWorld not found: §f" + worldName); + return 0; + } + + Location loc = player.getLocation(); + UUID worldId = world.getUID(); + + if (SpeakerManager.inst().speakerExists(worldId, name)) { + sender.sendMessage("§cA speaker named '§f" + name + "§c' already exists in world §f" + worldName); + return 0; + } + + // Use player's X, Y, Z but in the specified world + Speaker speaker = new Speaker(worldId, loc.getX(), loc.getY(), loc.getZ(), range, name); + SpeakerManager.inst().addSpeaker(speaker); + + sender.sendMessage("§aAdded speaker in world §f" + worldName + "§a: " + speaker.toString()); + return 1; + }) + ) + ) + // Option 3: Full specification with world and coordinates .then(Commands.argument("world", StringArgumentType.word()) .suggests((ctx, builder) -> { Helpers.getAllWorldsSuggestion(builder); @@ -53,13 +121,13 @@ public class SpeakerCommand implements Handler { World world = Bukkit.getWorld(worldName); if (world == null) { - sender.sendMessage("§cWorld not found: " + worldName); + sender.sendMessage("§cWorld not found: §f" + 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"); + sender.sendMessage("§cA speaker named '§f" + name + "§c' already exists in world §f" + worldName); return 0; } @@ -74,34 +142,6 @@ public class SpeakerCommand implements Handler { ) ) ) - // 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; - }) - ) ); } diff --git a/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java b/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java index 5aafe8a..556afaf 100644 --- a/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java +++ b/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java @@ -75,6 +75,7 @@ public final class BroadcasterState { private final UUID playerId; // null for FILE mode private final UUID worldId; private final BroadcastType type; + private final BroadcastMode mode; // GLOBAL or SPEAKER private final long endTimeMillis; private final String fileName; // only for FILE mode @@ -82,6 +83,7 @@ public final class BroadcasterState { UUID playerId, UUID worldId, BroadcastType type, + BroadcastMode mode, long durationMillis, String fileName ) { @@ -97,9 +99,13 @@ public final class BroadcasterState { if (type == BroadcastType.FILE && fileName == null) throw new IllegalArgumentException("FILE requires fileName"); + if (mode == null) + throw new IllegalArgumentException("mode cannot be null"); + this.playerId = playerId; this.worldId = worldId; this.type = type; + this.mode = mode; this.endTimeMillis = System.currentTimeMillis() + durationMillis; this.fileName = fileName; } @@ -128,9 +134,18 @@ public final class BroadcasterState { return endTimeMillis; } + public BroadcastMode getMode() { + return mode; + } + public enum BroadcastType { LIVE, FILE } + + public enum BroadcastMode { + GLOBAL, // Everyone hears equally, ignores speakers + SPEAKER // Only audible near speakers + } } } 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 ff6575e..88295d2 100644 --- a/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java +++ b/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java @@ -59,18 +59,15 @@ 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 + // Check broadcast mode + if (broadcaster.getMode() == BroadcasterState.Broadcaster.BroadcastMode.GLOBAL) { + // Global broadcast - everyone hears equally, no 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()); @@ -78,6 +75,14 @@ public class VoicePlugin implements VoicechatPlugin { return; } + // Speaker broadcast - use positional audio from speakers + java.util.List<Speaker> speakers = SpeakerManager.inst().getSpeakers(worldId); + + if (speakers.isEmpty()) { + // No speakers but speaker mode - no audio will be heard + return; + } + // Use positional audio from speakers for (org.bukkit.entity.Player online : player.getWorld().getPlayers()) { if (online.getUniqueId().equals(player.getUniqueId())) { |
