aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/eu
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu')
-rw-r--r--src/main/java/eu/projnull/spelis/svci/Intercom.java6
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/BroadcastTypeHandler.java19
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/Handler.java8
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/Helpers.java37
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java55
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/AboutCommand.java34
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/FileBroadcastHandler.java115
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java106
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoBroadcastHandler.java49
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoCommand.java48
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveBroadcastHandler.java78
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java59
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/StopBroadcastHandler.java38
-rw-r--r--src/main/java/eu/projnull/spelis/svci/commands/handlers/StopCommand.java45
-rw-r--r--src/main/java/eu/projnull/spelis/svci/misc/BroadcastHudTask.java5
-rw-r--r--src/main/java/eu/projnull/spelis/svci/misc/OggDecoder.java23
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java21
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/FileBroadcast.java56
-rw-r--r--src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java25
19 files changed, 395 insertions, 432 deletions
diff --git a/src/main/java/eu/projnull/spelis/svci/Intercom.java b/src/main/java/eu/projnull/spelis/svci/Intercom.java
index e4cf3f5..4f40019 100644
--- a/src/main/java/eu/projnull/spelis/svci/Intercom.java
+++ b/src/main/java/eu/projnull/spelis/svci/Intercom.java
@@ -4,10 +4,10 @@ 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.VoicePlugin;
-import org.bukkit.plugin.java.JavaPlugin;
-
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.bukkit.plugin.java.JavaPlugin;
import javax.annotation.Nullable;
@@ -32,7 +32,7 @@ public final class Intercom extends JavaPlugin {
LOGGER.info("Failed to register intercom plugin");
}
- getCommand("intercom").setExecutor(new IntercomCommand());
+ this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> new IntercomCommand().register(commands));
hudTask.start(this);
}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/BroadcastTypeHandler.java b/src/main/java/eu/projnull/spelis/svci/commands/BroadcastTypeHandler.java
deleted file mode 100644
index b394a74..0000000
--- a/src/main/java/eu/projnull/spelis/svci/commands/BroadcastTypeHandler.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package eu.projnull.spelis.svci.commands;
-
-import org.bukkit.command.CommandSender;
-
-public interface BroadcastTypeHandler {
-
- /**
- * Called when the command executes for this type.
- * @param sender The executor
- * @param args Command arguments
- * @return true if handled successfully (for Bukkit), false to show usage
- */
- boolean execute(CommandSender sender, String[] args);
-
- /**
- * The name of this type (e.g., "file", "live")
- */
- String getName();
-}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/Handler.java b/src/main/java/eu/projnull/spelis/svci/commands/Handler.java
new file mode 100644
index 0000000..62922cf
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/Handler.java
@@ -0,0 +1,8 @@
+package eu.projnull.spelis.svci.commands;
+
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+
+public interface Handler {
+ ArgumentBuilder<CommandSourceStack, ?> buildCommand();
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/Helpers.java b/src/main/java/eu/projnull/spelis/svci/commands/Helpers.java
new file mode 100644
index 0000000..cd65279
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/Helpers.java
@@ -0,0 +1,37 @@
+package eu.projnull.spelis.svci.commands;
+
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+import eu.projnull.spelis.svci.Intercom;
+import eu.projnull.spelis.svci.voice.BroadcasterState;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+
+import java.io.File;
+import java.util.Arrays;
+
+public class Helpers {
+ public static void getActiveWorldsSuggestion(SuggestionsBuilder builder) {
+ BroadcasterState.inst().activeBroadcastWorldNames().forEach(builder::suggest);
+ }
+
+ public static void getAllWorldsSuggestion(SuggestionsBuilder builder) {
+ Bukkit.getWorlds()
+ .stream()
+ .map(World::getName)
+ .forEach(builder::suggest);
+ }
+
+ public static void getAllSoundsSuggestion(SuggestionsBuilder builder) {
+ File soundsDir = new File(
+ Intercom.getPlugin(Intercom.class).getDataFolder(),
+ "sounds"
+ );
+ File[] files = soundsDir.listFiles();
+ if (files == null) return;
+
+ Arrays.stream(files)
+ .filter(File::isFile)
+ .map(File::getName)
+ .forEach(builder::suggest);
+ }
+}
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 1e9e5bc..418408e 100644
--- a/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java
+++ b/src/main/java/eu/projnull/spelis/svci/commands/IntercomCommand.java
@@ -1,48 +1,37 @@
package eu.projnull.spelis.svci.commands;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import eu.projnull.spelis.svci.commands.handlers.*;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
+import org.jetbrains.annotations.NotNull;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
-public class IntercomCommand implements CommandExecutor {
-
- private final Map<String, BroadcastTypeHandler> handlers = new HashMap<>();
+public class IntercomCommand {
+ private final List<Handler> handlers = new ArrayList<>();
public IntercomCommand() {
- registerHandler(new eu.projnull.spelis.svci.commands.handlers.LiveBroadcastHandler());
- registerHandler(new eu.projnull.spelis.svci.commands.handlers.StopBroadcastHandler());
- registerHandler(new eu.projnull.spelis.svci.commands.handlers.InfoBroadcastHandler());
- registerHandler(new eu.projnull.spelis.svci.commands.handlers.FileBroadcastHandler());
+ registerHandler(new InfoCommand());
+ registerHandler(new StopCommand());
+ registerHandler(new LiveCommand());
+ registerHandler(new FileCommand());
+ registerHandler(new AboutCommand());
}
- public void registerHandler(BroadcastTypeHandler handler) {
- handlers.put(handler.getName().toLowerCase(), handler);
+ public void registerHandler(Handler handler) {
+ handlers.add(handler);
}
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
-
- if (!sender.hasPermission("svcintercom.broadcast")) {
- sender.sendMessage("§cYou don't have permission to use the intercom.");
- return true;
- }
-
- if (args.length < 1) {
- sender.sendMessage("§cUsage: /intercom <type> ...");
- return true;
- }
-
- String type = args[0].toLowerCase();
- BroadcastTypeHandler handler = handlers.get(type);
+ public void register(ReloadableRegistrarEvent<@NotNull Commands> commands) {
+ LiteralArgumentBuilder<CommandSourceStack> root = Commands.literal("intercom").requires(cs->cs.getSender().hasPermission("svcintercom.broadcast"));
- if (handler == null) {
- sender.sendMessage("§cUnknown broadcast type: " + type);
- return true;
+ for (Handler handler : handlers) {
+ root.then(handler.buildCommand());
}
- return handler.execute(sender, args);
+ commands.registrar().register(root.build());
}
}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/AboutCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/AboutCommand.java
new file mode 100644
index 0000000..452bfd3
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/AboutCommand.java
@@ -0,0 +1,34 @@
+package eu.projnull.spelis.svci.commands.handlers;
+
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import eu.projnull.spelis.svci.Intercom;
+import eu.projnull.spelis.svci.commands.Handler;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.command.CommandSender;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginDescriptionFile;
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class AboutCommand implements Handler {
+ @Override
+ public ArgumentBuilder<CommandSourceStack, ?> buildCommand() {
+ return Commands.literal("about").executes(ctx -> { // <-- attach here
+ CommandSourceStack source = ctx.getSource();
+ CommandSender sender = source.getSender();
+
+ Plugin plugin = JavaPlugin.getPlugin(Intercom.class);
+ PluginDescriptionFile desc = plugin.getDescription();
+
+ String pluginName = "§e" + desc.getName();
+ String version = "§a" + desc.getVersion();
+
+ String authors = String.join(", ", desc.getAuthors());
+ String authorText = "§7By §b" + authors;
+
+ sender.sendMessage(pluginName + " §fVersion §f" + version + "\n" + authorText);
+
+ return 1;
+ });
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileBroadcastHandler.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileBroadcastHandler.java
deleted file mode 100644
index 71f5f74..0000000
--- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileBroadcastHandler.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package eu.projnull.spelis.svci.commands.handlers;
-
-import de.maxhenkel.voicechat.api.ServerLevel;
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer;
-import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel;
-import de.maxhenkel.voicechat.api.VoicechatConnection;
-import eu.projnull.spelis.svci.Intercom;
-import eu.projnull.spelis.svci.commands.BroadcastTypeHandler;
-import eu.projnull.spelis.svci.voice.BroadcasterState;
-import eu.projnull.spelis.svci.voice.BroadcasterState.Broadcaster;
-import eu.projnull.spelis.svci.voice.BroadcasterState.Broadcaster.BroadcastType;
-import eu.projnull.spelis.svci.misc.OggDecoder;
-import org.bukkit.Bukkit;
-import org.bukkit.World;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-import org.bukkit.craftbukkit.CraftWorld;
-
-import java.io.File;
-import java.util.UUID;
-
-public class FileBroadcastHandler implements BroadcastTypeHandler {
-
- @Override
- public boolean execute(CommandSender sender, String[] args) {
- if (args.length < 3) {
- sender.sendMessage("§cUsage: /intercom file <filename> <world>");
- return true;
- }
-
- String fileName = args[1];
- World world = Bukkit.getWorld(args[2]);
- if (world == null) {
- sender.sendMessage("§cWorld not found.");
- return true;
- }
-
- if (BroadcasterState.inst().isBroadcastActive(world.getUID())) {
- sender.sendMessage("§cA broadcast is already active in this world.");
- return true;
- }
-
- 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: sounds/" + fileName);
- return true;
- }
-
- sender.sendMessage("§7Loading audio file...");
-
- Bukkit.getScheduler().runTaskAsynchronously(Intercom.getPlugin(Intercom.class), () -> {
- short[] pcm;
- try {
- pcm = OggDecoder.decode(soundFile);
- } catch (Exception e) {
- sender.sendMessage("§cFailed to decode audio file: " + e.getMessage());
- return;
- }
-
- long durationMillis = pcm.length / 48L;
-
- Bukkit.getScheduler().runTask(Intercom.getPlugin(Intercom.class), () -> {
- if (BroadcasterState.inst().isBroadcastActive(world.getUID())) {
- sender.sendMessage("§cA broadcast started in this world while the file was loading.");
- return;
- }
-
- VoicechatServerApi api = Intercom.getPlugin(Intercom.class).getVoicechatPlugin().getVoicechatServerApi();
- if (api == null) {
- sender.sendMessage("§cVoice chat API is not available.");
- return;
- }
-
- de.maxhenkel.voicechat.api.ServerLevel level = api.fromServerLevel(world);
-
- Broadcaster broadcaster = new Broadcaster(
- null,
- world.getUID(),
- BroadcastType.FILE,
- durationMillis,
- fileName
- );
- BroadcasterState.inst().startBroadcast(broadcaster);
-
- 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;
-
- AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm);
- audioPlayer.startPlaying();
- }
-
- sender.sendMessage("§aFile broadcast started: §f" + fileName + " §ain §f" + world.getName());
-
- Bukkit.getScheduler().runTaskLaterAsynchronously(
- Intercom.getPlugin(Intercom.class),
- () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has ended."),
- durationMillis / 50L
- );
- });
- });
-
- return true;
- }
-
- @Override
- public String getName() {
- return "file";
- }
-}
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
new file mode 100644
index 0000000..3d7fb4c
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/FileCommand.java
@@ -0,0 +1,106 @@
+package eu.projnull.spelis.svci.commands.handlers;
+
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.builder.ArgumentBuilder;
+import de.maxhenkel.voicechat.api.VoicechatConnection;
+import de.maxhenkel.voicechat.api.VoicechatServerApi;
+import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer;
+import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel;
+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.misc.OggDecoder;
+import eu.projnull.spelis.svci.voice.BroadcasterState;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.io.File;
+import java.util.Objects;
+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"));
+
+ 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;
+ }
+
+ 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());
+ return;
+ }
+
+ 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;
+ }
+
+ VoicechatServerApi api = Objects.requireNonNull(Intercom.getPlugin(Intercom.class).getVoicechatPlugin()).getVoicechatServerApi();
+ if (api == null) {
+ sender.sendMessage("§cSimple Voice Chat API not available.");
+ return;
+ }
+
+ de.maxhenkel.voicechat.api.ServerLevel level = api.fromServerLevel(world);
+
+ 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;
+
+ StaticAudioChannel channel = api.createStaticAudioChannel(UUID.randomUUID(), level, connection);
+ if (channel == null) continue;
+
+ AudioPlayer audioPlayer = api.createAudioPlayer(channel, api.createEncoder(), pcm);
+ audioPlayer.startPlaying();
+ }
+
+ sender.sendMessage("§aFile broadcast started: §f" + filename + " §ain §f" + world.getName());
+
+ Bukkit.getScheduler().runTaskLaterAsynchronously(Intercom.getPlugin(Intercom.class), () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has ended."), durationMillis / 50L);
+ });
+ });
+
+ return 1;
+ })));
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoBroadcastHandler.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoBroadcastHandler.java
deleted file mode 100644
index 7712c39..0000000
--- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoBroadcastHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package eu.projnull.spelis.svci.commands.handlers;
-
-import eu.projnull.spelis.svci.commands.BroadcastTypeHandler;
-import eu.projnull.spelis.svci.voice.BroadcasterState;
-import org.bukkit.Bukkit;
-import org.bukkit.World;
-import org.bukkit.command.CommandSender;
-
-public class InfoBroadcastHandler implements BroadcastTypeHandler {
-
- @Override
- public boolean execute(CommandSender sender, String[] args) {
-
- if (args.length < 2) {
- sender.sendMessage("§cUsage: /intercom info <world>");
- return true;
- }
-
- World world = Bukkit.getWorld(args[1]);
- if (world == null) {
- sender.sendMessage("§cWorld not found.");
- return true;
- }
-
- BroadcasterState.Broadcaster b = BroadcasterState.inst().getBroadcast(world.getUID());
-
- if (b == null) {
- sender.sendMessage("§aNo active broadcast in this world.");
- return true;
- }
-
- String type = b.getType().name();
- String owner = b.getType() == BroadcasterState.Broadcaster.BroadcastType.LIVE ?
- Bukkit.getOfflinePlayer(b.getPlayerId()).getName() : b.getFileName();
- long secondsLeft = (b.getEndTimeMillis() - System.currentTimeMillis()) / 1000;
-
- sender.sendMessage("§eBroadcast info:");
- sender.sendMessage(" §7Type: §f" + type);
- sender.sendMessage(" §7Owner/File: §f" + owner);
- sender.sendMessage(" §7Time remaining: §f" + secondsLeft + "s");
-
- return true;
- }
-
- @Override
- public String getName() {
- return "info";
- }
-}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoCommand.java
new file mode 100644
index 0000000..a256cb8
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/InfoCommand.java
@@ -0,0 +1,48 @@
+package eu.projnull.spelis.svci.commands.handlers;
+
+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.BroadcasterState;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+
+public class InfoCommand implements Handler {
+ @Override
+ public ArgumentBuilder<CommandSourceStack, ?> buildCommand() {
+ return Commands.literal("info").requires(cs->cs.getSender().hasPermission("svcintercom.broadcast.info")).then(Commands.argument("world", StringArgumentType.word()).suggests((ctx, builder) -> {
+ Helpers.getActiveWorldsSuggestion(builder);
+ return builder.buildFuture();
+ }).executes(ctx -> {
+ CommandSourceStack source = ctx.getSource();
+ CommandSender sender = source.getSender();
+ String worldName = ctx.getArgument("world", String.class);
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ sender.sendMessage("§cWorld not found.");
+ return 0;
+ }
+
+ BroadcasterState.Broadcaster b = BroadcasterState.inst().getBroadcast(world.getUID());
+ if (b == null) {
+ sender.sendMessage(String.format("§cNo active broadcast in %s.", worldName));
+ return 0;
+ }
+
+ String type = b.getType().name();
+ String owner = b.getType() == BroadcasterState.Broadcaster.BroadcastType.LIVE ? Bukkit.getOfflinePlayer(b.getPlayerId()).getName() : b.getFileName();
+ long secondsLeft = (b.getEndTimeMillis() - System.currentTimeMillis()) / 1000;
+
+ sender.sendMessage("§eBroadcast info:");
+ sender.sendMessage(" §7Type: §f" + type);
+ sender.sendMessage(" §7Owner/File: §f" + owner);
+ sender.sendMessage(" §7Time remaining: §f" + secondsLeft + "s");
+
+ return 1;
+ }).build());
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveBroadcastHandler.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveBroadcastHandler.java
deleted file mode 100644
index 798ac66..0000000
--- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveBroadcastHandler.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package eu.projnull.spelis.svci.commands.handlers;
-
-import eu.projnull.spelis.svci.Intercom;
-import eu.projnull.spelis.svci.commands.BroadcastTypeHandler;
-import eu.projnull.spelis.svci.voice.BroadcasterState;
-import eu.projnull.spelis.svci.voice.BroadcasterState.Broadcaster;
-import eu.projnull.spelis.svci.voice.BroadcasterState.Broadcaster.BroadcastType;
-import org.bukkit.Bukkit;
-import org.bukkit.World;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-
-public class LiveBroadcastHandler implements BroadcastTypeHandler {
-
- @Override
- public boolean execute(CommandSender sender, String[] args) {
-
- if (args.length < 4) {
- sender.sendMessage("§cUsage: /intercom live <player> <world> <seconds>");
- return true;
- }
-
- Player target = Bukkit.getPlayer(args[1]);
- if (target == null) {
- sender.sendMessage("§cPlayer not found.");
- return true;
- }
-
- World world = Bukkit.getWorld(args[2]);
- if (world == null) {
- sender.sendMessage("§cWorld not found.");
- return true;
- }
-
- int seconds;
- try {
- seconds = Integer.parseInt(args[3]);
- } catch (NumberFormatException e) {
- sender.sendMessage("§cInvalid duration.");
- return true;
- }
-
- if (seconds <= 0 || seconds > 300) { // optional max
- sender.sendMessage("§cDuration must be between 1 and 300 seconds.");
- return true;
- }
-
- if (BroadcasterState.inst().isBroadcastActive(world.getUID())) {
- sender.sendMessage("§cA broadcast is already active in this world.");
- return true;
- }
-
- Broadcaster broadcaster = new Broadcaster(
- target.getUniqueId(),
- world.getUID(),
- BroadcastType.LIVE,
- seconds * 1000L,
- null
- );
-
- BroadcasterState.inst().startBroadcast(broadcaster);
- sender.sendMessage("§aLive broadcast started for " + target.getName() + " in world " + world.getName());
-
- // Schedule auto-stop
- Bukkit.getScheduler().runTaskLaterAsynchronously(
- Intercom.getPlugin(Intercom.class),
- () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has expired"),
- seconds * 20L
- );
-
- return true;
- }
-
- @Override
- public String getName() {
- return "live";
- }
-}
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
new file mode 100644
index 0000000..a220b11
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/LiveCommand.java
@@ -0,0 +1,59 @@
+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 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 io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
+import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+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);
+
+ 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.");
+ return 0;
+ }
+
+ BroadcasterState.Broadcaster broadcaster = new BroadcasterState.Broadcaster(player.getUniqueId(), world.getUID(), BroadcasterState.Broadcaster.BroadcastType.LIVE, seconds * 1000L, null);
+
+ BroadcasterState.inst().startBroadcast(broadcaster);
+ sender.sendMessage("§aLive broadcast started for " + player.getName() + " in world " + world.getName());
+
+ // Schedule auto-stop
+ Bukkit.getScheduler().runTaskLaterAsynchronously(Intercom.getPlugin(Intercom.class), () -> BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has expired"), seconds * 20L);
+
+
+ return 1;
+ }))));
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopBroadcastHandler.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopBroadcastHandler.java
deleted file mode 100644
index c806b47..0000000
--- a/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopBroadcastHandler.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package eu.projnull.spelis.svci.commands.handlers;
-
-import eu.projnull.spelis.svci.commands.BroadcastTypeHandler;
-import eu.projnull.spelis.svci.voice.BroadcasterState;
-import org.bukkit.Bukkit;
-import org.bukkit.World;
-import org.bukkit.command.CommandSender;
-
-public class StopBroadcastHandler implements BroadcastTypeHandler {
-
- @Override
- public boolean execute(CommandSender sender, String[] args) {
-
- if (args.length < 2) {
- sender.sendMessage("§cUsage: /intercom stop <world>");
- return true;
- }
-
- World world = Bukkit.getWorld(args[1]);
- if (world == null) {
- sender.sendMessage("§cWorld not found.");
- return true;
- }
-
- if (!BroadcasterState.inst().isBroadcastActive(world.getUID())) {
- sender.sendMessage("§cNo active broadcast in this world.");
- return true;
- }
-
- BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has been stopped.");
- return true;
- }
-
- @Override
- public String getName() {
- return "stop";
- }
-}
diff --git a/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopCommand.java b/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopCommand.java
new file mode 100644
index 0000000..559234f
--- /dev/null
+++ b/src/main/java/eu/projnull/spelis/svci/commands/handlers/StopCommand.java
@@ -0,0 +1,45 @@
+package eu.projnull.spelis.svci.commands.handlers;
+
+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.BroadcasterState;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import io.papermc.paper.command.brigadier.Commands;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+
+public class StopCommand implements Handler {
+ @Override
+ public ArgumentBuilder<CommandSourceStack, ?> buildCommand() {
+ return Commands.literal("stop").requires(cs->cs.getSender().hasPermission("svcintercom.broadcast.stop"))
+ .then(Commands.argument("world", StringArgumentType.word())
+ .suggests((ctx, builder) -> {
+ Helpers.getActiveWorldsSuggestion(builder);
+ return builder.buildFuture();
+ })
+ .executes(ctx -> { // <-- attach here
+ CommandSourceStack source = ctx.getSource();
+ CommandSender sender = source.getSender();
+ String worldName = StringArgumentType.getString(ctx, "world");
+
+ World world = Bukkit.getWorld(worldName);
+ if (world == null) {
+ sender.sendMessage("§cWorld not found.");
+ return 0;
+ }
+
+ if (!BroadcasterState.inst().isBroadcastActive(world.getUID())) {
+ sender.sendMessage("§cNo active broadcast in this world.");
+ return 0;
+ }
+
+ BroadcasterState.inst().stopBroadcastWithMessage(world.getUID(), "§aThe broadcast has been stopped.");
+ sender.sendMessage("§aBroadcast stopped in " + world.getName());
+ return 1;
+ })
+ );
+ }
+}
diff --git a/src/main/java/eu/projnull/spelis/svci/misc/BroadcastHudTask.java b/src/main/java/eu/projnull/spelis/svci/misc/BroadcastHudTask.java
index aecf751..017f565 100644
--- a/src/main/java/eu/projnull/spelis/svci/misc/BroadcastHudTask.java
+++ b/src/main/java/eu/projnull/spelis/svci/misc/BroadcastHudTask.java
@@ -11,7 +11,6 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;
-import java.util.UUID;
import java.util.concurrent.TimeUnit;
public final class BroadcastHudTask {
@@ -46,8 +45,8 @@ public final class BroadcastHudTask {
private Component buildMessage(Broadcaster broadcaster) {
long remaining = broadcaster.getEndTimeMillis() - System.currentTimeMillis();
- long minutes = TimeUnit.MILLISECONDS.toMinutes(remaining);
- long seconds = TimeUnit.MILLISECONDS.toSeconds(remaining) % 60;
+ long minutes = TimeUnit.MILLISECONDS.toMinutes(remaining);
+ long seconds = TimeUnit.MILLISECONDS.toSeconds(remaining) % 60;
String timer = String.format("%d:%02d remaining", minutes, seconds);
diff --git a/src/main/java/eu/projnull/spelis/svci/misc/OggDecoder.java b/src/main/java/eu/projnull/spelis/svci/misc/OggDecoder.java
index 6ea5b76..07db0cb 100644
--- a/src/main/java/eu/projnull/spelis/svci/misc/OggDecoder.java
+++ b/src/main/java/eu/projnull/spelis/svci/misc/OggDecoder.java
@@ -1,30 +1,23 @@
package eu.projnull.spelis.svci.misc;
-import javax.sound.sampled.*;
-import java.io.File;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
import java.io.ByteArrayOutputStream;
+import java.io.File;
public final class OggDecoder {
- private OggDecoder() {}
+ private OggDecoder() {
+ }
public static short[] decode(File file) throws Exception {
try (AudioInputStream raw = AudioSystem.getAudioInputStream(file)) {
AudioFormat source = raw.getFormat();
- AudioFormat target = new AudioFormat(
- AudioFormat.Encoding.PCM_SIGNED,
- source.getSampleRate(),
- 16,
- source.getChannels(),
- source.getChannels() * 2,
- source.getSampleRate(),
- false
- );
+ AudioFormat target = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, source.getSampleRate(), 16, source.getChannels(), source.getChannels() * 2, source.getSampleRate(), false);
- try (AudioInputStream pcm =
- AudioSystem.getAudioInputStream(target, raw);
- ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
+ try (AudioInputStream pcm = AudioSystem.getAudioInputStream(target, raw); ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
byte[] data = new byte[4096];
int read;
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 2512af4..5aafe8a 100644
--- a/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java
+++ b/src/main/java/eu/projnull/spelis/svci/voice/BroadcasterState.java
@@ -4,9 +4,11 @@ import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
-import java.util.UUID;
import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
public final class BroadcasterState {
@@ -15,17 +17,18 @@ public final class BroadcasterState {
// Key = worldId
private final Map<UUID, Broadcaster> broadcasts = new ConcurrentHashMap<>();
- private BroadcasterState() {}
+ private BroadcasterState() {
+ }
public static BroadcasterState inst() {
return INSTANCE;
}
- public boolean startBroadcast(Broadcaster broadcaster) {
+ public void startBroadcast(Broadcaster broadcaster) {
UUID worldId = broadcaster.getWorldId();
// only allow one broadcast per world
- return broadcasts.putIfAbsent(worldId, broadcaster) == null;
+ broadcasts.putIfAbsent(worldId, broadcaster);
}
public Broadcaster getBroadcast(UUID worldId) {
@@ -41,6 +44,16 @@ public final class BroadcasterState {
return b;
}
+ public Stream<String> activeBroadcastWorldNames() {
+ long now = System.currentTimeMillis();
+
+ return broadcasts.values().stream()
+ .filter(b -> b.getEndTimeMillis() > now)
+ .map(b -> Bukkit.getWorld(b.getWorldId()))
+ .filter(Objects::nonNull)
+ .map(World::getName);
+ }
+
public void stopBroadcastWithMessage(UUID worldId, String message) {
Broadcaster b = broadcasts.remove(worldId);
if (b == null) return;
diff --git a/src/main/java/eu/projnull/spelis/svci/voice/FileBroadcast.java b/src/main/java/eu/projnull/spelis/svci/voice/FileBroadcast.java
deleted file mode 100644
index 07cb5f5..0000000
--- a/src/main/java/eu/projnull/spelis/svci/voice/FileBroadcast.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package eu.projnull.spelis.svci.voice;
-
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer;
-import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel;
-import de.maxhenkel.voicechat.api.VoicechatConnection;
-import eu.projnull.spelis.svci.misc.OggDecoder;
-import org.bukkit.World;
-import org.bukkit.entity.Player;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-public class FileBroadcast {
-
- private final List<AudioPlayer> players = new ArrayList<>();
-
- /**
- * Starts playing an audio file to all players currently in the world.
- * New players joining mid-broadcast won't hear it (acceptable for now).
- *
- * @param api the SVC server API instance
- * @param world the target world
- * @param audioFile the .ogg file to play
- * @param onDone called when all players' audio finishes
- */
- public void start(VoicechatServerApi api, World world, File audioFile, Runnable onDone) throws Exception {
- short[] pcm = OggDecoder.decode(audioFile); // see below
-
- for (Player player : world.getPlayers()) {
- VoicechatConnection connection = api.getConnectionOf(player.getUniqueId());
- if (connection == null) continue; // player doesn't have SVC mod
-
- StaticAudioChannel channel = api.createStaticAudioChannel(
- UUID.randomUUID(),
- api.fromServerLevel(/* ServerLevel */ null), // explained below
- connection
- );
- if (channel == null) continue;
-
- AudioPlayer ap = api.createAudioPlayer(channel, api.createEncoder(), pcm);
- ap.setOnStopped(onDone);
- ap.startPlaying();
- players.add(ap);
- }
- }
-
- public void stop() {
- for (AudioPlayer ap : players) {
- ap.stopPlaying();
- }
- players.clear();
- }
-}
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 ba11792..55b19ca 100644
--- a/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java
+++ b/src/main/java/eu/projnull/spelis/svci/voice/VoicePlugin.java
@@ -8,15 +8,11 @@ import de.maxhenkel.voicechat.api.events.EventRegistration;
import de.maxhenkel.voicechat.api.events.MicrophonePacketEvent;
import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent;
import eu.projnull.spelis.svci.Intercom;
-import org.bukkit.permissions.Permission;
-import org.bukkit.permissions.PermissionDefault;
import java.util.UUID;
public class VoicePlugin implements VoicechatPlugin {
-
- public static Permission BROADCAST_PERMISSION = new Permission("svcintercom.broadcast", PermissionDefault.OP);
private VoicechatServerApi voicechatServerApi;
public VoicechatServerApi getVoicechatServerApi() {
@@ -36,9 +32,7 @@ public class VoicePlugin implements VoicechatPlugin {
@Override
public void registerEvents(EventRegistration registration) {
registration.registerEvent(MicrophonePacketEvent.class, this::onMicPacket);
- registration.registerEvent(VoicechatServerStartedEvent.class, e->{
- this.voicechatServerApi = e.getVoicechat();
- });
+ registration.registerEvent(VoicechatServerStartedEvent.class, e -> this.voicechatServerApi = e.getVoicechat());
}
private void onMicPacket(MicrophonePacketEvent event) {
@@ -53,16 +47,13 @@ public class VoicePlugin implements VoicechatPlugin {
UUID worldId = player.getWorld().getUID();
- BroadcasterState.Broadcaster broadcaster =
- BroadcasterState.inst().getBroadcast(worldId);
+ BroadcasterState.Broadcaster broadcaster = BroadcasterState.inst().getBroadcast(worldId);
if (broadcaster == null) return;
- if (broadcaster.getType() != BroadcasterState.Broadcaster.BroadcastType.LIVE)
- return;
+ if (broadcaster.getType() != BroadcasterState.Broadcaster.BroadcastType.LIVE) return;
- if (!player.getUniqueId().equals(broadcaster.getPlayerId()))
- return;
+ if (!player.getUniqueId().equals(broadcaster.getPlayerId())) return;
event.cancel();
@@ -73,15 +64,11 @@ public class VoicePlugin implements VoicechatPlugin {
continue;
}
- VoicechatConnection connection =
- api.getConnectionOf(online.getUniqueId());
+ VoicechatConnection connection = api.getConnectionOf(online.getUniqueId());
if (connection == null) continue;
- api.sendStaticSoundPacketTo(
- connection,
- event.getPacket().toStaticSoundPacket()
- );
+ api.sendStaticSoundPacketTo(connection, event.getPacket().toStaticSoundPacket());
}
}
}