├── .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 | --------------------------------------------------------------------------------