├── .editorconfig
├── .github
├── dependabot.yml
└── workflows
│ └── maven.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bukkit
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── github
│ │ └── games647
│ │ └── commandforward
│ │ └── bukkit
│ │ ├── CommandForwardBukkit.java
│ │ ├── Permissions.java
│ │ └── command
│ │ ├── ForwardCommand.java
│ │ ├── InterceptCommand.java
│ │ └── MessageCommand.java
│ └── resources
│ └── plugin.yml
├── bungee
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── github
│ │ └── games647
│ │ └── commandforward
│ │ └── bungee
│ │ └── CommandForwardBungee.java
│ └── resources
│ └── bungee.yml
├── pom.xml
├── universal
└── pom.xml
└── velocity
├── pom.xml
└── src
└── main
└── java
└── com
└── github
└── games647
└── commandforward
└── velocity
├── CommandForwardVelocity.java
└── MessageListener.java
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = space
3 | indent_size = 4
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: maven
4 | directory: "/"
5 | schedule:
6 | interval: weekly
7 | open-pull-requests-limit: 10
8 | ignore:
9 | - dependency-name: net.md-5:bungeecord-api
10 | versions:
11 | - "> 1.16-R0.2"
12 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # Human-readable name
2 | name: Java CI
3 |
4 | # Build on every push and pull request regardless of the branch
5 | # Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
6 | on:
7 | - push
8 | - pull_request
9 |
10 | jobs:
11 | # job id
12 | build_and_test:
13 |
14 | # Environment image - always use latest OS
15 | runs-on: ubuntu-latest
16 |
17 | # Run steps
18 | steps:
19 | # Pull changes
20 | - uses: actions/checkout@v2.3.4
21 | # Cache artifacts - however this has the downside that we don't get notified of
22 | # artifact resolution failures like invalid repository
23 | # Nevertheless the repositories should be more stable, and it makes no sense to pull
24 | # a same version every time
25 | # A dry run would make more sense
26 | - uses: actions/cache@v2.1.5
27 | with:
28 | path: ~/.m2/repository
29 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
30 | restore-keys: |
31 | ${{ runner.os }}-maven-
32 | # Setup Java
33 | - name: Set up JDK
34 | uses: actions/setup-java@v2
35 | with:
36 | distribution: 'adopt'
37 | # Use Java 8, because it's minimum required version
38 | java-version: 8
39 | # Build and test (included in package)
40 | - name: Build with Maven and test
41 | # Run non-interactive, package (with compile+test),
42 | # ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate possible
43 | # errors in dependencies
44 | run: mvn package --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Eclipse
2 | .classpath
3 | .project
4 | .settings/
5 |
6 | # NetBeans
7 | nbproject/
8 | nb-configuration.xml
9 |
10 | # IntelliJ
11 | *.iml
12 | *.ipr
13 | *.iws
14 | .idea/
15 |
16 | # Maven
17 | target/
18 | pom.xml.versionsBackup
19 |
20 | # Gradle
21 | .gradle
22 |
23 | # Ignore Gradle GUI config
24 | gradle-app.setting
25 |
26 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
27 | !gradle-wrapper.jar
28 |
29 | # various other potential build files
30 | build/
31 | bin/
32 | dist/
33 | manifest.mf
34 | *.log
35 |
36 | # Vim
37 | .*.sw[a-p]
38 |
39 | # virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
40 | hs_err_pid*
41 |
42 | # Mac filesystem dust
43 | .DS_Store
44 |
45 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | #Changelog
2 |
3 | ## 0.3
4 |
5 | * Add op permissions forwarding (Fixes #1)
6 |
7 | ## 0.2
8 |
9 | * Fix child reference to parent pom
10 |
11 | ## 0.1
12 |
13 | * First release
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016-2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CommandForward
2 |
3 | ## Description
4 |
5 | This lightweight plugin to forward commands from the Bukkit-side to a Bungeecord server. It uses plugin-channels to
6 | communicate between the server instances. This part of a server->proxy->player connection and requires at least one
7 | player to be online. Therefore, we don't need to open a port and establish a new connection between the servers.
8 | Furthermore, on a RedisBungee environment it forwards the command only to currently used proxy by that specified player.
9 |
10 | I'm open for suggestions.
11 |
12 | If you like the project, leave a star on GitHub and contribute there.
13 |
14 | ## Features
15 | * Lightweight
16 | * Easy to use
17 | * RedisBungee support (executes command only on the proxy of that player)
18 | * Forwarding as Player or Console
19 |
20 | ## Warning
21 | * This plugin cannot forward commands to a Bungeecord server if no player is online
22 |
23 | ## How to use
24 |
25 | ### Setup
26 |
27 | * Drop the plugin in the Bungee and Bukkit server
28 | * Finished setup
29 |
30 | ## Permissions
31 |
32 | commandforward.bukkit.command.forward.* - Allow to use all features of forward command
33 | commandforward.bukkit.command.forward - Allow to use base forward command
34 | commandforward.bukkit.command.forward.console - Allow to use console in forward command
35 | commandforward.bukkit.command.forward.other - Allow to use another player than themselves in forward command
36 |
37 | ### Using it
38 |
39 | #### Execute this command on the Bukkit side
40 | `/forward bridgePlayer cmd [args...]`
41 |
42 | bridgePlayer is the player which connection should be used.
43 | This is relevant for receiving the output or selecting the correct proxy in a RedisBungee environment.
44 |
45 | cmd is the start of the actual command `[]` means it's optional
46 |
47 | ##### Example Execute as Self:
48 | `/intercept ping`
49 |
50 | This will select the command invoker for forwarding the data or random player if the invoker
51 | is the console.
52 |
53 | ##### Example Execute as Player:
54 | `/forward playerName ping`
55 |
56 | ##### Example Execute as Console:
57 | `/forward console ping`
58 |
59 | This will select a random player to forward the connection to the Bungee server, but it will be executed as Bungee
60 | console there.
61 |
--------------------------------------------------------------------------------
/bukkit/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | commandforward
8 | 0.5.0
9 | ../pom.xml
10 |
11 |
12 |
13 | commandforward.bukkit
14 | jar
15 |
16 | CommandForwardBukkit
17 |
18 |
19 |
20 |
21 | spigot-repo
22 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/
23 |
24 |
25 |
26 |
27 |
28 |
29 | org.spigotmc
30 | spigot-api
31 | 1.19-R0.1-SNAPSHOT
32 | provided
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/bukkit/src/main/java/com/github/games647/commandforward/bukkit/CommandForwardBukkit.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bukkit;
2 |
3 | import com.github.games647.commandforward.bukkit.command.ForwardCommand;
4 | import com.github.games647.commandforward.bukkit.command.InterceptCommand;
5 |
6 | import java.util.Optional;
7 |
8 | import org.bukkit.ChatColor;
9 | import org.bukkit.command.CommandExecutor;
10 | import org.bukkit.plugin.java.JavaPlugin;
11 |
12 | public class CommandForwardBukkit extends JavaPlugin {
13 |
14 | private static final String MESSAGE_CHANNEL = "commandforward:cmd";
15 |
16 | private final String prefix = ChatColor.DARK_AQUA + "["
17 | + ChatColor.GOLD + this.getName()
18 | + ChatColor.DARK_AQUA + "] ";
19 |
20 | @Override
21 | public void onEnable() {
22 | getServer().getMessenger().registerOutgoingPluginChannel(this, MESSAGE_CHANNEL);
23 |
24 | CommandExecutor forwardCommand = new ForwardCommand(this, MESSAGE_CHANNEL, prefix);
25 | Optional.ofNullable(getCommand("forward")).ifPresent(cmd -> cmd.setExecutor(forwardCommand));
26 |
27 | CommandExecutor interceptCommand = new InterceptCommand(this, MESSAGE_CHANNEL, prefix);
28 | Optional.ofNullable(getCommand("intercept")).ifPresent(cmd -> cmd.setExecutor(interceptCommand));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/bukkit/src/main/java/com/github/games647/commandforward/bukkit/Permissions.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bukkit;
2 |
3 | import org.bukkit.entity.Player;
4 | import org.bukkit.permissions.Permissible;
5 |
6 | public enum Permissions {
7 | FORWARD("commandforward.bukkit.command.forward"),
8 | FORWARD_CONSOLE("commandforward.bukkit.command.forward.console"),
9 | FORWARD_OTHER("commandforward.bukkit.command.forward.other");
10 |
11 | private final String permission;
12 | public static final String ERROR_MESSAGE = "You are not allowed to execute this command";
13 |
14 | Permissions(String perm) {
15 | this.permission = perm;
16 | }
17 |
18 | /**
19 | * Define if the permission is set
20 | *
21 | * @param player Player on which check the permissions
22 | * @return Return a boolean to define if the permission is set
23 | */
24 | public boolean isSetOn(Permissible player) {
25 | return player != null && (!(player instanceof Player) || player.hasPermission(this.permission));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/bukkit/src/main/java/com/github/games647/commandforward/bukkit/command/ForwardCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bukkit.command;
2 |
3 | import com.github.games647.commandforward.bukkit.CommandForwardBukkit;
4 | import com.github.games647.commandforward.bukkit.Permissions;
5 |
6 | import org.bukkit.Bukkit;
7 | import org.bukkit.command.Command;
8 | import org.bukkit.command.CommandSender;
9 | import org.bukkit.entity.Player;
10 |
11 | public class ForwardCommand extends MessageCommand {
12 |
13 | // it starts with the player name, followed by the command and then the arg starts
14 | private final int ARG_START = 2;
15 |
16 | public ForwardCommand(CommandForwardBukkit plugin, String channel, String messagePrefix) {
17 | super(plugin, channel, messagePrefix);
18 | }
19 |
20 | @Override
21 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
22 | if (args.length <= 1) {
23 | sendErrorMessage(sender, "Wrong command, Missing arguments");
24 | return false;
25 | }
26 |
27 | String channelPlayer = args[0];
28 |
29 | if ("Console".equalsIgnoreCase(channelPlayer)) {
30 | if (!Permissions.FORWARD_CONSOLE.isSetOn(sender)) {
31 | sendErrorMessage(sender, Permissions.ERROR_MESSAGE);
32 | return true;
33 | }
34 |
35 | Bukkit.getOnlinePlayers().stream().findAny().ifPresent(messageSender -> {
36 | sendForwardCommand(messageSender, false, args[1], dropFirstArgs(args, ARG_START), sender.isOp());
37 | });
38 | } else if ("*".equalsIgnoreCase(channelPlayer)) {
39 | if (!Permissions.FORWARD_OTHER.isSetOn(sender)) {
40 | sendErrorMessage(sender, Permissions.ERROR_MESSAGE);
41 | return true;
42 | }
43 |
44 | for (Player player : Bukkit.getOnlinePlayers()) {
45 | sendForwardCommand(player, true, args[1], dropFirstArgs(args, ARG_START), sender.isOp());
46 | }
47 | } else {
48 | if (sender instanceof Player && !sender.getName().equalsIgnoreCase(channelPlayer)
49 | && !Permissions.FORWARD_OTHER.isSetOn(sender)) {
50 | sendErrorMessage(sender, Permissions.ERROR_MESSAGE);
51 | return true;
52 | }
53 |
54 | if (Bukkit.getServer().getPlayer(channelPlayer) == null) {
55 | sendErrorMessage(sender, "Player '" + channelPlayer + "' not found");
56 | return true;
57 | }
58 |
59 | Player messageSender = Bukkit.getServer().getPlayer(channelPlayer);
60 | if (messageSender != null) {
61 | sendForwardCommand(messageSender, true, args[1], dropFirstArgs(args, ARG_START), sender.isOp());
62 | }
63 | }
64 |
65 | return true;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/bukkit/src/main/java/com/github/games647/commandforward/bukkit/command/InterceptCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bukkit.command;
2 |
3 | import com.github.games647.commandforward.bukkit.CommandForwardBukkit;
4 |
5 | import org.bukkit.Bukkit;
6 | import org.bukkit.command.Command;
7 | import org.bukkit.command.CommandSender;
8 | import org.bukkit.entity.Player;
9 |
10 | public class InterceptCommand extends MessageCommand {
11 |
12 | // arguments starts after the command
13 | private static final int ARG_START = 1;
14 |
15 | public InterceptCommand(CommandForwardBukkit plugin, String messageChannel, String prefix) {
16 | super(plugin, messageChannel, prefix);
17 | }
18 |
19 | @Override
20 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
21 | if (args.length <= 1) {
22 | sendErrorMessage(sender, "Missing command to invoke");
23 | return false;
24 | }
25 |
26 | if (sender instanceof Player) {
27 | Player messageSender = (Player) sender;
28 | sendForwardCommand(messageSender, true, args[0], dropFirstArgs(args, ARG_START), sender.isOp());
29 | return true;
30 | }
31 |
32 | Bukkit.getOnlinePlayers().stream().findAny().ifPresent(messageSender -> {
33 | sendForwardCommand(messageSender, false, args[0], dropFirstArgs(args, ARG_START), sender.isOp());
34 | });
35 |
36 | return true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/bukkit/src/main/java/com/github/games647/commandforward/bukkit/command/MessageCommand.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bukkit.command;
2 |
3 | import com.github.games647.commandforward.bukkit.CommandForwardBukkit;
4 | import com.google.common.io.ByteArrayDataOutput;
5 | import com.google.common.io.ByteStreams;
6 | import org.bukkit.ChatColor;
7 | import org.bukkit.command.CommandExecutor;
8 | import org.bukkit.command.CommandSender;
9 | import org.bukkit.plugin.messaging.PluginMessageRecipient;
10 |
11 | import java.util.Arrays;
12 |
13 | public abstract class MessageCommand implements CommandExecutor {
14 |
15 | protected final CommandForwardBukkit plugin;
16 | protected final String channel;
17 | protected final String messagePrefix;
18 |
19 | public MessageCommand(CommandForwardBukkit plugin, String channel, String messagePrefix) {
20 | this.plugin = plugin;
21 | this.channel = channel;
22 | this.messagePrefix = messagePrefix;
23 | }
24 |
25 | /**
26 | * Print an error message
27 | *
28 | * @param sender Sender that execute the current command
29 | * @param message Message to send to command sender
30 | */
31 | protected void sendErrorMessage(CommandSender sender, String message) {
32 | sender.sendMessage(messagePrefix + ChatColor.RED + message);
33 | }
34 |
35 | protected void sendForwardCommand(PluginMessageRecipient sender, boolean isPlayer, String command,
36 | String[] args, boolean isOp) {
37 | ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
38 |
39 | dataOutput.writeBoolean(isPlayer);
40 |
41 | dataOutput.writeUTF(command);
42 | dataOutput.writeUTF(String.join(" ", args));
43 | dataOutput.writeBoolean(isOp);
44 | sender.sendPluginMessage(plugin, channel, dataOutput.toByteArray());
45 | }
46 |
47 | protected String[] dropFirstArgs(String[] args, int pos) {
48 | return Arrays.copyOfRange(args, pos, args.length);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/bukkit/src/main/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | # project data for Bukkit in order to register our plugin with all its components
2 | # ${-} are variables from Maven (pom.xml) which will be replaced after the build
3 | name: ${project.parent.name}
4 | version: ${project.version}
5 | main: ${project.groupId}.${project.artifactId}.${project.name}
6 |
7 | # meta data for plugin managers
8 | authors: [games647, 'https://github.com/games647/CommandForward/graphs/contributors']
9 | description: |
10 | ${project.description}
11 | website: ${project.url}
12 | dev-url: ${project.url}
13 | folia-supported: true
14 |
15 | # This plugin don't have to be transformed for compatibility with Minecraft >= 1.13
16 | api-version: '1.13'
17 |
18 | commands:
19 | forward:
20 | description: 'Forward a command to the bungee proxy'
21 | usage: / [arg]
22 | permission: ${project.artifactId}.command.forward
23 | intercept:
24 | description: 'Forward a command using the command invoker channel for message delivery'
25 | usage: / [arg]
26 | permission: ${project.artifactId}.command.intercept
27 | permissions:
28 | ${project.artifactId}.command.forward.*:
29 | description: 'Allow to use all features of forward command'
30 | children:
31 | ${project.artifactId}.command.forward: true
32 | ${project.artifactId}.command.forward.console: true
33 | ${project.artifactId}.command.forward.other: true
34 | ${project.artifactId}.command.forward:
35 | description: 'Allow to use base forward command'
36 | ${project.artifactId}.command.forward.console:
37 | description: 'Allow to use console in forward command'
38 | ${project.artifactId}.command.forward.other:
39 | description: 'Allow to use another player than ourself in forward command'
40 |
--------------------------------------------------------------------------------
/bungee/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | commandforward
8 | 0.5.0
9 | ../pom.xml
10 |
11 |
12 |
13 | commandforward.bungee
14 | jar
15 |
16 |
17 | CommandForwardBungee
18 |
19 |
20 |
21 |
22 |
23 | bungeecord-repo
24 | https://oss.sonatype.org/content/groups/public/
25 |
26 |
27 |
28 |
29 |
30 | net.md-5
31 | bungeecord-api
32 | 1.19-R0.1-SNAPSHOT
33 | provided
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/bungee/src/main/java/com/github/games647/commandforward/bungee/CommandForwardBungee.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.bungee;
2 |
3 | import com.google.common.io.ByteArrayDataInput;
4 | import com.google.common.io.ByteStreams;
5 | import net.md_5.bungee.api.ChatColor;
6 | import net.md_5.bungee.api.CommandSender;
7 | import net.md_5.bungee.api.chat.BaseComponent;
8 | import net.md_5.bungee.api.chat.ComponentBuilder;
9 | import net.md_5.bungee.api.connection.ProxiedPlayer;
10 | import net.md_5.bungee.api.event.ChatEvent;
11 | import net.md_5.bungee.api.event.PluginMessageEvent;
12 | import net.md_5.bungee.api.plugin.Command;
13 | import net.md_5.bungee.api.plugin.Listener;
14 | import net.md_5.bungee.api.plugin.Plugin;
15 | import net.md_5.bungee.event.EventHandler;
16 |
17 | import java.util.Map;
18 | import java.util.Objects;
19 | import java.util.Optional;
20 |
21 | public class CommandForwardBungee extends Plugin implements Listener {
22 |
23 | private static final String MESSAGE_CHANNEL = "commandforward:cmd";
24 |
25 | @Override
26 | public void onEnable() {
27 | //this is required to listen to messages from the server
28 | getProxy().registerChannel(MESSAGE_CHANNEL);
29 | getProxy().getPluginManager().registerListener(this, this);
30 | }
31 |
32 | @EventHandler
33 | public void onServerConnected(PluginMessageEvent messageEvent) {
34 | String channel = messageEvent.getTag();
35 | if (messageEvent.isCancelled() || !Objects.equals(MESSAGE_CHANNEL, channel)) {
36 | return;
37 | }
38 |
39 | // do not forward it further to the client
40 | messageEvent.setCancelled(true);
41 |
42 | //check if the message is sent from the server
43 | if (ProxiedPlayer.class.isAssignableFrom(messageEvent.getReceiver().getClass())) {
44 | executeMessage(
45 | (ProxiedPlayer) messageEvent.getReceiver(),
46 | ByteStreams.newDataInput(messageEvent.getData())
47 | );
48 | }
49 | }
50 |
51 | private void executeMessage(ProxiedPlayer sender, ByteArrayDataInput dataInput) {
52 | boolean isPlayer = dataInput.readBoolean();
53 | String command = dataInput.readUTF().toLowerCase();
54 | String arguments = dataInput.readUTF();
55 | CommandSender invoker = (isPlayer) ? sender : getProxy().getConsole();
56 | boolean isOp = dataInput.readBoolean();
57 |
58 | Optional optCmd = getRegisteredCommand(command);
59 | if (!optCmd.isPresent()) {
60 | if (isPlayer) {
61 | // execute only player commands if Bukkit requested to use this player as the receiver
62 | ChatEvent event = new ChatEvent(sender, sender, '/' + command + ' ' + arguments);
63 | getProxy().getPluginManager().callEvent(event);
64 | if (!event.isCancelled()) {
65 | sendErrorMessage(invoker, "Unknown player command: " + command);
66 | }
67 | } else {
68 | sendErrorMessage(invoker, "Unknown console command: " + command);
69 | }
70 | } else {
71 | Command cmd = optCmd.get();
72 | boolean isDisabled = !getProxy().getPluginManager().isExecutableCommand(command, invoker);
73 | if (isDisabled) {
74 | sendErrorMessage(invoker, "Command is disabled");
75 | return;
76 | }
77 |
78 | if (isOp) {
79 | // skip additional permission checks - the permissions will be checked on Bukkit
80 | cmd.execute(invoker, arguments.split(" "));
81 | } else {
82 | getProxy().getPluginManager().dispatchCommand(invoker, command + ' ' + arguments);
83 | }
84 | }
85 | }
86 |
87 | private Optional getRegisteredCommand(String command) {
88 | return getProxy().getPluginManager().getCommands()
89 | .stream()
90 | .filter(entry -> entry.getKey().equals(command))
91 | .findFirst()
92 | .map(Map.Entry::getValue);
93 | }
94 |
95 | /**
96 | * Print an error message
97 | *
98 | * @param sender Sender that execute the current command
99 | * @param message Message to send to command sender
100 | */
101 | private void sendErrorMessage(CommandSender sender, String message) {
102 | String prefix = String.format("[%s] %s", getDescription().getName(), message);
103 | BaseComponent[] components = new ComponentBuilder(prefix)
104 | .color(ChatColor.RED)
105 | .append(message)
106 | .create();
107 | sender.sendMessage(components);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/bungee/src/main/resources/bungee.yml:
--------------------------------------------------------------------------------
1 | # project meta data for BungeeCord
2 | # This file will be prioritised over plugin.yml which can be also used for Bungee
3 | # This make it easy to combine BungeeCord and Bukkit support in one plugin
4 | name: ${project.parent.name}
5 | # ${-} will be automatically replaced by Maven
6 | main: ${project.groupId}.${project.artifactId}.${project.name}
7 |
8 | version: ${project.version}
9 | author: games647, https://github.com/games647/CommandForward/graphs/contributors
10 |
11 | description: |
12 | ${project.description}
13 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.github.games647
6 |
7 | commandforward
8 | pom
9 |
10 | CommandForward
11 | 0.5.0
12 |
13 | https://dev.bukkit.org/projects/commandforward
14 |
15 | Forwards commands from Bukkit to BungeeCord (Or Velocity) to execute it there
16 |
17 |
18 |
19 | UTF-8
20 |
21 | 1.8
22 | 1.8
23 |
24 |
25 |
26 | bukkit
27 | bungee
28 | velocity
29 | universal
30 |
31 |
32 |
33 | install
34 |
35 | ${project.name}
36 |
37 |
38 |
39 | src/main/resources
40 |
41 | true
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/universal/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | commandforward
8 | 0.5.0
9 | ../pom.xml
10 |
11 |
12 | commandforward-universal
13 | jar
14 |
15 | CommandForwardUniversal
16 |
17 |
18 | package
19 | ${project.parent.name}
20 |
21 |
22 |
23 | org.apache.maven.plugins
24 | maven-shade-plugin
25 | 3.2.4
26 |
27 | false
28 | false
29 |
30 |
31 | ${project.groupId}:*
32 |
33 |
34 |
35 |
36 |
37 | package
38 |
39 | shade
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | ${project.groupId}
50 | commandforward.bukkit
51 | ${project.version}
52 |
53 |
54 |
55 | ${project.groupId}
56 | commandforward.bungee
57 | ${project.version}
58 |
59 |
60 |
61 | ${project.groupId}
62 | commandforward.velocity
63 | ${project.version}
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/velocity/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 |
6 | com.github.games647
7 | commandforward
8 | 0.5.0
9 | ../pom.xml
10 |
11 |
12 |
13 | commandforward.velocity
14 | jar
15 |
16 |
17 | CommandForwardVelocity
18 |
19 |
20 |
21 |
22 | velocitypowered-repo
23 | https://nexus.velocitypowered.com/repository/maven-public/
24 |
25 |
26 |
27 |
28 |
29 | com.velocitypowered
30 | velocity-api
31 | 3.1.0
32 | provided
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/velocity/src/main/java/com/github/games647/commandforward/velocity/CommandForwardVelocity.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.velocity;
2 |
3 | import com.google.inject.Inject;
4 | import com.velocitypowered.api.event.Subscribe;
5 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
6 | import com.velocitypowered.api.plugin.Plugin;
7 | import com.velocitypowered.api.proxy.ProxyServer;
8 | import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
9 | import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
10 | import org.slf4j.Logger;
11 |
12 | /**
13 | *
14 | * Velocity support for CommandForward plugin
15 | * Since Velocity supports BungeeCord plugin-
16 | * messaging channels now
17 | *
18 | * @author Alijk
19 | * @since 2022-02-13
20 | *
21 | */
22 | @Plugin(
23 | id = "commandforward",
24 | name = "CommandForward",
25 | version = "0.5.0",
26 | description = "Forwards commands from Bukkit to BungeeCord (Or Velocity) to execute it there",
27 | authors = {"games647", "https://github.com/games647/CommandForward/graphs/contributors"}
28 | )
29 | public class CommandForwardVelocity {
30 |
31 | private final ChannelIdentifier MESSAGE_CHANNEL = MinecraftChannelIdentifier.from("commandforward:cmd");
32 |
33 | private final ProxyServer proxyServer;
34 | private final Logger logger;
35 |
36 | @Inject
37 | public CommandForwardVelocity(ProxyServer proxyServer, Logger logger) {
38 | this.proxyServer = proxyServer;
39 | this.logger = logger;
40 | }
41 |
42 | @Subscribe
43 | public void onProxyInit(ProxyInitializeEvent event) {
44 | // Register the custom messaging channel
45 | proxyServer.getChannelRegistrar().register(MESSAGE_CHANNEL);
46 |
47 | // Register an event handler to catch messages for it
48 | proxyServer.getEventManager().register(this, new MessageListener(proxyServer, logger, MESSAGE_CHANNEL));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/velocity/src/main/java/com/github/games647/commandforward/velocity/MessageListener.java:
--------------------------------------------------------------------------------
1 | package com.github.games647.commandforward.velocity;
2 |
3 | import com.google.common.io.ByteArrayDataInput;
4 | import com.google.common.io.ByteStreams;
5 | import com.velocitypowered.api.command.CommandManager;
6 | import com.velocitypowered.api.command.CommandSource;
7 | import com.velocitypowered.api.event.Subscribe;
8 | import com.velocitypowered.api.event.connection.PluginMessageEvent;
9 | import com.velocitypowered.api.plugin.PluginManager;
10 | import com.velocitypowered.api.proxy.Player;
11 | import com.velocitypowered.api.proxy.ProxyServer;
12 | import com.velocitypowered.api.proxy.ServerConnection;
13 | import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
14 | import net.kyori.adventure.audience.Audience;
15 | import net.kyori.adventure.text.Component;
16 | import net.kyori.adventure.text.format.NamedTextColor;
17 | import org.slf4j.Logger;
18 |
19 | public class MessageListener {
20 |
21 | private final ProxyServer server;
22 | private final Logger logger;
23 | private final ChannelIdentifier identifier;
24 |
25 | public MessageListener(ProxyServer server, Logger logger, ChannelIdentifier identifier) {
26 | this.server = server;
27 | this.logger = logger;
28 | this.identifier = identifier;
29 | }
30 |
31 | @Subscribe
32 | public void onPluginMessageEvent(PluginMessageEvent event) {
33 | // Received plugin message, check channel identifier matches
34 | if (event.getIdentifier().equals(identifier)) {
35 | // Since this message was meant for this listener set it to handled
36 | // We do this so the message doesn't get routed through.
37 | event.setResult(PluginMessageEvent.ForwardResult.handled());
38 |
39 | if (event.getSource() instanceof ServerConnection) {
40 | // Read the data written to the message
41 | Player player = ((ServerConnection) event.getSource()).getPlayer();
42 | ByteArrayDataInput in = ByteStreams.newDataInput(event.getData());
43 | parseMessage(player, in);
44 | }
45 | }
46 | }
47 |
48 | private void parseMessage(Player source, ByteArrayDataInput dataInput) {
49 | boolean isPlayer = dataInput.readBoolean();
50 | String command = dataInput.readUTF();
51 | String arguments = dataInput.readUTF();
52 | CommandSource invoker = (isPlayer) ? source : server.getConsoleCommandSource();
53 |
54 | String username = source.getUsername();
55 | logger.info("Received new forward command '{}' with Args: '{}' from {}", command, arguments, username);
56 |
57 | invokeCommand(invoker, dataInput.readBoolean(), command, arguments);
58 | }
59 |
60 | private void invokeCommand(CommandSource invoker, boolean isOp, String command, String arguments) {
61 | PluginManager pluginManager = server.getPluginManager();
62 | CommandManager commandManager = server.getCommandManager();
63 |
64 | // TODO implement isOp handle progress
65 | commandManager.executeImmediatelyAsync(invoker, command + ' ' + arguments)
66 | .thenAccept(success -> {
67 | if (!success) {
68 | sendErrorMessage(invoker, "Failed to find command");
69 | }
70 | })
71 | .exceptionally(error -> {
72 | logger.warn("Failed to invoke forwarded command", error);
73 | return null;
74 | });
75 | }
76 |
77 | /**
78 | * Print an error message
79 | *
80 | * @param source Sender that execute the current command
81 | * @param message Message to send to command sender
82 | */
83 | private void sendErrorMessage(Audience source, String message) {
84 | String msg = String.format("[%s] %s", "CommandForward", message);
85 | Component textComponent = Component.text(msg, NamedTextColor.RED);
86 | source.sendMessage(textComponent);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------