From 9df88c561aed19ff269cef0d69e9da3e6303ac70 Mon Sep 17 00:00:00 2001 From: Johny Muffin Date: Thu, 8 Feb 2024 18:06:08 +1000 Subject: [PATCH 1/6] Change PublicChatMessage to use the same way as Poseidon --- .../java/dev/oxs/staffchat/StaffChat.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/java/dev/oxs/staffchat/StaffChat.java b/src/main/java/dev/oxs/staffchat/StaffChat.java index 52aaeb1..e4e1986 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; @@ -68,13 +70,28 @@ public void StaffChatMessage(Player sender, String message) { } 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"); +// 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); } } From 82e4152145f62244604a4a92e321541ae1ad3a11 Mon Sep 17 00:00:00 2001 From: Rhys B Date: Sun, 30 Nov 2025 17:32:03 +1000 Subject: [PATCH 2/6] Add Maven stuff --- .github/workflows/build-and-test.yaml | 41 +++++++++++++++ .github/workflows/release.yml | 45 ++++++++++++++++ .gitignore | 4 ++ pom.xml | 74 +++++++++++++++++++++++++++ src/main/resources/plugin.yml | 6 +-- 5 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/build-and-test.yaml create mode 100644 .github/workflows/release.yml create mode 100644 pom.xml 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..e1a18b0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + dev.oxs.staffchat + StaffChat + 2.0.0-SNAPSHOT + 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 + + + + + + + + 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 + + + + + \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5122f7e..fea9c9b 100755 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ -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: [ ] From c4d14cf7fc7fc9e1af18b3a391d6f22906a484fa Mon Sep 17 00:00:00 2001 From: Garsooon Date: Thu, 4 Jun 2026 22:58:02 +0900 Subject: [PATCH 3/6] Add discord bridging --- pom.xml | 24 ++++++++++++++++++ .../java/dev/oxs/staffchat/StaffChat.java | 25 +++++++++++++++++-- .../dev/oxs/staffchat/StaffChatSettings.java | 4 +++ src/main/resources/plugin.yml | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e1a18b0..d2cff5d 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,10 @@ true + + dv8tion + https://m2.dv8tion.net/releases + @@ -69,6 +73,26 @@ 1.1.8 + + com.johnymuffin + discordcore-4 + 1.0-SNAPSHOT + 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/StaffChat.java b/src/main/java/dev/oxs/staffchat/StaffChat.java index e4e1986..7428883 100755 --- a/src/main/java/dev/oxs/staffchat/StaffChat.java +++ b/src/main/java/dev/oxs/staffchat/StaffChat.java @@ -28,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; @@ -51,22 +54,40 @@ 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()) { + onlinePlayer.sendMessage(formatted); + } + } + if (discordBridge != null) discordBridge.sendToDiscord(name, 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()) { - 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); } } + log.info("[StaffChat][Discord] " + discordUsername + ": " + message); } public void PublicChatMessage(Player sender, String message) { 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 fea9c9b..ff99655 100755 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,6 +5,7 @@ version: ${project.version} authors: - oxs depend: [ ] +softdepend: [ DiscordCore ] commands: StaffChatCommand: From 5d4a00cf0e993880a849188a42563de03d3c99ac Mon Sep 17 00:00:00 2001 From: Garsooon Date: Thu, 4 Jun 2026 22:59:55 +0900 Subject: [PATCH 4/6] Bump version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2cff5d..0143328 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.oxs.staffchat StaffChat - 2.0.0-SNAPSHOT + 2.0.1 A simple staff chat plugin for Poseidon Minecraft servers. From cb36df6b90738f53de3ac72cc467fa7947ea4ecb Mon Sep 17 00:00:00 2001 From: Garsooon Date: Thu, 4 Jun 2026 23:08:46 +0900 Subject: [PATCH 5/6] Update Discord Core dependancy --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0143328..7853bcf 100644 --- a/pom.xml +++ b/pom.xml @@ -75,8 +75,8 @@ com.johnymuffin - discordcore-4 - 1.0-SNAPSHOT + discordcore + 1.1.3 provided From 0a2f627dc3447c19ff5443e36444741ac5752bf6 Mon Sep 17 00:00:00 2001 From: Garsooon Date: Thu, 4 Jun 2026 23:10:22 +0900 Subject: [PATCH 6/6] Add DiscordBridge to wire StaffChat into DiscordCore-4 Registers a JDA listener via DiscordCore's bot to relay Discord messages into in-game staff chat, and forwards staff chat messages to the configured Discord channel. --- .../java/dev/oxs/staffchat/DiscordBridge.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/main/java/dev/oxs/staffchat/DiscordBridge.java 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); + } +}