diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml
new file mode 100644
index 0000000..d60b654
--- /dev/null
+++ b/.github/workflows/build-and-test.yaml
@@ -0,0 +1,41 @@
+name: build-and-test
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - reopened
+ push:
+ branches:
+ - master
+ - main
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: 8
+
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v4.5
+ with:
+ maven-version: 3.9.1
+
+ - name: build application
+ shell: bash
+ run: |
+ mvn clean install
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ github.event.repository.name }}-artifact
+ path: target/*.jar
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..cbdb0d7
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,45 @@
+name: Release Workflow
+
+on:
+ push:
+ branches:
+ - main
+ - master
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin'
+ java-version: 8
+
+ - name: Get the version from pom.xml
+ id: get_version
+ run: echo "PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
+
+ - name: Fail if snapshot version
+ run: |
+ if [[ $PROJECT_VERSION == *"-SNAPSHOT"* ]]; then
+ echo "Snapshot versions are not releasable"
+ exit 0
+ fi
+
+ - name: Build with Maven
+ run: mvn clean package
+
+ - name: Create GitHub Release
+ if: ${{ !endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ env.PROJECT_VERSION }}
+ files: |
+ target/*.jar
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 524f096..20eae6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,7 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
+
+*.iml
+
+.idea/
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..7853bcf
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,98 @@
+
+
+ 4.0.0
+
+ dev.oxs.staffchat
+ StaffChat
+ 2.0.1
+ A simple staff chat plugin for Poseidon Minecraft servers.
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ johnymuffin-nexus-releases
+ https://repository.johnymuffin.com/repository/maven-public/
+
+ true
+
+
+ false
+
+
+
+
+ johnymuffin-nexus-snapshots
+ https://repository.johnymuffin.com/repository/maven-snapshots/
+
+ false
+
+
+ true
+
+
+
+ dv8tion
+ https://m2.dv8tion.net/releases
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+
+
+
+ com.legacyminecraft.poseidon
+ poseidon-craftbukkit
+ 1.1.8
+
+
+
+ com.johnymuffin
+ discordcore
+ 1.1.3
+ provided
+
+
+
+ net.dv8tion
+ JDA
+ 4.4.0_350
+ provided
+
+
+ club.minnced
+ opus-java
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/dev/oxs/staffchat/DiscordBridge.java b/src/main/java/dev/oxs/staffchat/DiscordBridge.java
new file mode 100644
index 0000000..260458b
--- /dev/null
+++ b/src/main/java/dev/oxs/staffchat/DiscordBridge.java
@@ -0,0 +1,100 @@
+package dev.oxs.staffchat;
+
+import com.johnymuffin.discordcore.DiscordCore;
+import com.johnymuffin.discordcore.DiscordBot;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import org.bukkit.Bukkit;
+
+import java.util.logging.Logger;
+
+/**
+ * Bridges StaffChat ↔ Discord via DiscordCore-4.
+ *
+ * The JDA listener anonymous class (DiscordBridge$1) is a separate .class file
+ * compiled by javac. It is only loaded by the JVM when start() executes the
+ * "new ListenerAdapter(){}" expression — by which point DiscordCore (and its
+ * shaded JDA) are guaranteed to be in the plugin classloader graph.
+ *
+ * DiscordBridge itself has NO JDA references in its own class definition, so
+ * it loads cleanly even if DiscordCore is absent.
+ */
+public class DiscordBridge {
+
+ private final StaffChat plugin;
+ private final Logger log;
+ private DiscordBot discordBot;
+ private String channelId;
+ private Object jdaListener; // typed as Object so this class has no JDA dependency
+
+ public DiscordBridge(StaffChat plugin) {
+ this.plugin = plugin;
+ this.log = plugin.getServer().getLogger();
+ }
+
+ public void start() {
+ if (!plugin.getConfig().getConfigBoolean("discord.enabled")) return;
+
+ channelId = plugin.getConfig().getConfigString("discord.channel-id");
+ if (channelId == null || channelId.isEmpty() || channelId.equals("none")) {
+ log.warning("[StaffChat] discord.enabled is true but discord.channel-id is not set.");
+ return;
+ }
+
+ org.bukkit.plugin.Plugin dc = Bukkit.getPluginManager().getPlugin("DiscordCore");
+ if (dc == null || !dc.isEnabled()) {
+ log.warning("[StaffChat] DiscordCore not found — Discord bridge disabled.");
+ return;
+ }
+
+ try {
+ discordBot = ((DiscordCore) dc).getDiscordBot();
+ registerJdaListener();
+ log.info("[StaffChat] Discord bridge active on channel " + channelId);
+ } catch (Exception | NoClassDefFoundError e) {
+ log.severe("[StaffChat] Failed to attach JDA listener: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Separated into its own method so the anonymous ListenerAdapter subclass
+ * (which lives in DiscordBridge$1.class) is only resolved when this method
+ * is called, not when DiscordBridge.class is first loaded.
+ */
+ private void registerJdaListener() {
+ final String targetChannel = this.channelId;
+ ListenerAdapter listener = new ListenerAdapter() {
+ @Override
+ public void onMessageReceived(MessageReceivedEvent event) {
+ if (!event.getChannel().getId().equals(targetChannel)) return;
+ if (event.getAuthor().isBot() || event.isWebhookMessage()) return;
+
+ final String username = event.getAuthor().getName();
+ final String message = event.getMessage().getContentRaw();
+
+ Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
+ @Override
+ public void run() {
+ plugin.staffChatMessageFromDiscord(username, message);
+ }
+ });
+ }
+ };
+ discordBot.getJda().addEventListener(listener);
+ jdaListener = listener;
+ }
+
+ public void stop() {
+ if (discordBot != null && jdaListener != null) {
+ try {
+ discordBot.getJda().removeEventListener(jdaListener);
+ } catch (Exception | NoClassDefFoundError ignored) {}
+ }
+ }
+
+ /** Forward an in-game staff chat message to the Discord channel. */
+ public void sendToDiscord(String playerName, String message) {
+ if (discordBot == null) return;
+ discordBot.discordSendToChannel(channelId, "**[StaffChat]** " + playerName + ": " + message);
+ }
+}
diff --git a/src/main/java/dev/oxs/staffchat/StaffChat.java b/src/main/java/dev/oxs/staffchat/StaffChat.java
index 52aaeb1..7428883 100755
--- a/src/main/java/dev/oxs/staffchat/StaffChat.java
+++ b/src/main/java/dev/oxs/staffchat/StaffChat.java
@@ -3,8 +3,10 @@
import dev.oxs.staffchat.commands.*;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
+import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
@@ -26,6 +28,9 @@ public class StaffChat extends JavaPlugin implements Listener {
private HashMap staffChatToggled = new HashMap<>();
+ private DiscordBridge discordBridge;
+ //TODO upgrade to Discord Core 5 when support is EOL
+
@Override
public void onEnable() {
plugin = this;
@@ -49,32 +54,65 @@ public void onEnable() {
pluginName = pdf.getName();
+ discordBridge = new DiscordBridge(plugin);
+ discordBridge.start();
+
log.info("[" + pluginName + "] Is Loading, Version: " + pdf.getVersion());
}
@Override
public void onDisable() {
+ if (discordBridge != null) discordBridge.stop();
log.info(pluginName + " has been disabled.");
}
public void StaffChatMessage(Player sender, String message) {
+ String name = StaffChatSettings.getInstance(plugin).getConfigBoolean("settings.staffchat-use-displayNamesStaffChat")
+ ? sender.getDisplayName() : sender.getName();
+ String formatted = plugin.getPluginPrefix() + " " + ChatColor.WHITE + name + ": " + ChatColor.WHITE + printColours(message);
for (Player onlinePlayer : getServer().getOnlinePlayers()) {
if (onlinePlayer.hasPermission("staffchat.see") || onlinePlayer.isOp()) {
- Boolean useDisplayName = StaffChatSettings.getInstance(plugin).getConfigBoolean("settings.staffchat-use-displayNamesStaffChat");
- onlinePlayer.sendMessage(plugin.getPluginPrefix() + " " + ChatColor.WHITE + (useDisplayName ? sender.getDisplayName() : sender.getName()) + ": " + ChatColor.WHITE + printColours(message));
+ onlinePlayer.sendMessage(formatted);
}
}
+ if (discordBridge != null) discordBridge.sendToDiscord(name, message);
}
- public void PublicChatMessage(Player sender, String message) {
+ public void staffChatMessageFromDiscord(String discordUsername, String message) {
+ String discordPrefix = printColours(StaffChatSettings.getInstance(plugin).getConfigString("discord.discord-prefix"));
+ String formatted = discordPrefix + " " + ChatColor.WHITE + discordUsername + ": " + ChatColor.WHITE + printColours(message);
for (Player onlinePlayer : getServer().getOnlinePlayers()) {
+ if (onlinePlayer.hasPermission("staffchat.see") || onlinePlayer.isOp()) {
+ onlinePlayer.sendMessage(formatted);
+ }
+ }
+ log.info("[StaffChat][Discord] " + discordUsername + ": " + message);
+ }
- String playerPrefix = StaffChatSettings.getInstance(plugin).getConfigString("settings.staffchat-publicChatPrefix");
- Boolean useDisplay = StaffChatSettings.getInstance(plugin).getConfigBoolean("settings.staffchat-use-displayNamesPublicChat");
+ public void PublicChatMessage(Player sender, String message) {
+// for (Player onlinePlayer : getServer().getOnlinePlayers()) {
+//
+// String playerPrefix = StaffChatSettings.getInstance(plugin).getConfigString("settings.staffchat-publicChatPrefix");
+// Boolean useDisplay = StaffChatSettings.getInstance(plugin).getConfigBoolean("settings.staffchat-use-displayNamesPublicChat");
+//
+// String replacedString = playerPrefix.replace("%player%", (useDisplay ? sender.getDisplayName() : sender.getName()));
+// onlinePlayer.sendMessage(plugin.printColours(replacedString) + ChatColor.WHITE + plugin.printColours(message));
+// }
+
+ // Send public chat message the same way as the server does
+
+ PlayerChatEvent event = new PlayerChatEvent(sender, message);
+ Bukkit.getPluginManager().callEvent(event);
+
+ if(event.isCancelled()) {
+ return;
+ }
- String replacedString = playerPrefix.replace("%player%", (useDisplay ? sender.getDisplayName() : sender.getName()));
- onlinePlayer.sendMessage(plugin.printColours(replacedString) + ChatColor.WHITE + plugin.printColours(message));
+ String formattedMessage = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage());
+ ((CraftServer) Bukkit.getServer()).getServer().console.sendMessage(formattedMessage); // Log to console
+ for (Player recipient : event.getRecipients()) {
+ recipient.sendMessage(formattedMessage);
}
}
diff --git a/src/main/java/dev/oxs/staffchat/StaffChatSettings.java b/src/main/java/dev/oxs/staffchat/StaffChatSettings.java
index 5e13b3c..02b25fc 100644
--- a/src/main/java/dev/oxs/staffchat/StaffChatSettings.java
+++ b/src/main/java/dev/oxs/staffchat/StaffChatSettings.java
@@ -23,6 +23,10 @@ private void write() {
generateConfigOption("settings.staffchat-use-displayNamesStaffChat", false);
generateConfigOption("settings.staffchat-use-displayNamesPublicChat", true);
generateConfigOption("settings.staffchat-publicChatPrefix", "&f<%player%&f> ");
+ //Discord Bridge (requires DiscordCore-4)
+ generateConfigOption("discord.enabled", false);
+ generateConfigOption("discord.channel-id", "none");
+ generateConfigOption("discord.discord-prefix", "&d[&9D&dStaffChat]&f");
}
public void generateConfigOption(String key, Object defaultValue) {
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 5122f7e..ff99655 100755
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,10 +1,11 @@
-name: StaffChat
-description: Staff Chat beta plugin
+name: ${project.name}
+description: ${project.description}
main: dev.oxs.staffchat.StaffChat
-version: 2.0.0
+version: ${project.version}
authors:
- oxs
depend: [ ]
+softdepend: [ DiscordCore ]
commands:
StaffChatCommand: