├── .gitignore ├── LICENSE ├── README.md ├── bukkit ├── pom.xml └── src │ └── main │ ├── java │ └── de │ │ └── themoep │ │ └── connectorplugin │ │ └── bukkit │ │ ├── Bridge.java │ │ ├── BukkitConnectorPlugin.java │ │ ├── commands │ │ ├── ConnectorCommand.java │ │ ├── ProxyConsoleCommand.java │ │ ├── ProxyPlayerCommand.java │ │ ├── ServerConsoleCommand.java │ │ ├── SubCommand.java │ │ ├── TeleportCommand.java │ │ └── TeleportToPlayerCommand.java │ │ └── connector │ │ ├── BukkitConnector.java │ │ ├── MqttConnector.java │ │ ├── PluginMessageConnector.java │ │ └── RedisConnector.java │ └── resources │ ├── config.yml │ └── plugin.yml ├── bungee ├── pom.xml └── src │ └── main │ ├── java │ └── de │ │ └── themoep │ │ └── connectorplugin │ │ └── bungee │ │ ├── Bridge.java │ │ ├── BungeeConnectorPlugin.java │ │ ├── commands │ │ ├── ConnectorCommand.java │ │ ├── ProxyConsoleCommand.java │ │ ├── ServerConsoleCommand.java │ │ ├── ServerPlayerCommand.java │ │ ├── SubCommand.java │ │ ├── TeleportCommand.java │ │ └── TeleportToPlayerCommand.java │ │ └── connector │ │ ├── BungeeConnector.java │ │ ├── MqttConnector.java │ │ ├── PluginMessageConnector.java │ │ └── RedisConnector.java │ └── resources │ ├── bungee-config.yml │ └── bungee.yml ├── core ├── pom.xml └── src │ └── main │ └── java │ └── de │ └── themoep │ └── connectorplugin │ ├── BridgeCommon.java │ ├── BridgedCommand.java │ ├── BridgedSuggestions.java │ ├── ConnectorPlugin.java │ ├── LocationInfo.java │ ├── ProxyBridgeCommon.java │ ├── ResponseHandler.java │ └── connector │ ├── ConnectingPlugin.java │ ├── Connector.java │ ├── Message.java │ ├── MessageTarget.java │ ├── MqttConnection.java │ ├── RedisConnection.java │ └── VersionMismatchException.java ├── plugin └── pom.xml ├── pom.xml └── velocity ├── pom.xml └── src └── main ├── java └── de │ └── themoep │ └── connectorplugin │ └── velocity │ ├── Bridge.java │ ├── PluginConfig.java │ ├── VelocityConnectorPlugin.java │ ├── commands │ ├── ConnectorCommand.java │ ├── ProxyConsoleCommand.java │ ├── ServerConsoleCommand.java │ ├── ServerPlayerCommand.java │ ├── SubCommand.java │ ├── TeleportCommand.java │ └── TeleportToPlayerCommand.java │ └── connector │ ├── MqttConnector.java │ ├── PluginMessageConnector.java │ ├── RedisConnector.java │ └── VelocityConnector.java └── resources ├── velocity-config.yml └── velocity-plugin.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target/ 4 | dependency-reduced-pom.xml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConnectorPlugin 2 | 3 | Plugin to simplify communication between multiple Minecraft servers in a network (and their proxy). Support Spigot/Paper, BungeeCord and Velocity. 4 | 5 | This includes a bridging utility and some basic commands to use the provided utility functionality but it is mostly meant to be depended on by other plugins so they can easily query and send data between servers without having to implement that logic themselves. 6 | 7 | ## Features 8 | - [x] Send arbitrary data to servers and proxies 9 | - [x] Send commands to other servers and proxies 10 | - [x] Server-side command registration from proxy 11 | - [x] Location querying 12 | - [ ] Server state querying 13 | - [x] Teleporting (Including multi-proxy player to player teleportation) 14 | - [ ] Proxy-side Vault/Tresor integration 15 | 16 | ## Communication Methods 17 | 18 | - [x] Plugin Messages (limited functionality) 19 | - [ ] peer-to-peer 20 | - [x] redis pub sub 21 | - [x] MQTT (E.g. with RabbitMQ) 22 | 23 | ## Commands 24 | 25 | ### On the Spigot server 26 | 27 | > `/connectorplugin` 28 | > *Permission:* `connectorplugin.command` 29 | > *Aliases:* `connector`, `connectorcommand`, `connplugin`, `cp` 30 | > 31 | >> `teleport [ [ [ ]]]` 32 | >> Teleport a player to the specified server/world/location 33 | >> *Permission:* `connectorplugin.command.teleport` 34 | >> *Aliases:* `tp`, `send` 35 | > 36 | >> `teleporttoplayer []` 37 | >> Teleport yourself to the player or the player to the target player 38 | >> *Permission:* `connectorplugin.command.teleporttoplayer` 39 | >> *Aliases:* `teleportplayer`, `tpp` 40 | > 41 | >> `servercommand ...` 42 | >> Executes command in the console of the specified server 43 | >> *Permission:* `connectorplugin.command.servercommand` 44 | >> *Aliases:* `serverconsole`, `serverconsolecommand`, `server`, `scc` 45 | > 46 | >> `servercommand p: ...` 47 | >> Executes command in the console of the server the player is connected to 48 | >> *Permission:* `connectorplugin.command.servercommand` 49 | >> *Aliases:* `serverconsole`, `serverconsolecommand`, `server`, `scc` 50 | > 51 | >> `proxycommand ...` 52 | >> Execute a command on all other proxies 53 | >> *Permission:* `connectorplugin.command.proxycommand` 54 | >> *Aliases:* `proxyconsole`, `proxyconsolecommand`, `proxy`, `pcc` 55 | > 56 | >> `proxyplayercommand ...` 57 | >> Execute a command as a player on the proxy they are connected to 58 | >> *Permission:* `connectorplugin.command.proxyplayercommand` 59 | >> *Aliases:* `proxyplayer`, `player`, `ppc` 60 | 61 | ### On the Proxies 62 | 63 | ### Main Bungee command 64 | > `/connectorpluginbungee` 65 | > *Permission:* `connectorplugin.command` 66 | > *Aliases:* `connectorbungee`, `connectorcommandbungee`, `connpluginbungee`, `cpb` 67 | > 68 | > 69 | ### Main Velocity command 70 | > `/connectorpluginvelocity` 71 | > *Permission:* `connectorplugin.command` 72 | > *Aliases:* `connectorvelocity`, `connectorcommandvelocity`, `connpluginvelocity`, `cpv` 73 | > 74 | 75 | ### Proxy sub commands 76 | >> `teleport [ [ [ ]]]` 77 | >> Teleport a player to the specified server/world/location 78 | >> *Permission:* `connectorplugin.command.teleport` 79 | >> *Aliases:* `tp`, `send` 80 | > 81 | >> `teleporttoplayer []` 82 | >> Teleport yourself to the player or the player to the target player 83 | >> *Permission:* `connectorplugin.command.teleporttoplayer` 84 | >> *Aliases:* `teleportplayer`, `tpp` 85 | > 86 | >> `servercommand ...` 87 | >> Executes command in the console of the specified server 88 | >> *Permission:* `connectorplugin.command.servercommand` 89 | >> *Aliases:* `serverconsole`, `serverconsolecommand`, `server`, `scc` 90 | > 91 | >> `servercommand p: ...` 92 | >> Executes command in the console of the server the player is connected to 93 | >> *Permission:* `connectorplugin.command.servercommand` 94 | >> *Aliases:* `serverconsole`, `serverconsolecommand`, `server`, `scc` 95 | > 96 | >> `serverplayercommand ...` 97 | >> Execute a command as the target player on their server 98 | >> *Permission:* `connectorplugin.command.serverplayercommand` 99 | >> *Aliases:* `serverplayer`, `player`, `spc` 100 | > 101 | >> `proxycommand ...` 102 | >> Execute a command on all other proxies 103 | >> *Permission:* `connectorplugin.command.proxycommand` 104 | >> *Aliases:* `proxyconsole`, `proxyconsolecommand`, `proxy`, `pcc` 105 | 106 | ## Developer Info 107 | 108 | ### Usage 109 | 110 | [Javadocs](https://docs.phoenix616.dev/connectorplugin/) 111 | 112 | Check [the wiki](https://wiki.phoenix616.dev/plugin:connectorplugin:usage:start) for usage examples. 113 | 114 | ### Maven Info 115 | 116 | ```xml 117 | 118 | minebench-repo 119 | https://repo.minebench.de/ 120 | 121 | ``` 122 | 123 | ```xml 124 | 125 | de.themoep.connectorplugin 126 | [bukkit|bungee|velocity] 127 | 1.2-SNAPSHOT 128 | provided 129 | 130 | ``` 131 | 132 | ## Download 133 | 134 | Latest development builds can be found on the Minebench.de Jenkins: https://ci.minebench.de/job/ConnectorPlugin/ 135 | 136 | ## License 137 | 138 | This project is [licensed](LICENSE) under the AGPLv3: 139 | 140 | ``` 141 | ConnectorPlugin 142 | Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 143 | 144 | This program is free software: you can redistribute it and/or modify 145 | it under the terms of the GNU Affero General Public License as published 146 | by the Free Software Foundation, either version 3 of the License, or 147 | (at your option) any later version. 148 | 149 | This program is distributed in the hope that it will be useful, 150 | but WITHOUT ANY WARRANTY; without even the implied warranty of 151 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 152 | GNU Affero General Public License for more details. 153 | 154 | You should have received a copy of the GNU Affero General Public License 155 | along with this program. If not, see . 156 | ``` 157 | -------------------------------------------------------------------------------- /bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | de.themoep.connectorplugin 9 | parent 10 | 1.5.2-SNAPSHOT 11 | 12 | 13 | bukkit 14 | jar 15 | 16 | 17 | 18 | spigotmc-repo 19 | https://hub.spigotmc.org/nexus/content/groups/public/ 20 | 21 | 22 | sonatype 23 | https://oss.sonatype.org/content/groups/public/ 24 | 25 | 26 | papermc 27 | https://papermc.io/repo/repository/maven-public/ 28 | 29 | 30 | 31 | 32 | 33 | org.spigotmc 34 | spigot-api 35 | 36 | 37 | ${groupId} 38 | core 39 | ${project.version} 40 | 41 | 42 | io.papermc 43 | paperlib 44 | 45 | 46 | 47 | 48 | clean package 49 | ${project.name} 50 | 51 | 52 | src/main/resources 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | static_build_number 61 | 62 | 63 | !env.BUILD_NUMBER 64 | 65 | 66 | 67 | 0 68 | (compiled at ${maven.build.timestamp}) 69 | 70 | 71 | 72 | dynamic_build_number 73 | 74 | 75 | env.BUILD_NUMBER 76 | 77 | 78 | 79 | ${env.BUILD_NUMBER} 80 | (build ${env.BUILD_NUMBER}) 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/BukkitConnectorPlugin.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.ConnectorPlugin; 22 | import de.themoep.connectorplugin.bukkit.commands.ConnectorCommand; 23 | import de.themoep.connectorplugin.bukkit.connector.BukkitConnector; 24 | import de.themoep.connectorplugin.bukkit.connector.MqttConnector; 25 | import de.themoep.connectorplugin.bukkit.connector.PluginMessageConnector; 26 | import de.themoep.connectorplugin.bukkit.connector.RedisConnector; 27 | import de.themoep.connectorplugin.connector.ConnectingPlugin; 28 | import de.themoep.connectorplugin.connector.MessageTarget; 29 | import org.bukkit.configuration.ConfigurationSection; 30 | import org.bukkit.entity.Player; 31 | import org.bukkit.event.EventHandler; 32 | import org.bukkit.event.Listener; 33 | import org.bukkit.event.server.PluginDisableEvent; 34 | import org.bukkit.plugin.java.JavaPlugin; 35 | 36 | import java.io.File; 37 | import java.util.HashMap; 38 | import java.util.Locale; 39 | import java.util.Map; 40 | import java.util.logging.Level; 41 | 42 | public final class BukkitConnectorPlugin extends JavaPlugin implements ConnectorPlugin, Listener { 43 | 44 | private BukkitConnector connector; 45 | private Bridge bridge; 46 | private boolean debug = true; 47 | private String globalGroup; 48 | private Map pluginGroups; 49 | private String serverName; 50 | 51 | @Override 52 | public void onEnable() { 53 | saveDefaultConfig(); 54 | reloadConfig(); 55 | 56 | debug = getConfig().getBoolean("debug"); 57 | globalGroup = getConfig().getString("group", "global"); 58 | pluginGroups = new HashMap<>(); 59 | ConfigurationSection pluginGroupsConfig = getConfig().getConfigurationSection("plugin-groups"); 60 | if (pluginGroupsConfig != null) { 61 | for (String pluginName : pluginGroupsConfig.getKeys(false)) { 62 | pluginGroups.put(pluginName.toLowerCase(Locale.ROOT), pluginGroupsConfig.getString(pluginName)); 63 | } 64 | } 65 | serverName = getConfig().getString("server-name", "changeme"); 66 | if ("changeme".equals(serverName)) { 67 | serverName = new File(".").getAbsoluteFile().getParentFile().getName(); 68 | getLogger().log(Level.WARNING, "Server name is not configured! Please set it in your plugin config! Using the name of the server directory instead: " + serverName); 69 | } 70 | 71 | String messengerType = getConfig().getString("messenger-type", "plugin_messages").toLowerCase(Locale.ROOT); 72 | switch (messengerType) { 73 | default: 74 | getLogger().log(Level.WARNING, "Messenger type '" + messengerType + "' is not supported, falling back to plugin messages!"); 75 | case "plugin_messages": 76 | connector = new PluginMessageConnector(this); 77 | getLogger().log(Level.WARNING, "Using plugin messages as the messenger type will come with" + 78 | " some caveats like sending to servers without players or to" + 79 | " other proxies not working!"); 80 | getLogger().log(Level.WARNING, "Please consider using one of the other messenger types!"); 81 | break; 82 | case "redis": 83 | connector = new RedisConnector(this); 84 | break; 85 | case "mqtt": 86 | connector = new MqttConnector(this); 87 | break; 88 | } 89 | 90 | getCommand("connectorplugin").setExecutor(new ConnectorCommand(this)); 91 | 92 | bridge = new Bridge(this); 93 | } 94 | 95 | @Override 96 | public void onDisable() { 97 | connector.close(); 98 | } 99 | 100 | @EventHandler 101 | public void onPluginDisable(PluginDisableEvent event) { 102 | if (event.getPlugin() instanceof ConnectingPlugin) { 103 | connector.unregisterMessageHandlers((ConnectingPlugin) event.getPlugin()); 104 | } 105 | } 106 | 107 | /** 108 | * Get the bridge helper class for executing certain actions on the proxy and other servers 109 | * @return The bridge helper 110 | */ 111 | public Bridge getBridge() { 112 | return bridge; 113 | } 114 | 115 | @Override 116 | public BukkitConnector getConnector() { 117 | return connector; 118 | } 119 | 120 | @Override 121 | public void runAsync(Runnable runnable) { 122 | getServer().getScheduler().runTaskAsynchronously(this, runnable); 123 | } 124 | 125 | public void runSync(Runnable runnable) { 126 | if (getServer().isPrimaryThread()) { 127 | runnable.run(); 128 | return; 129 | } 130 | getServer().getScheduler().runTask(this, runnable); 131 | } 132 | 133 | @Override 134 | public MessageTarget.Type getSourceType() { 135 | return MessageTarget.Type.SERVER; 136 | } 137 | 138 | @Override 139 | public void logDebug(String message, Throwable... throwables) { 140 | if (debug) { 141 | getLogger().log(Level.INFO, "[DEBUG] " + message, throwables.length > 0 ? throwables[0] : null); 142 | } 143 | } 144 | 145 | @Override 146 | public void logInfo(String message, Throwable... throwables) { 147 | getLogger().log(Level.INFO, message, throwables.length > 0 ? throwables[0] : null); 148 | } 149 | 150 | @Override 151 | public void logWarning(String message, Throwable... throwables) { 152 | getLogger().log(Level.WARNING, message, throwables.length > 0 ? throwables[0] : null); 153 | } 154 | 155 | @Override 156 | public void logError(String message, Throwable... throwables) { 157 | getLogger().log(Level.SEVERE, message, throwables.length > 0 ? throwables[0] : null); 158 | } 159 | 160 | @Override 161 | public String getGlobalGroup() { 162 | return globalGroup; 163 | } 164 | 165 | @Override 166 | public Map getGroups() { 167 | return pluginGroups; 168 | } 169 | 170 | @Override 171 | public String getServerName() { 172 | return serverName; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/ConnectorCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | 22 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 23 | 24 | public class ConnectorCommand extends SubCommand { 25 | 26 | public ConnectorCommand(BukkitConnectorPlugin plugin) { 27 | super(plugin, "connectorplugin", "connectorplugin.command"); 28 | registerSubCommand(new TeleportCommand(this)); 29 | registerSubCommand(new TeleportToPlayerCommand(this)); 30 | registerSubCommand(new ServerConsoleCommand(this)); 31 | registerSubCommand(new ProxyConsoleCommand(this)); 32 | registerSubCommand(new ProxyPlayerCommand(this)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/ProxyConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import org.bukkit.ChatColor; 22 | import org.bukkit.command.Command; 23 | import org.bukkit.command.CommandSender; 24 | import org.bukkit.command.PluginCommand; 25 | 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import java.util.stream.Collectors; 29 | 30 | public class ProxyConsoleCommand extends SubCommand { 31 | 32 | public ProxyConsoleCommand(ConnectorCommand parent) { 33 | super(parent.getPlugin(), "proxycommand ", parent.getPermission() + ".proxycommand", "proxyconsole", "proxyconsolecommand", "proxy", "pcc"); 34 | } 35 | 36 | @Override 37 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 38 | if (args.length < 1) { 39 | return false; 40 | } 41 | 42 | String commandString = String.join(" ", args); 43 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on proxy"); 44 | plugin.getBridge().runProxyConsoleCommand(commandString, sender::sendMessage).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 45 | return true; 46 | } 47 | 48 | @Override 49 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 50 | if (args.length > 1) { 51 | PluginCommand pluginCommand = plugin.getServer().getPluginCommand(args[1]); 52 | if (pluginCommand != null) { 53 | return pluginCommand.tabComplete(sender, args[1], Arrays.copyOfRange(args, 2, args.length)); 54 | } 55 | } 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/ProxyPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import org.bukkit.ChatColor; 22 | import org.bukkit.command.Command; 23 | import org.bukkit.command.CommandSender; 24 | import org.bukkit.entity.Player; 25 | 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import java.util.stream.Collectors; 29 | 30 | public class ProxyPlayerCommand extends SubCommand { 31 | 32 | public ProxyPlayerCommand(ConnectorCommand parent) { 33 | super(parent.getPlugin(), "proxyplayercommand ", parent.getPermission() + ".proxyplayercommand", "proxyplayer", "player", "ppc"); 34 | } 35 | 36 | @Override 37 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 38 | if (args.length < 2) { 39 | return false; 40 | } 41 | 42 | Player player = plugin.getServer().getPlayerExact(args[0]); 43 | if (player == null) { 44 | sender.sendMessage("No player with the name " + args[0] + " is online on this server!"); 45 | return true; 46 | } 47 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 48 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on the proxy for player '" + player.getName() + "'"); 49 | plugin.getBridge().runProxyPlayerCommand(player, commandString).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 50 | return true; 51 | } 52 | 53 | @Override 54 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/ServerConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import org.bukkit.ChatColor; 22 | import org.bukkit.command.Command; 23 | import org.bukkit.command.CommandSender; 24 | import org.bukkit.command.PluginCommand; 25 | 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import java.util.stream.Collectors; 29 | 30 | public class ServerConsoleCommand extends SubCommand { 31 | 32 | public ServerConsoleCommand(ConnectorCommand parent) { 33 | super(parent.getPlugin(), "servercommand ", parent.getPermission() + ".servercommand", "serverconsole", "serverconsolecommand", "server", "scc"); 34 | } 35 | 36 | @Override 37 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 38 | if (args.length < 2) { 39 | return false; 40 | } 41 | 42 | String serverName = args[0]; 43 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 44 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on server '" + serverName + "'"); 45 | plugin.getBridge().runServerConsoleCommand(serverName, commandString, sender::sendMessage).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 46 | return true; 47 | } 48 | 49 | @Override 50 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 51 | if (args.length > 1) { 52 | PluginCommand pluginCommand = plugin.getServer().getPluginCommand(args[1]); 53 | if (pluginCommand != null) { 54 | return pluginCommand.tabComplete(sender, args[1], Arrays.copyOfRange(args, 2, args.length)); 55 | } 56 | } 57 | return null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/SubCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | /* 3 | * ConnectorPlugin 4 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 21 | import org.bukkit.command.Command; 22 | import org.bukkit.command.CommandSender; 23 | import org.bukkit.command.TabExecutor; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Arrays; 27 | import java.util.LinkedHashMap; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.Map; 31 | 32 | public abstract class SubCommand implements TabExecutor { 33 | protected final BukkitConnectorPlugin plugin; 34 | private final String name; 35 | private final String usage; 36 | private final String[] aliases; 37 | private final String permission; 38 | 39 | private Map subCommands = new LinkedHashMap<>(); 40 | private Map subCommandAliases = new LinkedHashMap<>(); 41 | 42 | public SubCommand(BukkitConnectorPlugin plugin, String usage, String permission, String... aliases) { 43 | this.plugin = plugin; 44 | String[] usageParts = usage.split(" ", 2); 45 | this.name = usageParts[0]; 46 | this.usage = usageParts.length > 1 ? usageParts[1] : ""; 47 | this.permission = permission; 48 | this.aliases = aliases; 49 | } 50 | 51 | public void registerSubCommand(SubCommand subCommand) { 52 | subCommands.put(subCommand.getName().toLowerCase(Locale.ROOT), subCommand); 53 | for (String alias : subCommand.getAliases()) { 54 | subCommandAliases.put(alias.toLowerCase(Locale.ROOT), subCommand); 55 | } 56 | } 57 | 58 | public SubCommand getSubCommand(String name) { 59 | SubCommand subCommand = subCommands.get(name.toLowerCase(Locale.ROOT)); 60 | if (subCommand == null) { 61 | return subCommandAliases.get(name.toLowerCase(Locale.ROOT)); 62 | } 63 | return subCommand; 64 | } 65 | 66 | @Override 67 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 68 | if (args.length == 0) { 69 | return false; 70 | } 71 | SubCommand subCommand = getSubCommand(args[0]); 72 | if (subCommand != null) { 73 | if (sender.hasPermission(subCommand.getPermission())) { 74 | if (subCommand.onCommand(sender, command, label + " " + args[0], Arrays.copyOfRange(args, 1, args.length))) { 75 | return true; 76 | } else if (!subCommand.getUsage().isEmpty()) { 77 | sender.sendMessage("Usage: /" + label + " " + args[0] + " " + subCommand.getUsage()); 78 | return true; 79 | } 80 | } 81 | } 82 | return false; 83 | } 84 | 85 | @Override 86 | public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { 87 | if (args.length == 0 || args[0].isEmpty()) { 88 | return new ArrayList<>(subCommands.keySet()); 89 | } 90 | SubCommand subCommand = getSubCommand(args[0]); 91 | if (subCommand != null && sender.hasPermission(subCommand.getPermission())) { 92 | return subCommand.onTabComplete(sender, command, label, Arrays.copyOfRange(args, 1, args.length)); 93 | } 94 | List completions = new ArrayList<>(); 95 | for (Map.Entry e : subCommands.entrySet()) { 96 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && sender.hasPermission(e.getValue().getPermission())) { 97 | completions.add(e.getKey()); 98 | } 99 | } 100 | for (Map.Entry e : subCommandAliases.entrySet()) { 101 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && sender.hasPermission(e.getValue().getPermission())) { 102 | completions.add(e.getKey()); 103 | } 104 | } 105 | return completions; 106 | } 107 | 108 | public String getName() { 109 | return name; 110 | } 111 | 112 | public String getUsage() { 113 | return usage; 114 | } 115 | 116 | public String[] getAliases() { 117 | return aliases; 118 | } 119 | 120 | public String getPermission() { 121 | return permission; 122 | } 123 | 124 | public BukkitConnectorPlugin getPlugin() { 125 | return plugin; 126 | } 127 | 128 | public Map getSubCommands() { 129 | return subCommands; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/TeleportCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.LocationInfo; 22 | import net.md_5.bungee.api.ChatColor; 23 | import org.bukkit.command.Command; 24 | import org.bukkit.command.CommandSender; 25 | 26 | import java.util.List; 27 | 28 | public class TeleportCommand extends SubCommand { 29 | public TeleportCommand(ConnectorCommand parent) { 30 | super(parent.getPlugin(), "teleport [ [ ]]", parent.getPermission() + ".teleport", "tp", "send"); 31 | } 32 | 33 | @Override 34 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 35 | if (args.length < 2) { 36 | return false; 37 | } 38 | 39 | String playerName = args[0]; 40 | String serverName = args[1]; 41 | 42 | if (args.length == 2) { 43 | plugin.getBridge().sendToServer(playerName, serverName, sender::sendMessage).thenAccept(success -> { 44 | if (success) { 45 | sender.sendMessage(ChatColor.GREEN + "Successfully connected " + playerName + " to " + serverName); 46 | } else { 47 | sender.sendMessage(ChatColor.RED + "Error while sending " + playerName + " to " + serverName); 48 | } 49 | }); 50 | return true; 51 | } 52 | 53 | if (args.length == 3) { 54 | plugin.getBridge().teleport(playerName, serverName, args[2], sender::sendMessage) 55 | .thenAccept(success -> { 56 | if (!success) { 57 | sender.sendMessage(ChatColor.RED + "Error while teleporting..."); 58 | } 59 | }); 60 | return true; 61 | } 62 | 63 | if (args.length < 6) { 64 | return false; 65 | } 66 | 67 | try { 68 | LocationInfo location = new LocationInfo( 69 | serverName, 70 | args[2], 71 | Double.parseDouble(args[3]), 72 | Double.parseDouble(args[4]), 73 | Double.parseDouble(args[5]), 74 | args.length > 6 ? Float.parseFloat(args[6]) : 0, 75 | args.length > 7 ? Float.parseFloat(args[7]) : 0 76 | ); 77 | 78 | sender.sendMessage("Sending teleport request for " + playerName); 79 | plugin.getBridge().teleport(playerName, location, sender::sendMessage) 80 | .thenAccept(success -> { 81 | if (!success) { 82 | sender.sendMessage(ChatColor.RED + "Error while teleporting..."); 83 | } 84 | }); 85 | return true; 86 | } catch (IllegalArgumentException e) { 87 | sender.sendMessage(ChatColor.RED + "Error while parsing input! " + e.getMessage()); 88 | return false; 89 | } 90 | } 91 | 92 | @Override 93 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 94 | return null; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/commands/TeleportToPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import net.md_5.bungee.api.ChatColor; 22 | import org.bukkit.command.Command; 23 | import org.bukkit.command.CommandSender; 24 | import org.bukkit.entity.Player; 25 | 26 | import java.util.List; 27 | 28 | public class TeleportToPlayerCommand extends SubCommand { 29 | public TeleportToPlayerCommand(ConnectorCommand parent) { 30 | super(parent.getPlugin(), "teleporttoplayer []", parent.getPermission() + ".teleporttoplayer", "teleportplayer"); 31 | } 32 | 33 | @Override 34 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 35 | String playerName; 36 | String targetName; 37 | if (args.length == 1 && sender instanceof Player) { 38 | playerName = sender.getName(); 39 | targetName = args[0]; 40 | } else if (args.length == 2) { 41 | playerName = args[0]; 42 | targetName = args[1]; 43 | } else { 44 | return false; 45 | } 46 | 47 | plugin.getBridge().teleport(playerName, targetName, sender::sendMessage).thenAccept(success -> { 48 | if (success) { 49 | sender.sendMessage(ChatColor.GREEN + "Successfully teleported " + playerName + " to " + targetName); 50 | } else { 51 | sender.sendMessage(ChatColor.RED + "Error while teleporting " + playerName + " to " + targetName); 52 | } 53 | }); 54 | return true; 55 | } 56 | 57 | @Override 58 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/connector/BukkitConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Connector; 23 | import de.themoep.connectorplugin.connector.Message; 24 | import org.bukkit.entity.Player; 25 | 26 | public abstract class BukkitConnector extends Connector { 27 | 28 | public BukkitConnector(BukkitConnectorPlugin plugin, boolean requiresPlayer) { 29 | super(plugin, requiresPlayer); 30 | } 31 | 32 | protected Player getReceiverImplementation(String name) { 33 | Player player = plugin.getServer().getPlayerExact(name); 34 | if (player != null && player.isOnline()) { 35 | return player; 36 | } 37 | return null; 38 | } 39 | 40 | @Override 41 | protected void handle(Player receiver, Message message) { 42 | switch (message.getTarget()) { 43 | case OTHERS_WITH_PLAYERS: 44 | case ALL_WITH_PLAYERS: 45 | if (plugin.getServer().getOnlinePlayers().isEmpty()) { 46 | return; 47 | } 48 | } 49 | super.handle(receiver, message); 50 | } 51 | 52 | @Override 53 | protected void sendDataImplementation(Object targetData, Message message) { 54 | sendDataImplementation(targetData instanceof String 55 | ? (hasPrefix((String) targetData) 56 | ? (String) targetData 57 | : SERVER_PREFIX + targetData) 58 | : (targetData instanceof Player 59 | ? PLAYER_PREFIX + ((Player) targetData).getName() 60 | : ""), message); 61 | } 62 | 63 | protected abstract void sendDataImplementation(String targetData, Message message); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/connector/MqttConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.MqttConnection; 24 | 25 | public class MqttConnector extends BukkitConnector { 26 | private final MqttConnection connection; 27 | 28 | public MqttConnector(BukkitConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new MqttConnection( 31 | plugin, 32 | plugin.getConfig().getString("mqtt.broker-uri"), 33 | plugin.getConfig().getString("mqtt.client-id", null), 34 | plugin.getConfig().getString("mqtt.username"), 35 | plugin.getConfig().getString("mqtt.password"), 36 | plugin.getConfig().getInt("mqtt.keep-alive"), 37 | (receiver, message) -> plugin.runSync(() -> handle(receiver, message)) 38 | ); 39 | } 40 | 41 | @Override 42 | protected void sendDataImplementation(String targetData, Message message) { 43 | connection.sendMessage(targetData, message); 44 | } 45 | 46 | @Override 47 | public void close() { 48 | connection.close(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/connector/PluginMessageConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.common.io.ByteArrayDataInput; 22 | import com.google.common.io.ByteArrayDataOutput; 23 | import com.google.common.io.ByteStreams; 24 | import de.themoep.connectorplugin.connector.Message; 25 | import de.themoep.connectorplugin.connector.MessageTarget; 26 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 27 | import de.themoep.connectorplugin.connector.VersionMismatchException; 28 | import org.bukkit.entity.Player; 29 | import org.bukkit.event.EventHandler; 30 | import org.bukkit.event.EventPriority; 31 | import org.bukkit.event.Listener; 32 | import org.bukkit.event.player.PlayerJoinEvent; 33 | import org.bukkit.plugin.messaging.PluginMessageListener; 34 | 35 | import java.util.ArrayDeque; 36 | import java.util.Deque; 37 | import java.util.logging.Level; 38 | 39 | public class PluginMessageConnector extends BukkitConnector implements PluginMessageListener, Listener { 40 | 41 | private final Deque queue = new ArrayDeque<>(); 42 | 43 | public PluginMessageConnector(BukkitConnectorPlugin plugin) { 44 | super(plugin, true); 45 | plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, plugin.getMessageChannel()); 46 | plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, plugin.getMessageChannel(), this); 47 | plugin.getServer().getPluginManager().registerEvents(this, plugin); 48 | } 49 | 50 | @Override 51 | public void onPluginMessageReceived(String channel, Player player, byte[] data) { 52 | if (!channel.equals(plugin.getMessageChannel())) { 53 | return; 54 | } 55 | 56 | ByteArrayDataInput in = ByteStreams.newDataInput(data); 57 | String group = in.readUTF(); 58 | 59 | String target = in.readUTF(); 60 | if (!isThis(target)) { 61 | return; 62 | } 63 | 64 | if (target.startsWith(PLAYER_PREFIX)) { 65 | String playerName = target.substring(PLAYER_PREFIX.length()); 66 | player = getReceiver(playerName); 67 | if (player == null) { 68 | plugin.logError("Player " + playerName + " wasn't found online?"); 69 | return; 70 | } 71 | } 72 | 73 | int messageLength = in.readInt(); 74 | byte[] messageData = new byte[messageLength]; 75 | in.readFully(messageData); 76 | 77 | try { 78 | handle( player, Message.fromByteArray(group, messageData)); 79 | } catch (IllegalArgumentException e) { 80 | plugin.logError("Invalid message target! " + e.getMessage()); 81 | } catch (VersionMismatchException e) { 82 | plugin.getLogger().log(Level.WARNING, e.getMessage() + ". Ignoring message!"); 83 | } 84 | } 85 | 86 | @Override 87 | public void sendDataImplementation(String targetData, Message message) { 88 | byte[] messageData = message.writeToByteArray(); 89 | 90 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 91 | out.writeUTF(message.getGroup()); 92 | out.writeUTF(targetData); 93 | out.writeInt(messageData.length); 94 | out.write(messageData); 95 | byte[] dataToSend = out.toByteArray(); 96 | 97 | Player player = null; 98 | if (!targetData.startsWith("server:")) { 99 | player = plugin.getServer().getPlayerExact(targetData); 100 | } 101 | if (player != null) { 102 | player.sendPluginMessage(plugin, plugin.getMessageChannel(), dataToSend); 103 | } else { 104 | if (plugin.getServer().getOnlinePlayers().isEmpty()) { 105 | queue.add(dataToSend); 106 | } else { 107 | plugin.getServer().getOnlinePlayers().iterator().next().sendPluginMessage(plugin, plugin.getMessageChannel(), dataToSend); 108 | } 109 | } 110 | } 111 | 112 | @EventHandler(priority = EventPriority.MONITOR) 113 | public void onPlayerJoin(PlayerJoinEvent event) { 114 | while (!queue.isEmpty()) { 115 | event.getPlayer().sendPluginMessage(plugin, plugin.getMessageChannel(), queue.remove()); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /bukkit/src/main/java/de/themoep/connectorplugin/bukkit/connector/RedisConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bukkit.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.RedisConnection; 24 | 25 | public class RedisConnector extends BukkitConnector { 26 | private final RedisConnection connection; 27 | 28 | public RedisConnector(BukkitConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new RedisConnection( 31 | plugin, 32 | plugin.getConfig().getString("redis.uri"), 33 | plugin.getConfig().getString("redis.host"), 34 | plugin.getConfig().getInt("redis.port"), 35 | plugin.getConfig().getInt("redis.db"), 36 | plugin.getConfig().getString("redis.password"), 37 | plugin.getConfig().getLong("redis.timeout"), 38 | (receiver, message) -> plugin.runSync(() -> handle(receiver, message)) 39 | ); 40 | } 41 | 42 | @Override 43 | protected void sendDataImplementation(String targetData, Message message) { 44 | connection.sendMessage(targetData, message); 45 | } 46 | 47 | @Override 48 | public void close() { 49 | connection.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bukkit/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | # Plugin debugging 2 | debug: true 3 | 4 | # Currently supported types: 5 | # - "plugin_messages" (will not be able to send messages from server without any players online) 6 | # - "redis" (does not require online players) 7 | # - "mqtt" (does not require online players) 8 | messenger-type: redis 9 | 10 | # The group this server is in 11 | group: global 12 | 13 | # Put specific plugins into different groupings 14 | plugin-groups: 15 | pluginname: specialgroup 16 | 17 | # Should be the same name as in the proxy config 18 | server-name: changeme 19 | 20 | # Redis messenger settings 21 | redis: 22 | host: localhost 23 | port: 6379 24 | db: 0 25 | timeout: 60 26 | password: password1 27 | 28 | # Mqtt messenger settings 29 | mqtt: 30 | broker-uri: tcp://localhost:1883 31 | keep-alive: 30 32 | username: mqttuser 33 | password: password1 -------------------------------------------------------------------------------- /bukkit/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ConnectorPlugin 2 | version: ${minecraft.plugin.version} 3 | main: de.themoep.connectorplugin.bukkit.BukkitConnectorPlugin 4 | api-version: 1.13 5 | authors: [Phoenix616] 6 | commands: 7 | connectorplugin: 8 | description: Connector plugin command 9 | permission: connectorplugin.command 10 | usage: / teleport|teleportoplayer|servercommand |proxyplayercommand |proxycommand 11 | aliases: 12 | - connector 13 | - connectorcommand 14 | - connplugin 15 | - cp -------------------------------------------------------------------------------- /bungee/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | de.themoep.connectorplugin 9 | parent 10 | 1.5.2-SNAPSHOT 11 | 12 | 13 | bungee 14 | jar 15 | 16 | 17 | 18 | sonatype-oss-repo 19 | https://oss.sonatype.org/content/groups/public/ 20 | 21 | 22 | 23 | 24 | 25 | net.md-5 26 | bungeecord-api 27 | 28 | 29 | de.themoep 30 | bungeeplugin 31 | 32 | 33 | ${groupId} 34 | core 35 | ${project.version} 36 | 37 | 38 | 39 | 40 | clean package 41 | ${project.name} 42 | 43 | 44 | src/main/resources 45 | true 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/BungeeConnectorPlugin.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.bungeeplugin.BungeePlugin; 22 | import de.themoep.connectorplugin.ConnectorPlugin; 23 | import de.themoep.connectorplugin.bungee.commands.ConnectorCommand; 24 | import de.themoep.connectorplugin.bungee.connector.BungeeConnector; 25 | import de.themoep.connectorplugin.bungee.connector.MqttConnector; 26 | import de.themoep.connectorplugin.bungee.connector.PluginMessageConnector; 27 | import de.themoep.connectorplugin.bungee.connector.RedisConnector; 28 | import de.themoep.connectorplugin.connector.MessageTarget; 29 | import net.md_5.bungee.api.connection.ProxiedPlayer; 30 | 31 | import java.util.Collections; 32 | import java.util.Locale; 33 | import java.util.Map; 34 | import java.util.logging.Level; 35 | 36 | import static de.themoep.connectorplugin.connector.Connector.PROXY_ID_PREFIX; 37 | 38 | public final class BungeeConnectorPlugin extends BungeePlugin implements ConnectorPlugin { 39 | 40 | private BungeeConnector connector; 41 | private Bridge bridge; 42 | private boolean debug = true; 43 | 44 | @Override 45 | public void onEnable() { 46 | debug = getConfig().getBoolean("debug"); 47 | 48 | String messengerType = getConfig().getString("messenger-type", "plugin_messages").toLowerCase(Locale.ROOT); 49 | switch (messengerType) { 50 | default: 51 | getLogger().log(Level.WARNING, "Messenger type '" + messengerType + "' is not supported, falling back to plugin messages!"); 52 | case "plugin_messages": 53 | connector = new PluginMessageConnector(this); 54 | getLogger().log(Level.WARNING, "Using plugin messages as the messenger type will come with" + 55 | " some caveats like sending to servers without players or to" + 56 | " other proxies not working!"); 57 | getLogger().log(Level.WARNING, "Please consider using one of the other messenger types!"); 58 | break; 59 | case "redis": 60 | connector = new RedisConnector(this); 61 | break; 62 | case "mqtt": 63 | connector = new MqttConnector(this); 64 | break; 65 | } 66 | 67 | getProxy().getPluginManager().registerCommand(this, new ConnectorCommand(this)); 68 | 69 | bridge = new Bridge(this); 70 | } 71 | 72 | @Override 73 | public void onDisable() { 74 | connector.close(); 75 | } 76 | 77 | @Override 78 | public BungeeConnector getConnector() { 79 | return connector; 80 | } 81 | 82 | /** 83 | * Get the bridge helper class for executing certain actions on other servers 84 | * @return The bridge helper 85 | */ 86 | public Bridge getBridge() { 87 | return bridge; 88 | } 89 | 90 | @Override 91 | public void runAsync(Runnable runnable) { 92 | getProxy().getScheduler().runAsync(this, runnable); 93 | } 94 | 95 | @Override 96 | public MessageTarget.Type getSourceType() { 97 | return MessageTarget.Type.PROXY; 98 | } 99 | 100 | @Override 101 | public void logDebug(String message, Throwable... throwables) { 102 | if (debug) { 103 | getLogger().log(Level.INFO, "[DEBUG] " + message, throwables.length > 0 ? throwables[0] : null); 104 | } 105 | } 106 | 107 | @Override 108 | public void logInfo(String message, Throwable... throwables) { 109 | getLogger().log(Level.INFO, message, throwables.length > 0 ? throwables[0] : null); 110 | } 111 | 112 | @Override 113 | public void logWarning(String message, Throwable... throwables) { 114 | getLogger().log(Level.WARNING, message, throwables.length > 0 ? throwables[0] : null); 115 | } 116 | 117 | @Override 118 | public void logError(String message, Throwable... throwables) { 119 | getLogger().log(Level.SEVERE, message, throwables.length > 0 ? throwables[0] : null); 120 | } 121 | 122 | @Override 123 | public String getServerName() { 124 | return PROXY_ID_PREFIX + getProxy().getConfig().getUuid(); 125 | } 126 | 127 | @Override 128 | public String getGlobalGroup() { 129 | return ""; 130 | } 131 | 132 | @Override 133 | public Map getGroups() { 134 | return Collections.emptyMap(); 135 | } 136 | 137 | @Override 138 | public String getName() { 139 | return getDescription().getName(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/ConnectorCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | 22 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 23 | 24 | public class ConnectorCommand extends SubCommand { 25 | 26 | public ConnectorCommand(BungeeConnectorPlugin plugin) { 27 | super(plugin, "connectorpluginbungee"); 28 | registerSubCommand(new TeleportCommand(this)); 29 | registerSubCommand(new TeleportToPlayerCommand(this)); 30 | registerSubCommand(new ServerConsoleCommand(this)); 31 | registerSubCommand(new ServerPlayerCommand(this)); 32 | registerSubCommand(new ProxyConsoleCommand(this)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/ProxyConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.bungeeplugin.PluginCommand; 22 | import net.md_5.bungee.api.ChatColor; 23 | import net.md_5.bungee.api.CommandSender; 24 | import net.md_5.bungee.api.plugin.Command; 25 | 26 | import java.util.Collections; 27 | import java.util.Locale; 28 | import java.util.Map; 29 | import java.util.stream.Collectors; 30 | 31 | public class ProxyConsoleCommand extends SubCommand { 32 | 33 | public ProxyConsoleCommand(ConnectorCommand parent) { 34 | super(parent.getPlugin(), "proxycommand ", parent.getPermission() + ".proxycommand", "proxyconsole", "proxyconsolecommand", "proxy", "pcc"); 35 | } 36 | 37 | @Override 38 | public boolean run(CommandSender sender, String[] args) { 39 | if (args.length < 1) { 40 | return false; 41 | } 42 | 43 | String commandString = String.join(" ", args); 44 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on other proxies"); 45 | plugin.getBridge().runProxyConsoleCommand(commandString, sender::sendMessage).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 46 | return true; 47 | } 48 | 49 | @Override 50 | public Iterable onTabComplete(CommandSender sender, String[] args) { 51 | if (args.length == 0) { 52 | return plugin.getProxy().getPluginManager().getCommands().stream() 53 | .map(Map.Entry::getValue) 54 | .filter(e -> e instanceof PluginCommand ? ((PluginCommand) e).hasCommandPermission(sender) : e.hasPermission(sender)) 55 | .map(Command::getName) 56 | .sorted(String::compareToIgnoreCase) 57 | .collect(Collectors.toList()); 58 | } else if (args.length == 1) { 59 | return plugin.getProxy().getPluginManager().getCommands().stream() 60 | .map(Map.Entry::getValue) 61 | .filter(e -> e instanceof PluginCommand ? ((PluginCommand) e).hasCommandPermission(sender) : e.hasPermission(sender)) 62 | .map(Command::getName) 63 | .filter(s -> s.toLowerCase(Locale.ROOT).startsWith(args[0].toLowerCase(Locale.ROOT))) 64 | .sorted(String::compareToIgnoreCase) 65 | .collect(Collectors.toList()); 66 | } 67 | return Collections.emptySet(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/ServerConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import net.md_5.bungee.api.ChatColor; 22 | import net.md_5.bungee.api.CommandSender; 23 | import net.md_5.bungee.api.connection.ProxiedPlayer; 24 | 25 | import java.util.Arrays; 26 | import java.util.Collections; 27 | import java.util.Locale; 28 | import java.util.stream.Collectors; 29 | 30 | public class ServerConsoleCommand extends SubCommand { 31 | 32 | public ServerConsoleCommand(ConnectorCommand parent) { 33 | super(parent.getPlugin(), "servercommand ", parent.getPermission() + ".servercommand", "serverconsole", "serverconsolecommand", "server", "scc"); 34 | } 35 | 36 | @Override 37 | public boolean run(CommandSender sender, String[] args) { 38 | if (args.length < 2) { 39 | return false; 40 | } 41 | 42 | String serverName = args[0]; 43 | if (serverName.startsWith("p:")) { 44 | ProxiedPlayer player = plugin.getProxy().getPlayer(serverName.substring(2)); 45 | if (player != null) { 46 | if (player.getServer() != null) { 47 | serverName = player.getServer().getInfo().getName(); 48 | } else { 49 | sender.sendMessage(ChatColor.RED + "Player '" + player.getName() + "' is not connected to any server?"); 50 | return false; 51 | } 52 | } else { 53 | sender.sendMessage(ChatColor.RED + "The player '" + serverName.substring(2) + "' is not online?"); 54 | return false; 55 | } 56 | } else if (plugin.getProxy().getServerInfo(serverName) == null) { 57 | sender.sendMessage(ChatColor.GRAY + "There is no server with the name of '" + serverName + "' on the proxy. Trying to send command anyways..."); 58 | } 59 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 60 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on server '" + serverName + "'"); 61 | plugin.getBridge().runServerConsoleCommand(serverName, commandString, sender::sendMessage).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 62 | return true; 63 | } 64 | 65 | @Override 66 | public Iterable onTabComplete(CommandSender sender, String[] args) { 67 | if (!hasCommandPermission(sender)) { 68 | return Collections.emptySet(); 69 | } 70 | if (args.length == 0) { 71 | return plugin.getProxy().getServers().keySet(); 72 | } else if (args.length == 1) { 73 | return plugin.getProxy().getServers().keySet().stream().filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 74 | } 75 | return Collections.emptySet(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/ServerPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import net.md_5.bungee.api.ChatColor; 22 | import net.md_5.bungee.api.CommandSender; 23 | import net.md_5.bungee.api.connection.ProxiedPlayer; 24 | 25 | import java.util.Arrays; 26 | import java.util.Collections; 27 | import java.util.List; 28 | import java.util.Locale; 29 | import java.util.stream.Collectors; 30 | 31 | public class ServerPlayerCommand extends SubCommand { 32 | 33 | public ServerPlayerCommand(ConnectorCommand parent) { 34 | super(parent.getPlugin(), "serverplayercommand ", parent.getPermission() + ".serverplayercommand", "serverplayer", "player", "spc"); 35 | } 36 | 37 | @Override 38 | public boolean run(CommandSender sender, String[] args) { 39 | if (args.length < 2) { 40 | return false; 41 | } 42 | 43 | ProxiedPlayer player = plugin.getProxy().getPlayer(args[0]); 44 | if (player == null) { 45 | sender.sendMessage("No player with the name " + args[0] + " is online on this server!"); 46 | return true; 47 | } 48 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 49 | sender.sendMessage(ChatColor.GRAY + "Executing '" + commandString + "' on the server for player '" + player.getName() + "'"); 50 | plugin.getBridge().runServerPlayerCommand(player, commandString).thenAccept(success -> sender.sendMessage(success ? "Successfully executed command!" : "Error while executing the command.")); 51 | return true; 52 | } 53 | 54 | @Override 55 | public Iterable onTabComplete(CommandSender sender, String[] args) { 56 | if (!hasCommandPermission(sender)) { 57 | return Collections.emptySet(); 58 | } 59 | if (args.length == 0) { 60 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 61 | } else if (args.length == 1) { 62 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 63 | } 64 | return Collections.emptySet(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/SubCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | /* 3 | * ConnectorPlugin 4 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import de.themoep.bungeeplugin.PluginCommand; 21 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 22 | import net.md_5.bungee.api.CommandSender; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.Collections; 27 | import java.util.LinkedHashMap; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.Map; 31 | 32 | public class SubCommand extends PluginCommand { 33 | private Map subCommands = new LinkedHashMap<>(); 34 | private Map subCommandAliases = new LinkedHashMap<>(); 35 | 36 | public SubCommand(BungeeConnectorPlugin plugin, String name) { 37 | super(plugin, name); 38 | } 39 | 40 | public SubCommand(BungeeConnectorPlugin plugin, String usage, String permission, String... aliases) { 41 | super(plugin, usage.split(" ", 2)[0], permission, null, null, "/" + usage, aliases); 42 | } 43 | 44 | public void registerSubCommand(SubCommand subCommand) { 45 | subCommands.put(subCommand.getName().toLowerCase(Locale.ROOT), subCommand); 46 | for (String alias : subCommand.getAliases()) { 47 | subCommandAliases.put(alias.toLowerCase(Locale.ROOT), subCommand); 48 | } 49 | } 50 | 51 | public SubCommand getSubCommand(String name) { 52 | SubCommand subCommand = subCommands.get(name.toLowerCase(Locale.ROOT)); 53 | if (subCommand == null) { 54 | return subCommandAliases.get(name.toLowerCase(Locale.ROOT)); 55 | } 56 | return subCommand; 57 | } 58 | 59 | @Override 60 | protected boolean run(CommandSender sender, String[] args) { 61 | if (args.length == 0) { 62 | return false; 63 | } 64 | SubCommand subCommand = getSubCommand(args[0]); 65 | if (subCommand != null) { 66 | subCommand.execute(sender, Arrays.copyOfRange(args, 1, args.length)); 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | @Override 73 | public Iterable onTabComplete(CommandSender sender, String[] args) { 74 | if (!hasCommandPermission(sender)) { 75 | return Collections.emptySet(); 76 | } 77 | if (args.length == 0 || args[0].isEmpty()) { 78 | return new ArrayList<>(subCommands.keySet()); 79 | } 80 | SubCommand subCommand = getSubCommand(args[0]); 81 | if (subCommand != null && subCommand.hasCommandPermission(sender)) { 82 | return subCommand.onTabComplete(sender, Arrays.copyOfRange(args, 1, args.length)); 83 | } 84 | List completions = new ArrayList<>(); 85 | for (Map.Entry e : subCommands.entrySet()) { 86 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && e.getValue().hasCommandPermission(sender)) { 87 | completions.add(e.getKey()); 88 | } 89 | } 90 | for (Map.Entry e : subCommandAliases.entrySet()) { 91 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && e.getValue().hasCommandPermission(sender)) { 92 | completions.add(e.getKey()); 93 | } 94 | } 95 | return completions; 96 | } 97 | 98 | public Map getSubCommands() { 99 | return subCommands; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/TeleportCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.LocationInfo; 22 | import net.md_5.bungee.api.ChatColor; 23 | import net.md_5.bungee.api.CommandSender; 24 | import net.md_5.bungee.api.config.ServerInfo; 25 | import net.md_5.bungee.api.connection.ProxiedPlayer; 26 | 27 | import java.util.Collections; 28 | import java.util.Locale; 29 | import java.util.stream.Collectors; 30 | 31 | public class TeleportCommand extends SubCommand { 32 | public TeleportCommand(ConnectorCommand parent) { 33 | super(parent.getPlugin(), "teleport [ [ ]]", parent.getPermission() + ".teleport", "tp", "send"); 34 | } 35 | 36 | @Override 37 | public boolean run(CommandSender sender, String[] args) { 38 | if (args.length < 2) { 39 | return false; 40 | } 41 | 42 | ProxiedPlayer player = plugin.getProxy().getPlayer(args[0]); 43 | if (player == null) { 44 | sender.sendMessage(ChatColor.RED + "No player with the name " + args[0] + " found!"); 45 | return true; 46 | } 47 | 48 | ServerInfo server = plugin.getProxy().getServerInfo(args[1]); 49 | if (server == null) { 50 | sender.sendMessage(ChatColor.RED + "No server with the name " + args[1] + " found!"); 51 | return true; 52 | } 53 | 54 | if (args.length == 2) { 55 | player.connect(server, (success, ex) -> { 56 | if (success) { 57 | sender.sendMessage(ChatColor.GREEN + "Connected player " + player.getName() + " to server " + server.getName()); 58 | } else { 59 | sender.sendMessage(ChatColor.RED + "Error while connecting player " + player.getName() + " to server " + server.getName() + ": " + ex.getMessage()); 60 | } 61 | }); 62 | return true; 63 | } 64 | 65 | if (args.length == 3) { 66 | plugin.getBridge().teleport(player.getName(), server.getName(), args[2], sender::sendMessage) 67 | .thenAccept(success -> { 68 | if (!success) { 69 | sender.sendMessage(ChatColor.RED + "Error while teleporting..."); 70 | } 71 | }); 72 | return true; 73 | } 74 | 75 | if (args.length < 6) { 76 | return false; 77 | } 78 | 79 | try { 80 | LocationInfo location = new LocationInfo( 81 | server.getName(), 82 | args[2], 83 | Double.parseDouble(args[3]), 84 | Double.parseDouble(args[4]), 85 | Double.parseDouble(args[5]), 86 | args.length > 6 ? Float.parseFloat(args[6]) : 0, 87 | args.length > 7 ? Float.parseFloat(args[7]) : 0 88 | ); 89 | 90 | plugin.getBridge().teleport(player.getName(), location, sender::sendMessage) 91 | .thenAccept(success -> { 92 | if (!success) { 93 | sender.sendMessage(ChatColor.RED + "Error while teleporting..."); 94 | } 95 | }); 96 | return true; 97 | } catch (IllegalArgumentException e) { 98 | sender.sendMessage(ChatColor.RED + "Error while parsing input! " + e.getMessage()); 99 | return false; 100 | } 101 | } 102 | 103 | @Override 104 | public Iterable onTabComplete(CommandSender sender, String[] args) { 105 | if (!hasCommandPermission(sender)) { 106 | return Collections.emptySet(); 107 | } 108 | if (args.length == 0) { 109 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 110 | } else if (args.length == 1) { 111 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 112 | } else if (args.length == 2) { 113 | return plugin.getProxy().getServers().values().stream().filter(s -> s.canAccess(sender)).map(ServerInfo::getName).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 114 | } 115 | return Collections.emptySet(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/commands/TeleportToPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import net.md_5.bungee.api.ChatColor; 22 | import net.md_5.bungee.api.CommandSender; 23 | import net.md_5.bungee.api.connection.ProxiedPlayer; 24 | 25 | import java.util.Collections; 26 | import java.util.Locale; 27 | import java.util.stream.Collectors; 28 | 29 | public class TeleportToPlayerCommand extends SubCommand { 30 | public TeleportToPlayerCommand(ConnectorCommand parent) { 31 | super(parent.getPlugin(), "teleporttoplayer []", parent.getPermission() + ".teleporttoplayer", "teleportplayer"); 32 | } 33 | 34 | @Override 35 | public boolean run(CommandSender sender, String[] args) { 36 | String playerName; 37 | String targetName; 38 | if (args.length == 1 && sender instanceof ProxiedPlayer) { 39 | playerName = sender.getName(); 40 | targetName = args[0]; 41 | } else if (args.length == 2) { 42 | playerName = args[0]; 43 | targetName = args[1]; 44 | } else { 45 | return false; 46 | } 47 | 48 | ProxiedPlayer player = plugin.getProxy().getPlayer(playerName); 49 | if (player == null) { 50 | sender.sendMessage(ChatColor.RED + "No player with the name " + playerName + " found!"); 51 | return true; 52 | } 53 | 54 | ProxiedPlayer target = plugin.getProxy().getPlayer(targetName); 55 | if (target == null) { 56 | sender.sendMessage(ChatColor.RED + "No player with the name " + targetName + " found!"); 57 | return true; 58 | } 59 | 60 | plugin.getBridge().teleport(player.getName(), target.getName(), sender::sendMessage) 61 | .thenAccept(success -> { 62 | if (!success) { 63 | sender.sendMessage(ChatColor.RED + "Error while teleporting..."); 64 | } 65 | }); 66 | return true; 67 | } 68 | 69 | @Override 70 | public Iterable onTabComplete(CommandSender sender, String[] args) { 71 | if (!hasCommandPermission(sender)) { 72 | return Collections.emptySet(); 73 | } 74 | if (args.length == 0) { 75 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 76 | } else if (args.length == 1) { 77 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 78 | } else if (args.length == 2) { 79 | return plugin.getProxy().getPlayers().stream().map(ProxiedPlayer::getName).filter(s -> s.startsWith(args[1].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 80 | } 81 | return Collections.emptySet(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/connector/BungeeConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Connector; 23 | import de.themoep.connectorplugin.connector.Message; 24 | import net.md_5.bungee.api.config.ServerInfo; 25 | import net.md_5.bungee.api.connection.ProxiedPlayer; 26 | 27 | public abstract class BungeeConnector extends Connector { 28 | 29 | public BungeeConnector(BungeeConnectorPlugin plugin, boolean requiresPlayer) { 30 | super(plugin, requiresPlayer); 31 | } 32 | 33 | protected ProxiedPlayer getReceiverImplementation(String name) { 34 | return plugin.getProxy().getPlayer(name); 35 | } 36 | 37 | protected ServerInfo getTargetServer(String target) { 38 | if (target.startsWith(SERVER_PREFIX)) { 39 | return plugin.getProxy().getServerInfo(target.substring(SERVER_PREFIX.length())); 40 | } else if (target.startsWith(PLAYER_PREFIX)) { 41 | ProxiedPlayer player = getReceiver(target.substring(PLAYER_PREFIX.length())); 42 | if (player != null && player.getServer() != null) { 43 | return player.getServer().getInfo(); 44 | } 45 | } 46 | return null; 47 | } 48 | 49 | @Override 50 | protected void sendDataImplementation(Object targetData, Message message) { 51 | sendDataImplementation(targetData instanceof String 52 | ? (hasPrefix((String) targetData) 53 | ? (String) targetData 54 | : SERVER_PREFIX + targetData) 55 | : (targetData instanceof ProxiedPlayer 56 | ? PLAYER_PREFIX + ((ProxiedPlayer) targetData).getName() 57 | : ""), message); 58 | } 59 | 60 | protected abstract void sendDataImplementation(String targetData, Message message); 61 | } 62 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/connector/MqttConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.MqttConnection; 24 | 25 | public class MqttConnector extends BungeeConnector { 26 | private final MqttConnection connection; 27 | 28 | public MqttConnector(BungeeConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new MqttConnection( 31 | plugin, 32 | plugin.getConfig().getString("mqtt.broker-uri"), 33 | plugin.getConfig().getString("mqtt.client-id", null), 34 | plugin.getConfig().getString("mqtt.username"), 35 | plugin.getConfig().getString("mqtt.password"), 36 | plugin.getConfig().getInt("mqtt.keep-alive"), 37 | this::handle 38 | ); 39 | } 40 | 41 | @Override 42 | protected void sendDataImplementation(String targetData, Message message) { 43 | connection.sendMessage(targetData, message); 44 | } 45 | 46 | @Override 47 | public void close() { 48 | connection.close(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/connector/PluginMessageConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.common.io.ByteArrayDataInput; 22 | import com.google.common.io.ByteArrayDataOutput; 23 | import com.google.common.io.ByteStreams; 24 | import de.themoep.connectorplugin.connector.Message; 25 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 26 | import de.themoep.connectorplugin.connector.VersionMismatchException; 27 | import net.md_5.bungee.api.config.ServerInfo; 28 | import net.md_5.bungee.api.connection.ProxiedPlayer; 29 | import net.md_5.bungee.api.event.PluginMessageEvent; 30 | import net.md_5.bungee.api.plugin.Listener; 31 | import net.md_5.bungee.event.EventHandler; 32 | 33 | import java.util.logging.Level; 34 | 35 | public class PluginMessageConnector extends BungeeConnector implements Listener { 36 | 37 | public PluginMessageConnector(BungeeConnectorPlugin plugin) { 38 | super(plugin, true); 39 | plugin.getProxy().registerChannel(plugin.getMessageChannel()); 40 | } 41 | 42 | @EventHandler 43 | public void onPluginMessage(PluginMessageEvent event) { 44 | if (event.isCancelled() || !event.getTag().equals(plugin.getMessageChannel())) { 45 | return; 46 | } 47 | 48 | event.setCancelled(true); 49 | if (event.getSender() instanceof ProxiedPlayer) { 50 | return; 51 | } 52 | 53 | ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); 54 | String group = in.readUTF(); 55 | String target = in.readUTF(); 56 | 57 | int messageLength = in.readInt(); 58 | byte[] messageData = new byte[messageLength]; 59 | in.readFully(messageData); 60 | try { 61 | Message message = Message.fromByteArray(group, messageData); 62 | switch (message.getTarget()) { 63 | case ALL_WITH_PLAYERS: 64 | sendToAllWithPlayers(event.getData(), null); 65 | break; 66 | case ALL_QUEUE: 67 | sendToAllAndQueue(event.getData(), null); 68 | break; 69 | case OTHERS_WITH_PLAYERS: 70 | sendToAllWithPlayers(event.getData(), ((ProxiedPlayer) event.getSender()).getServer().getInfo()); 71 | break; 72 | case OTHERS_QUEUE: 73 | sendToAllAndQueue(event.getData(), ((ProxiedPlayer) event.getSender()).getServer().getInfo()); 74 | break; 75 | case PROXY: 76 | case ALL_PROXIES: 77 | handle(target, message); 78 | break; 79 | case SERVER: 80 | if (!target.isEmpty()) { 81 | ServerInfo server = getTargetServer(target); 82 | if (server != null) { 83 | server.sendData(plugin.getMessageChannel(), event.getData(), true); 84 | } else { 85 | plugin.logDebug(target + " doesn't exist?"); 86 | } 87 | } else { 88 | plugin.logError(message.getTarget() + " message target requires explicit target!"); 89 | } 90 | break; 91 | default: 92 | plugin.logError("Receiving " + message.getTarget() + " is not supported!"); 93 | } 94 | } catch (IllegalArgumentException e) { 95 | plugin.logError("Invalid message target! " + e.getMessage()); 96 | } catch (VersionMismatchException e) { 97 | plugin.getLogger().log(Level.WARNING, e.getMessage() + ". Ignoring message!"); 98 | } 99 | } 100 | 101 | private void sendToAllWithPlayers(byte[] data, ServerInfo excludedServer) { 102 | sendToAll(data, false, excludedServer); 103 | } 104 | 105 | private void sendToAllAndQueue(byte[] data, ServerInfo excludedServer) { 106 | sendToAll(data, true, excludedServer); 107 | } 108 | 109 | private void sendToAll(byte[] data, boolean queue, ServerInfo excludedServer) { 110 | for (ServerInfo server : plugin.getProxy().getServers().values()) { 111 | if (excludedServer == null || excludedServer != server) { 112 | server.sendData(plugin.getMessageChannel(), data, queue); 113 | } 114 | } 115 | } 116 | 117 | @Override 118 | public void sendDataImplementation(String targetData, Message message) { 119 | byte[] messageData = message.writeToByteArray(); 120 | 121 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 122 | out.writeUTF(message.getGroup()); 123 | out.writeUTF(targetData); 124 | out.writeInt(messageData.length); 125 | out.write(messageData); 126 | byte[] dataToSend = out.toByteArray(); 127 | 128 | ServerInfo server = getTargetServer(targetData); 129 | 130 | switch (message.getTarget()) { 131 | case ALL_WITH_PLAYERS: 132 | sendToAllWithPlayers(dataToSend, null); 133 | break; 134 | case ALL_QUEUE: 135 | sendToAllAndQueue(dataToSend, null); 136 | break; 137 | case OTHERS_WITH_PLAYERS: 138 | sendToAllWithPlayers(dataToSend, server); 139 | break; 140 | case OTHERS_QUEUE: 141 | sendToAllAndQueue(dataToSend, server); 142 | break; 143 | case SERVER: 144 | if (server != null) { 145 | server.sendData(plugin.getMessageChannel(), dataToSend); 146 | } else { 147 | throw new UnsupportedOperationException("Could not send data to " + message.getTarget() + " as target server wasn't found from " + targetData + "!"); 148 | } 149 | break; 150 | default: 151 | throw new UnsupportedOperationException("Sending to " + message.getTarget() + " is not supported!"); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /bungee/src/main/java/de/themoep/connectorplugin/bungee/connector/RedisConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.bungee.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.bungee.BungeeConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.RedisConnection; 24 | 25 | public class RedisConnector extends BungeeConnector { 26 | private final RedisConnection connection; 27 | 28 | public RedisConnector(BungeeConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new RedisConnection( 31 | plugin, 32 | plugin.getConfig().getString("redis.uri"), 33 | plugin.getConfig().getString("redis.host"), 34 | plugin.getConfig().getInt("redis.port"), 35 | plugin.getConfig().getInt("redis.db"), 36 | plugin.getConfig().getString("redis.password"), 37 | plugin.getConfig().getLong("redis.timeout"), 38 | this::handle 39 | ); 40 | } 41 | 42 | @Override 43 | protected void sendDataImplementation(String targetData, Message message) { 44 | connection.sendMessage(targetData, message); 45 | } 46 | 47 | @Override 48 | public void close() { 49 | connection.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bungee/src/main/resources/bungee-config.yml: -------------------------------------------------------------------------------- 1 | # Plugin debugging 2 | debug: true 3 | 4 | # Currently supported types: 5 | # - "plugin_messages" (will not be able to send messages to other proxies or servers without any players online, messages will be queued) 6 | # - "redis" (does not require any online players) 7 | # - "mqtt" (does not require any online players) 8 | messenger-type: redis 9 | 10 | # Redis messenger settings 11 | redis: 12 | host: localhost 13 | port: 6379 14 | db: 0 15 | timeout: 60 16 | password: password1 17 | 18 | # Mqtt messenger settings 19 | mqtt: 20 | broker-uri: tcp://localhost:1883 21 | keep-alive: 30 22 | username: mqttuser 23 | password: password1 -------------------------------------------------------------------------------- /bungee/src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: ConnectorPlugin 2 | version: ${minecraft.plugin.version} 3 | main: de.themoep.connectorplugin.bungee.BungeeConnectorPlugin 4 | author: Phoenix616 5 | commands: 6 | connectorpluginbungee: 7 | description: Connector plugin command 8 | permission: connectorplugin.command 9 | usage: / teleport|teleporttoplayer|servercommand |playercommand 10 | aliases: 11 | - connectorbungee 12 | - connectorcommandbungee 13 | - connpluginbungee 14 | - cpb -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | parent 7 | de.themoep.connectorplugin 8 | 1.5.2-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | core 13 | 14 | 15 | 16 | 17 | com.google.guava 18 | guava 19 | 20 | 21 | io.lettuce 22 | lettuce-core 23 | 24 | 25 | org.eclipse.paho 26 | org.eclipse.paho.mqttv5.client 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/BridgedCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.connector.ConnectingPlugin; 22 | 23 | import java.util.Collections; 24 | import java.util.List; 25 | import java.util.concurrent.CompletableFuture; 26 | 27 | public abstract class BridgedCommand

{ 28 | private final P plugin; 29 | private final String name; 30 | private final String[] aliases; 31 | private final String permission; 32 | private final String permissionMessage; 33 | private final String description; 34 | private final String usage; 35 | 36 | /** 37 | * A new bridged command 38 | * @param plugin The plugin that this command is from 39 | * @param name The name of the command 40 | */ 41 | public BridgedCommand(P plugin, String name) { 42 | this(plugin, name, null); 43 | } 44 | 45 | /** 46 | * A new bridged command 47 | * @param plugin The plugin that this command is from 48 | * @param name The name of the command 49 | * @param permission The permission of this command 50 | */ 51 | public BridgedCommand(P plugin, String name, String permission) { 52 | this(plugin, name, permission, "", "", "/" + name); 53 | } 54 | 55 | /** 56 | * A new bridged command 57 | * @param plugin The plugin that this command is from 58 | * @param name The name of the command 59 | * @param permission The permission of this command 60 | * @param description The description of this command 61 | * @param usage How the command can be used by the player 62 | * @param aliases An optional array of aliases for the command 63 | */ 64 | public BridgedCommand(P plugin, String name, String permission, String permissionMessage, String description, String usage, String... aliases) { 65 | this.plugin = plugin; 66 | this.name = name; 67 | this.aliases = aliases; 68 | this.permission = permission; 69 | this.permissionMessage = permissionMessage; 70 | this.description = description; 71 | this.usage = usage; 72 | } 73 | 74 | public P getPlugin() { 75 | return plugin; 76 | } 77 | 78 | public String getName() { 79 | return name; 80 | } 81 | 82 | public String[] getAliases() { 83 | return aliases; 84 | } 85 | 86 | public String getDescription() { 87 | return description; 88 | } 89 | 90 | public String getUsage() { 91 | return usage; 92 | } 93 | 94 | public String getPermission() { 95 | return permission; 96 | } 97 | 98 | public String getPermissionMessage() { 99 | return permissionMessage; 100 | } 101 | 102 | public abstract boolean onCommand(S sender, LocationInfo location, String label, String[] args); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/BridgedSuggestions.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | /* 3 | * ConnectorPlugin 4 | * Copyright (C) 2022 Max Lee aka Phoenix616 (max@themoep.de) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import java.util.Collections; 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | 24 | public interface BridgedSuggestions { 25 | 26 | public default List suggest(S sender, String label, String[] args) { 27 | return Collections.emptyList(); 28 | } 29 | 30 | public default CompletableFuture> suggestAsync(S sender, String label, String[] args) { 31 | return CompletableFuture.completedFuture(suggest(sender, label, args)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/ConnectorPlugin.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.connector.ConnectingPlugin; 22 | import de.themoep.connectorplugin.connector.Connector; 23 | import de.themoep.connectorplugin.connector.MessageTarget; 24 | 25 | import java.util.Locale; 26 | import java.util.Map; 27 | 28 | public interface ConnectorPlugin extends ConnectingPlugin { 29 | 30 | /** 31 | * Get the Connector which is used for sending and handling data 32 | * @return The Connector 33 | */ 34 | Connector getConnector(); 35 | 36 | /** 37 | * The type of source that this plugin is. {@link MessageTarget.Type#SERVER} or {@link MessageTarget.Type#PROXY} 38 | * @return The type of {@link MessageTarget.Type} that this implementation provides. 39 | */ 40 | MessageTarget.Type getSourceType(); 41 | 42 | default String getMessageChannel() { 43 | return "bbc:connection"; 44 | } 45 | 46 | void logDebug(String message, Throwable... throwables); 47 | 48 | void logInfo(String message, Throwable... throwables); 49 | 50 | void logWarning(String message, Throwable... throwables); 51 | 52 | void logError(String message, Throwable... throwables); 53 | 54 | String getServerName(); 55 | 56 | /** 57 | * @deprecated Use {@link #getGroup(String)} or {@link #getGlobalGroup()} 58 | */ 59 | @Deprecated 60 | default String getGroup() { 61 | return getGlobalGroup(); 62 | } 63 | 64 | String getGlobalGroup(); 65 | 66 | Map getGroups(); 67 | 68 | /** 69 | * Get the group that should apply to the plugin (either per-plugin group or global one) 70 | * @param pluginName The plugin's name 71 | * @return The per-plugin group or the global one 72 | */ 73 | default String getGroup(String pluginName) { 74 | return getGroups().getOrDefault(pluginName.toLowerCase(Locale.ROOT), getGlobalGroup()); 75 | } 76 | 77 | void runAsync(Runnable runnable); 78 | } 79 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/LocationInfo.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.common.io.ByteArrayDataInput; 22 | import com.google.common.io.ByteArrayDataOutput; 23 | 24 | public class LocationInfo { 25 | private final String server; 26 | private final String world; 27 | private final double x; 28 | private final double y; 29 | private final double z; 30 | private final float yaw; 31 | private final float pitch; 32 | 33 | public LocationInfo(String server, String world, double x, double y, double z) { 34 | this(server, world, x, y, z, 0, 0); 35 | } 36 | 37 | public LocationInfo(String server, String world, double x, double y, double z, float yaw, float pitch) { 38 | this.server = server; 39 | this.world = world; 40 | this.x = x; 41 | this.y = y; 42 | this.z = z; 43 | this.yaw = yaw; 44 | this.pitch = pitch; 45 | } 46 | 47 | public LocationInfo(LocationInfo location) { 48 | this( 49 | location.getServer(), 50 | location.getWorld(), 51 | location.getX(), 52 | location.getY(), 53 | location.getZ(), 54 | location.getYaw(), 55 | location.getPitch() 56 | ); 57 | } 58 | 59 | public String getServer() { 60 | return server; 61 | } 62 | 63 | public String getWorld() { 64 | return world; 65 | } 66 | 67 | public double getX() { 68 | return x; 69 | } 70 | 71 | public double getY() { 72 | return y; 73 | } 74 | 75 | public double getZ() { 76 | return z; 77 | } 78 | 79 | public float getYaw() { 80 | return yaw; 81 | } 82 | 83 | public float getPitch() { 84 | return pitch; 85 | } 86 | 87 | public void write(ByteArrayDataOutput out) { 88 | out.writeUTF(getServer()); 89 | out.writeUTF(getWorld()); 90 | out.writeDouble(getX()); 91 | out.writeDouble(getY()); 92 | out.writeDouble(getZ()); 93 | out.writeFloat(getPitch()); 94 | out.writeFloat(getYaw()); 95 | } 96 | 97 | public static LocationInfo read(ByteArrayDataInput in) { 98 | String serverName = in.readUTF(); 99 | if (serverName.isEmpty()) { 100 | return null; 101 | } 102 | return new LocationInfo( 103 | serverName, 104 | in.readUTF(), 105 | in.readDouble(), 106 | in.readDouble(), 107 | in.readDouble(), 108 | in.readFloat(), 109 | in.readFloat() 110 | ); 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | return this.getClass().getSimpleName() + "{server=" + getServer() + ",world=" + getWorld() + ",x=" + getX() + ",y=" + getY() + ",z=" + getZ() + ",yaw=" + getYaw() + ",pitch=" + getPitch() + "}"; 116 | } 117 | 118 | @Override 119 | public boolean equals(Object o) { 120 | if (o == null || !(o instanceof LocationInfo)) { 121 | return false; 122 | } 123 | if (o == this) { 124 | return true; 125 | } 126 | LocationInfo other = (LocationInfo) o; 127 | return other.getServer().equalsIgnoreCase(getServer()) 128 | && ((other.getWorld() == null && getWorld() == null) 129 | || (other.getWorld() != null && other.getWorld().equalsIgnoreCase(getWorld()))) 130 | && other.getX() == getX() 131 | && other.getY() == getY() 132 | && other.getZ() == getZ() 133 | && other.getYaw() == getYaw() 134 | && other.getPitch() == getPitch(); 135 | } 136 | } -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/ProxyBridgeCommon.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.common.io.ByteArrayDataInput; 22 | import com.google.common.io.ByteArrayDataOutput; 23 | import com.google.common.io.ByteStreams; 24 | import de.themoep.connectorplugin.connector.MessageTarget; 25 | 26 | import java.util.concurrent.CompletableFuture; 27 | import java.util.function.Consumer; 28 | 29 | import static de.themoep.connectorplugin.connector.Connector.PROXY_ID_PREFIX; 30 | 31 | public abstract class ProxyBridgeCommon

, R> extends BridgeCommon { 32 | 33 | public ProxyBridgeCommon(P plugin) { 34 | super(plugin); 35 | 36 | registerMessageHandler(Action.STARTED, (receiver, message) 37 | -> registerServerCommands(message.getReceivedMessage().getSendingServer())); 38 | 39 | registerMessageHandler(Action.TELEPORT, (receiver, message) -> { 40 | ByteArrayDataInput in = ByteStreams.newDataInput(message.getData()); 41 | String senderServer = message.getReceivedMessage().getSendingServer(); 42 | long id = in.readLong(); 43 | String playerName = in.readUTF(); 44 | LocationInfo targetLocation = LocationInfo.read(in); 45 | 46 | teleport(playerName, targetLocation, messages -> sendResponseMessage(senderServer, id, messages)) 47 | .thenAccept(success -> sendResponse(senderServer, id, success)); 48 | }); 49 | 50 | registerMessageHandler(Action.TELEPORT_TO_WORLD, (receiver, message) -> { 51 | ByteArrayDataInput in = ByteStreams.newDataInput(message.getData()); 52 | String senderServer = message.getReceivedMessage().getSendingServer(); 53 | long id = in.readLong(); 54 | String playerName = in.readUTF(); 55 | String targetServer = in.readUTF(); 56 | String targetWorld = in.readUTF(); 57 | 58 | teleport(playerName, targetServer, targetWorld, messages -> sendResponseMessage(senderServer, id, messages)) 59 | .thenAccept(success -> sendResponse(senderServer, id, success)); 60 | }); 61 | 62 | registerMessageHandler(Action.TELEPORT_TO_PLAYER, (receiver, message) -> { 63 | ByteArrayDataInput in = ByteStreams.newDataInput(message.getData()); 64 | String senderServer = message.getReceivedMessage().getSendingServer(); 65 | long id = in.readLong(); 66 | String playerName = in.readUTF(); 67 | String targetName = in.readUTF(); 68 | 69 | teleport(playerName, targetName, messages -> sendResponseMessage(senderServer, id, messages)) 70 | .thenAccept(success -> sendResponse(senderServer, id, success)); 71 | }); 72 | 73 | registerHandler(Action.GET_PLAYER_INFO, (receiver, data) -> { 74 | ByteArrayDataInput in = ByteStreams.newDataInput(data); 75 | String senderServer = in.readUTF(); 76 | long id = in.readLong(); 77 | String playerName = in.readUTF(); 78 | 79 | R player = getPlayer(playerName); 80 | if (player == null) { 81 | sendResponseMessage(senderServer, id, "Player " + playerName + " not found!"); 82 | } else { 83 | sendResponse(senderServer, id, createPlayerInfo(player)); 84 | } 85 | }); 86 | 87 | registerHandler(Action.RESPONSE, (receiver, data) -> { 88 | ByteArrayDataInput in = ByteStreams.newDataInput(data); 89 | long id = in.readLong(); 90 | boolean isCompletion = in.readBoolean(); 91 | if (isCompletion) { 92 | handleResponse(id, in); 93 | } else { 94 | String message = in.readUTF(); 95 | Consumer[] consumer = consumers.getIfPresent(id); 96 | if (consumer != null) { 97 | for (Consumer stringConsumer : consumer) { 98 | stringConsumer.accept(message); 99 | } 100 | } 101 | } 102 | }); 103 | } 104 | 105 | @Override 106 | protected void sendResponseData(String target, byte[] out) { 107 | sendData( 108 | Action.RESPONSE, 109 | target.startsWith(PROXY_ID_PREFIX) ? MessageTarget.PROXY : MessageTarget.SERVER, 110 | target, 111 | out); 112 | } 113 | 114 | /** 115 | * Run a console command on all other proxies 116 | * @param command The command to run 117 | * @param consumer Optional Consumer (or multiple) for the messages triggered by the command 118 | * @return A future for whether the command was run successfully 119 | */ 120 | public CompletableFuture runProxyConsoleCommand(String command, Consumer... consumer) { 121 | CompletableFuture future = new CompletableFuture<>(); 122 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 123 | long id = RANDOM.nextLong(); 124 | out.writeLong(id); 125 | out.writeUTF(command); 126 | responses.put(id, new ResponseHandler.Boolean(future)); 127 | if (consumer != null && consumer.length > 0) { 128 | consumers.put(id, consumer); 129 | } 130 | sendData(Action.CONSOLE_COMMAND, MessageTarget.OTHER_PROXIES, out.toByteArray()); 131 | return future; 132 | } 133 | 134 | protected void onPlayerJoin(PlayerInfo playerInfo) { 135 | addPlayerInfo(playerInfo); 136 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 137 | playerInfo.write(out); 138 | byte[] data = out.toByteArray(); 139 | try { 140 | sendData(Action.PLAYER_JOIN, MessageTarget.OTHER_PROXIES, data); 141 | } catch (UnsupportedOperationException ignored) { 142 | // Sending to other proxies is not supported when using plugin messages 143 | } 144 | sendData(Action.PLAYER_JOIN, MessageTarget.ALL_QUEUE, data); 145 | } 146 | 147 | protected void onPlayerLeave(String player) { 148 | unmarkTeleporting(player); 149 | 150 | removePlayerInfo(player); 151 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 152 | out.writeUTF(player); 153 | byte[] data = out.toByteArray(); 154 | try { 155 | sendData(Action.PLAYER_LEAVE, MessageTarget.OTHER_PROXIES, data); 156 | } catch (UnsupportedOperationException ignored) { 157 | // Sending to other proxies is not supported when using plugin messages 158 | } 159 | sendData(Action.PLAYER_LEAVE, MessageTarget.ALL_QUEUE, data); 160 | } 161 | 162 | /** 163 | * Register all known commands on a server 164 | * @param server The server 165 | */ 166 | protected abstract void registerServerCommands(String server); 167 | 168 | /** 169 | * Get a player by name 170 | * @param playerName The name of the player 171 | * @return The player 172 | */ 173 | protected abstract R getPlayer(String playerName); 174 | 175 | /** 176 | * Create a player info object for a player 177 | * @param player The player 178 | * @return The player info object 179 | */ 180 | public abstract PlayerInfo createPlayerInfo(R player); 181 | } 182 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/ResponseHandler.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import java.util.concurrent.CompletableFuture; 22 | 23 | public abstract class ResponseHandler { 24 | 25 | private final CompletableFuture future; 26 | 27 | protected ResponseHandler(CompletableFuture future) { 28 | this.future = future; 29 | } 30 | 31 | public CompletableFuture getFuture() { 32 | return future; 33 | } 34 | 35 | public static class Boolean extends ResponseHandler { 36 | public Boolean(CompletableFuture future) { 37 | super(future); 38 | } 39 | } 40 | 41 | public static class String extends ResponseHandler { 42 | public String(CompletableFuture future) { 43 | super(future); 44 | } 45 | } 46 | 47 | public static class Location extends ResponseHandler { 48 | public Location(CompletableFuture future) { 49 | super(future); 50 | } 51 | } 52 | 53 | public static class PlayerInfo extends ResponseHandler { 54 | public PlayerInfo(CompletableFuture future) { 55 | super(future); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/ConnectingPlugin.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | public interface ConnectingPlugin { 22 | String getName(); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/Message.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector;/* 2 | * ConnectorPlugin 3 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | import com.google.common.io.ByteArrayDataInput; 20 | import com.google.common.io.ByteArrayDataOutput; 21 | import com.google.common.io.ByteStreams; 22 | 23 | public class Message { 24 | private static final int VERSION = 2; 25 | private final String group; 26 | private final MessageTarget target; 27 | private final String sendingServer; 28 | private final String sendingPlugin; 29 | private final String action; 30 | private final byte[] data; 31 | 32 | public Message(String group, MessageTarget target, String sendingServer, String sendingPlugin, String action, byte[] data) { 33 | this.group = group; 34 | this.target = target; 35 | this.sendingServer = sendingServer; 36 | this.sendingPlugin = sendingPlugin; 37 | this.action = action; 38 | this.data = data; 39 | } 40 | 41 | public String getGroup() { 42 | return group; 43 | } 44 | 45 | public MessageTarget getTarget() { 46 | return target; 47 | } 48 | 49 | public String getSendingServer() { 50 | return sendingServer; 51 | } 52 | 53 | public String getSendingPlugin() { 54 | return sendingPlugin; 55 | } 56 | 57 | public String getAction() { 58 | return action; 59 | } 60 | 61 | public byte[] getData() { 62 | return data; 63 | } 64 | 65 | public byte[] writeToByteArray() { 66 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 67 | out.writeInt(VERSION); 68 | out.writeUTF(target.name()); 69 | out.writeUTF(sendingServer); 70 | out.writeUTF(sendingPlugin); 71 | out.writeUTF(action); 72 | out.writeInt(data.length); 73 | out.write(data); 74 | return out.toByteArray(); 75 | } 76 | 77 | public static Message fromByteArray(String group, byte[] messageData) throws VersionMismatchException { 78 | ByteArrayDataInput in = ByteStreams.newDataInput(messageData); 79 | int messageVersion = in.readInt(); 80 | if (messageVersion < VERSION) { 81 | throw new VersionMismatchException(messageVersion, VERSION, "Received message from an outdated version (" + messageVersion + ", this only supports " + VERSION + ")! Please update the sending plugin!"); 82 | } else if (messageVersion > VERSION) { 83 | throw new VersionMismatchException(messageVersion, VERSION, "Received message with a newer version (" + messageVersion + ", this only supports " + VERSION + ")! Please update this plugin!"); 84 | } 85 | MessageTarget target = MessageTarget.valueOf(in.readUTF()); 86 | String senderServer = in.readUTF(); 87 | String senderPlugin = in.readUTF(); 88 | String action = in.readUTF(); 89 | int length = in.readInt(); 90 | byte[] data = new byte[length]; 91 | in.readFully(data); 92 | return new Message(group, target, senderServer, senderPlugin, action, data); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/MessageTarget.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2020 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | public enum MessageTarget { 22 | /** 23 | * Sends to all servers that have players connected. (So this doesn't queue with plugin messages) 24 | */ 25 | ALL_WITH_PLAYERS(Type.SERVER), 26 | /** 27 | * Tries to send to all servers. (With plugin messages it queues if no player is connected to server) 28 | */ 29 | ALL_QUEUE(Type.SERVER), 30 | /** 31 | * Sends to all other servers that have players connected. (So this doesn't queue with plugin messages) 32 | */ 33 | OTHERS_WITH_PLAYERS(Type.SERVER), 34 | /** 35 | * Tries to send to all other servers. (With plugin messages it queues if no player is connected to server) 36 | */ 37 | OTHERS_QUEUE(Type.SERVER), 38 | /** 39 | * Send to the players current server.
40 | * Requires a server name or player parameter. 41 | */ 42 | SERVER(Type.SERVER), 43 | /** 44 | * Send to the players current proxy.
45 | * Requires a proxy id or player parameter 46 | */ 47 | PROXY(Type.PROXY), 48 | /** 49 | * Send to all connected proxies 50 | */ 51 | ALL_PROXIES(Type.PROXY), 52 | /** 53 | * Send to all proxies that aren't the current proxy 54 | */ 55 | OTHER_PROXIES(Type.PROXY, Type.PROXY); 56 | 57 | private final Type type; 58 | private final Type source; 59 | 60 | MessageTarget() { 61 | this(null, null); 62 | } 63 | 64 | MessageTarget(Type type) { 65 | this(type, null); 66 | } 67 | 68 | MessageTarget(Type type, Type source) { 69 | this.type = type; 70 | this.source = source; 71 | } 72 | 73 | public Type getType() { 74 | return type; 75 | } 76 | 77 | public Type getSource() { 78 | return source; 79 | } 80 | 81 | public enum Type { 82 | PROXY, 83 | SERVER 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/MqttConnection.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector; 2 | /* 3 | * ConnectorPlugin 4 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import com.google.common.io.ByteArrayDataInput; 21 | import com.google.common.io.ByteArrayDataOutput; 22 | import com.google.common.io.ByteStreams; 23 | import de.themoep.connectorplugin.ConnectorPlugin; 24 | import org.eclipse.paho.mqttv5.client.IMqttMessageListener; 25 | import org.eclipse.paho.mqttv5.client.MqttClient; 26 | import org.eclipse.paho.mqttv5.client.MqttConnectionOptions; 27 | import org.eclipse.paho.mqttv5.common.MqttException; 28 | import org.eclipse.paho.mqttv5.common.MqttSubscription; 29 | 30 | import java.nio.charset.StandardCharsets; 31 | import java.util.function.BiConsumer; 32 | 33 | public class MqttConnection { 34 | 35 | private final ConnectorPlugin plugin; 36 | private MqttClient client; 37 | 38 | public MqttConnection(ConnectorPlugin plugin, String brokerURI, String clientID, String username, String password, int keepAlive, BiConsumer onMessage) { 39 | this.plugin = plugin; 40 | 41 | MqttConnectionOptions conOpts = new MqttConnectionOptions(); 42 | 43 | conOpts.setCleanStart(true); 44 | 45 | if (clientID == null || clientID.isEmpty()) { 46 | clientID = plugin.getName() + "-" + plugin.getServerName() + "-" + Thread.currentThread().getId(); 47 | } 48 | 49 | if (username != null) { 50 | conOpts.setUserName(username); 51 | } 52 | 53 | if (password != null) { 54 | conOpts.setPassword(password.getBytes(StandardCharsets.UTF_8)); 55 | } 56 | 57 | conOpts.setKeepAliveInterval(keepAlive); 58 | conOpts.setAutomaticReconnect(true); 59 | 60 | try { 61 | client = new MqttClient(brokerURI, clientID); 62 | client.connect(conOpts); 63 | 64 | client.subscribe(new MqttSubscription[]{new MqttSubscription(plugin.getMessageChannel())}, new IMqttMessageListener[] {(topic, message) -> { 65 | if (!topic.equals(plugin.getMessageChannel())) { 66 | return; 67 | } 68 | if (message.getPayload().length == 0) { 69 | plugin.logWarning("Received a message with 0 bytes on " + topic + " MQTT topic? "); 70 | return; 71 | } 72 | 73 | ByteArrayDataInput in = ByteStreams.newDataInput(message.getPayload()); 74 | String group = in.readUTF(); 75 | 76 | String target = in.readUTF(); 77 | 78 | int messageLength = in.readInt(); 79 | byte[] messageData = new byte[messageLength]; 80 | in.readFully(messageData); 81 | 82 | try { 83 | onMessage.accept(target, Message.fromByteArray(group, messageData)); 84 | } catch (IllegalArgumentException e) { 85 | plugin.logError("Error while decoding message on " + topic + " MQTT topic! ", e); 86 | } catch (VersionMismatchException e) { 87 | plugin.logWarning(e.getMessage() + ". Ignoring message!"); 88 | } 89 | }}).waitForCompletion(10000); 90 | } catch (MqttException e) { 91 | throw new IllegalArgumentException(e); 92 | } 93 | } 94 | 95 | public void sendMessage(String senderName, Message message) { 96 | byte[] messageData = message.writeToByteArray(); 97 | 98 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 99 | out.writeUTF(message.getGroup()); 100 | out.writeUTF(senderName != null ? senderName : ""); 101 | out.writeInt(messageData.length); 102 | out.write(messageData); 103 | byte[] dataToSend = out.toByteArray(); 104 | 105 | plugin.runAsync(() -> { 106 | try { 107 | client.publish(plugin.getMessageChannel(), dataToSend, 1, false); 108 | } catch (MqttException e) { 109 | e.printStackTrace(); 110 | } 111 | }); 112 | } 113 | 114 | public void close() { 115 | try { 116 | client.disconnect(); 117 | } catch (MqttException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/RedisConnection.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector; 2 | /* 3 | * ConnectorPlugin 4 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Affero General Public License as published 8 | * by the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Affero General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Affero General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | import com.google.common.io.ByteArrayDataInput; 21 | import com.google.common.io.ByteArrayDataOutput; 22 | import com.google.common.io.ByteStreams; 23 | import de.themoep.connectorplugin.ConnectorPlugin; 24 | import io.lettuce.core.RedisClient; 25 | import io.lettuce.core.RedisURI; 26 | import io.lettuce.core.api.StatefulRedisConnection; 27 | import io.lettuce.core.codec.ByteArrayCodec; 28 | import io.lettuce.core.codec.RedisCodec; 29 | import io.lettuce.core.codec.StringCodec; 30 | import io.lettuce.core.pubsub.RedisPubSubListener; 31 | import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; 32 | 33 | import java.nio.ByteBuffer; 34 | import java.time.Duration; 35 | import java.util.function.BiConsumer; 36 | 37 | public class RedisConnection { 38 | 39 | private final ConnectorPlugin plugin; 40 | 41 | private final RedisClient client; 42 | private StatefulRedisConnection connection; 43 | 44 | public RedisConnection(ConnectorPlugin plugin, String uriString, String host, int port, int db, String password, long timeout, BiConsumer onMessage) { 45 | this.plugin = plugin; 46 | RedisURI uri; 47 | if (uriString != null && !uriString.isEmpty()) { 48 | uri = RedisURI.create(uriString); 49 | } else { 50 | uri = new RedisURI(); 51 | if (host != null && !host.isEmpty()) { 52 | uri.setHost(host); 53 | } 54 | if (port > 0) { 55 | uri.setPort(port); 56 | } 57 | if (db > -1) { 58 | uri.setDatabase(db); 59 | } 60 | if (password != null && !password.isEmpty()) { 61 | uri.setPassword(password); 62 | } 63 | if (timeout > 0) { 64 | uri.setTimeout(Duration.ofSeconds(timeout)); 65 | } 66 | } 67 | client = RedisClient.create(uri); 68 | 69 | StatefulRedisPubSubConnection connection = client.connectPubSub(new StringByteArrayCodec()); 70 | connection.addListener(new RedisPubSubListener() { 71 | @Override 72 | public void message(String channel, byte[] data) { 73 | if (!channel.equals(plugin.getMessageChannel())) { 74 | return; 75 | } 76 | if (data.length == 0) { 77 | plugin.logWarning("Received a message with 0 bytes on " + channel + " redis channel? "); 78 | return; 79 | } 80 | 81 | ByteArrayDataInput in = ByteStreams.newDataInput(data); 82 | String group = in.readUTF(); 83 | 84 | String target = in.readUTF(); 85 | 86 | int messageLength = in.readInt(); 87 | byte[] messageData = new byte[messageLength]; 88 | in.readFully(messageData); 89 | 90 | try { 91 | onMessage.accept(target, Message.fromByteArray(group, messageData)); 92 | } catch (IllegalArgumentException e) { 93 | plugin.logError("Error while decoding message on " + channel + " redis channel! ", e); 94 | } catch (VersionMismatchException e) { 95 | plugin.logWarning(e.getMessage() + ". Ignoring message!"); 96 | } 97 | } 98 | 99 | @Override 100 | public void message(String pattern, String channel, byte[] message) {} 101 | 102 | @Override 103 | public void subscribed(String channel, long count) {} 104 | 105 | @Override 106 | public void psubscribed(String pattern, long count) {} 107 | 108 | @Override 109 | public void unsubscribed(String channel, long count) {} 110 | 111 | @Override 112 | public void punsubscribed(String pattern, long count) {} 113 | }); 114 | 115 | connection.async().subscribe(plugin.getMessageChannel()); 116 | } 117 | 118 | public void sendMessage(String targetData, Message message) { 119 | if (connection == null || !connection.isOpen()) { 120 | connection = client.connect(new StringByteArrayCodec()); 121 | } 122 | byte[] messageData = message.writeToByteArray(); 123 | 124 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 125 | out.writeUTF(message.getGroup()); 126 | out.writeUTF(targetData != null ? targetData : ""); 127 | out.writeInt(messageData.length); 128 | out.write(messageData); 129 | byte[] dataToSend = out.toByteArray(); 130 | 131 | connection.async().publish(plugin.getMessageChannel(), dataToSend); 132 | } 133 | 134 | public void close() { 135 | client.shutdown(); 136 | } 137 | 138 | private class StringByteArrayCodec implements RedisCodec { 139 | 140 | private final StringCodec stringCodec = new StringCodec(); 141 | private final ByteArrayCodec byteArrayCodec = new ByteArrayCodec(); 142 | 143 | @Override 144 | public String decodeKey(ByteBuffer bytes) { 145 | return stringCodec.decodeKey(bytes); 146 | } 147 | 148 | @Override 149 | public byte[] decodeValue(ByteBuffer bytes) { 150 | return byteArrayCodec.decodeValue(bytes); 151 | } 152 | 153 | @Override 154 | public ByteBuffer encodeKey(String key) { 155 | return stringCodec.encodeKey(key); 156 | } 157 | 158 | @Override 159 | public ByteBuffer encodeValue(byte[] value) { 160 | return byteArrayCodec.encodeValue(value); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /core/src/main/java/de/themoep/connectorplugin/connector/VersionMismatchException.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.connector;/* 2 | * ConnectorPlugin 3 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as published 7 | * by the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | public class VersionMismatchException extends Exception { 20 | private final int receivedVersion; 21 | private final int supportedVersion; 22 | 23 | public VersionMismatchException(int receivedVersion, int supportedVersion, String message) { 24 | super(message); 25 | this.receivedVersion = receivedVersion; 26 | this.supportedVersion = supportedVersion; 27 | } 28 | 29 | public int getReceivedVersion() { 30 | return receivedVersion; 31 | } 32 | 33 | public int getSupportedVersion() { 34 | return supportedVersion; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | parent 9 | de.themoep.connectorplugin 10 | 1.5.2-SNAPSHOT 11 | 12 | 13 | plugin 14 | 15 | 16 | 17 | papermc 18 | https://papermc.io/repo/repository/maven-public/ 19 | 20 | 21 | 22 | 23 | 24 | ${groupId} 25 | core 26 | ${project.version} 27 | compile 28 | 29 | 30 | ${groupId} 31 | bukkit 32 | ${project.version} 33 | compile 34 | 35 | 36 | ${groupId} 37 | bungee 38 | ${project.version} 39 | compile 40 | 41 | 42 | ${groupId} 43 | velocity 44 | ${project.version} 45 | compile 46 | 47 | 48 | io.lettuce 49 | lettuce-core 50 | compile 51 | 52 | 53 | org.eclipse.paho 54 | org.eclipse.paho.mqttv5.client 55 | compile 56 | 57 | 58 | io.papermc 59 | paperlib 60 | compile 61 | 62 | 63 | de.themoep 64 | bungeeplugin 65 | compile 66 | 67 | 68 | 69 | 70 | ../target 71 | ${plugin.name} 72 | 73 | 74 | ../ 75 | 76 | LICENSE 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-shade-plugin 84 | 3.3.0 85 | 86 | 87 | package 88 | 89 | shade 90 | 91 | 92 | 93 | 94 | *:* 95 | 96 | bundle.properties 97 | META-INF/*.SF 98 | META-INF/*.DSA 99 | META-INF/*.RSA 100 | META-INF/LICENSE 101 | *.png 102 | *.html 103 | *.jpeg 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | de.themoep.bungeeplugin 114 | de.themoep.connectorplugin.bungee.plugin 115 | 116 | 117 | io.lettuce 118 | de.themoep.connectorplugin.lib.lettuce 119 | 120 | io.lettuce.core.support.LettuceCdiExtension 121 | 122 | 123 | 124 | io.netty 125 | de.themoep.connectorplugin.lib.netty 126 | 127 | io.netty.util.internal.Hidden 128 | 129 | 130 | 131 | reactor 132 | de.themoep.connectorplugin.lib.reactor 133 | 134 | 135 | org.reactivestreams 136 | de.themoep.connectorplugin.lib.reactivestreams 137 | 138 | 139 | org.eclipse.paho 140 | de.themoep.connectorplugin.lib.paho 141 | 142 | org.eclipse.paho.mqttv5.client.internal.TCPNetworkModuleFactory 143 | org.eclipse.paho.mqttv5.client.internal.SSLNetworkModuleFactory 144 | org.eclipse.paho.mqttv5.client.websocket.WebSocketNetworkModuleFactory 145 | org.eclipse.paho.mqttv5.client.websocket.WebSocketSecureNetworkModuleFactory 146 | org.eclipse.paho.mqttv5.client.spi.* 147 | 148 | 149 | 150 | io.papermc.lib 151 | de.themoep.connectorplugin.lib.paperlib 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 4.0.0 7 | 8 | de.themoep.connectorplugin 9 | parent 10 | 1.5.2-SNAPSHOT 11 | pom 12 | ${plugin.name} 13 | 14 | 15 | ConnectorPlugin 16 | UTF-8 17 | ${buildNumber} 18 | ${project.version} ${buildDescription} 19 | 1.8 20 | 1.8 21 | 22 | 23 | 24 | 25 | minebench-repo 26 | https://repo.minebench.de 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | com.google.guava 35 | guava 36 | 21.0 37 | provided 38 | 39 | 40 | io.lettuce 41 | lettuce-core 42 | 6.3.2.RELEASE 43 | provided 44 | 45 | 46 | org.eclipse.paho 47 | org.eclipse.paho.mqttv5.client 48 | 1.2.5 49 | provided 50 | 51 | 52 | org.spigotmc 53 | spigot-api 54 | 1.16.3-R0.1-SNAPSHOT 55 | provided 56 | 57 | 58 | io.papermc 59 | paperlib 60 | 1.0.8 61 | provided 62 | 63 | 64 | net.md-5 65 | bungeecord-api 66 | 1.16-R0.1-SNAPSHOT 67 | provided 68 | 69 | 70 | de.themoep 71 | bungeeplugin 72 | 1.3-SNAPSHOT 73 | provided 74 | 75 | 76 | com.velocitypowered 77 | velocity-api 78 | 3.3.0-SNAPSHOT 79 | provided 80 | 81 | 82 | 83 | 84 | 85 | core 86 | bukkit 87 | bungee 88 | velocity 89 | plugin 90 | 91 | 92 | 93 | 94 | static_build_number 95 | 96 | 97 | !env.BUILD_NUMBER 98 | 99 | 100 | 101 | 0 102 | (compiled at ${maven.build.timestamp}) 103 | 104 | 105 | 106 | dynamic_build_number 107 | 108 | 109 | env.BUILD_NUMBER 110 | 111 | 112 | 113 | ${env.BUILD_NUMBER} 114 | (build ${env.BUILD_NUMBER}) 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-compiler-plugin 124 | 3.10.1 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-javadoc-plugin 129 | 3.4.0 130 | 131 | 8 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /velocity/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | de.themoep.connectorplugin 9 | parent 10 | 1.5.2-SNAPSHOT 11 | 12 | 13 | velocity 14 | jar 15 | 16 | 17 | 18 | velocity-repo 19 | https://repo.velocitypowered.com/snapshots/ 20 | 21 | 22 | 23 | 24 | 25 | com.velocitypowered 26 | velocity-api 27 | 28 | 29 | ${groupId} 30 | core 31 | ${project.version} 32 | 33 | 34 | 35 | 36 | clean package 37 | ${project.name} 38 | 39 | 40 | src/main/resources 41 | true 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/PluginConfig.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.SerializationException; 23 | import org.spongepowered.configurate.yaml.NodeStyle; 24 | import org.spongepowered.configurate.yaml.YamlConfigurationLoader; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.InputStreamReader; 31 | import java.nio.file.Files; 32 | import java.util.regex.Pattern; 33 | 34 | public class PluginConfig { 35 | 36 | private static final Pattern PATH_PATTERN = Pattern.compile("\\."); 37 | 38 | private final VelocityConnectorPlugin plugin; 39 | private final File configFile; 40 | private final String defaultFile; 41 | private final YamlConfigurationLoader configLoader; 42 | private ConfigurationNode config; 43 | private ConfigurationNode defaultConfig; 44 | 45 | public PluginConfig(VelocityConnectorPlugin plugin, File configFile) { 46 | this(plugin, configFile, configFile.getName()); 47 | } 48 | 49 | public PluginConfig(VelocityConnectorPlugin plugin, File configFile, String defaultFile) { 50 | this.plugin = plugin; 51 | this.configFile = configFile; 52 | this.defaultFile = defaultFile; 53 | configLoader = YamlConfigurationLoader.builder() 54 | .indent(2) 55 | .path(configFile.toPath()) 56 | .nodeStyle(NodeStyle.FLOW) 57 | .build(); 58 | } 59 | 60 | public boolean load() { 61 | try { 62 | config = configLoader.load(); 63 | if (defaultFile != null) { 64 | defaultConfig = YamlConfigurationLoader.builder() 65 | .indent(2) 66 | .source(() -> new BufferedReader(new InputStreamReader(plugin.getResourceAsStream(defaultFile)))) 67 | .build().load(); 68 | if (config.empty()) { 69 | config = defaultConfig.copy(); 70 | } 71 | } 72 | plugin.logDebug("Loaded " + configFile.getName()); 73 | return true; 74 | } catch (IOException e) { 75 | plugin.logError("Unable to load configuration file " + configFile.getName(), e); 76 | return false; 77 | } 78 | } 79 | 80 | public boolean createDefaultConfig() throws IOException { 81 | try (InputStream in = plugin.getResourceAsStream(defaultFile)) { 82 | if (in == null) { 83 | plugin.logWarning("No default config '" + defaultFile + "' found in " + plugin.getName() + "!"); 84 | return false; 85 | } 86 | if (!configFile.exists()) { 87 | File parent = configFile.getParentFile(); 88 | if (!parent.exists()) { 89 | parent.mkdirs(); 90 | } 91 | try { 92 | Files.copy(in, configFile.toPath()); 93 | return true; 94 | } catch (IOException ex) { 95 | plugin.logError("Could not save " + configFile.getName() + " to " + configFile, ex); 96 | } 97 | } 98 | } catch (IOException ex) { 99 | plugin.logError("Could not load default config from " + defaultFile, ex); 100 | } 101 | return false; 102 | } 103 | 104 | public void save() { 105 | try { 106 | configLoader.save(config); 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | 112 | public Object set(String path, Object value) throws SerializationException { 113 | ConfigurationNode node = config.node(splitPath(path)); 114 | Object prev = node.raw(); 115 | node.set(value); 116 | return prev; 117 | } 118 | 119 | public ConfigurationNode remove(String path) { 120 | ConfigurationNode node = config.node(splitPath(path)); 121 | try { 122 | return node.virtual() ? node : node.set(null); 123 | } catch (SerializationException ignored) {} 124 | return node; 125 | } 126 | 127 | public ConfigurationNode getRawConfig() { 128 | return config; 129 | } 130 | 131 | public ConfigurationNode getRawConfig(String path) { 132 | return getRawConfig().node(splitPath(path)); 133 | } 134 | 135 | public boolean has(String path) { 136 | return !getRawConfig(path).virtual(); 137 | } 138 | 139 | public boolean isSection(String path) { 140 | return !getRawConfig(path).childrenMap().isEmpty(); 141 | } 142 | 143 | public int getInt(String path) { 144 | return getInt(path, 0); 145 | } 146 | 147 | public int getInt(String path, int def) { 148 | return getRawConfig(path).getInt(def); 149 | } 150 | 151 | public long getLong(String path) { 152 | return getLong(path, 0); 153 | } 154 | 155 | public long getLong(String path, long def) { 156 | return getRawConfig(path).getLong(def); 157 | } 158 | 159 | public double getDouble(String path) { 160 | return getDouble(path, 0); 161 | } 162 | 163 | public double getDouble(String path, double def) { 164 | return getRawConfig(path).getDouble(def); 165 | } 166 | 167 | public String getString(String path) { 168 | return getRawConfig(path).getString(); 169 | } 170 | 171 | public String getString(String path, String def) { 172 | return getRawConfig(path).getString(def); 173 | } 174 | 175 | public boolean getBoolean(String path) { 176 | return getBoolean(path, false); 177 | } 178 | 179 | public boolean getBoolean(String path, boolean def) { 180 | return getRawConfig(path).getBoolean(def); 181 | } 182 | 183 | private static Object[] splitPath(String key) { 184 | return PATH_PATTERN.split(key); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/VelocityConnectorPlugin.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.inject.Inject; 22 | import com.velocitypowered.api.event.Subscribe; 23 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; 24 | import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; 25 | import com.velocitypowered.api.plugin.annotation.DataDirectory; 26 | import com.velocitypowered.api.proxy.Player; 27 | import com.velocitypowered.api.proxy.ProxyServer; 28 | import de.themoep.connectorplugin.ConnectorPlugin; 29 | import de.themoep.connectorplugin.velocity.commands.ConnectorCommand; 30 | import de.themoep.connectorplugin.velocity.connector.VelocityConnector; 31 | import de.themoep.connectorplugin.velocity.connector.MqttConnector; 32 | import de.themoep.connectorplugin.velocity.connector.PluginMessageConnector; 33 | import de.themoep.connectorplugin.velocity.connector.RedisConnector; 34 | import de.themoep.connectorplugin.connector.MessageTarget; 35 | import org.slf4j.Logger; 36 | 37 | import java.io.File; 38 | import java.io.IOException; 39 | import java.io.InputStream; 40 | import java.nio.file.Path; 41 | import java.util.Collections; 42 | import java.util.Locale; 43 | import java.util.Map; 44 | 45 | import static de.themoep.connectorplugin.connector.Connector.PROXY_ID_PREFIX; 46 | 47 | public final class VelocityConnectorPlugin implements ConnectorPlugin { 48 | 49 | private final ProxyServer proxy; 50 | private final Logger logger; 51 | private final File dataFolder; 52 | private PluginConfig config; 53 | private VelocityConnector connector; 54 | private Bridge bridge; 55 | private boolean debug = true; 56 | private String serverId; 57 | 58 | @Inject 59 | public VelocityConnectorPlugin(ProxyServer proxy, Logger logger, @DataDirectory Path dataFolder) { 60 | this.proxy = proxy; 61 | this.logger = logger; 62 | this.dataFolder = dataFolder.toFile(); 63 | } 64 | 65 | @Subscribe 66 | public void onProxyInitialization(ProxyInitializeEvent event) { 67 | config = new PluginConfig(this, new File(dataFolder, "config.yml"), "velocity-config.yml"); 68 | try { 69 | config.createDefaultConfig(); 70 | } catch (IOException e) { 71 | logger.error("Could not created default config! " + e.getMessage()); 72 | return; 73 | } 74 | if (!config.load()) { 75 | return; 76 | } 77 | 78 | debug = getConfig().getBoolean("debug"); 79 | serverId = getConfig().getString("server-id"); 80 | 81 | String messengerType = getConfig().getString("messenger-type", "plugin_messages").toLowerCase(Locale.ROOT); 82 | switch (messengerType) { 83 | default: 84 | logger.warn("Messenger type '" + messengerType + "' is not supported, falling back to plugin messages!"); 85 | case "plugin_messages": 86 | connector = new PluginMessageConnector(this); 87 | logger.warn("Using plugin messages as the messenger type will come with" + 88 | " some caveats like sending to servers without players or to" + 89 | " other proxies not working!"); 90 | logger.warn("Please consider using one of the other messenger types!"); 91 | break; 92 | case "redis": 93 | connector = new RedisConnector(this); 94 | break; 95 | case "mqtt": 96 | connector = new MqttConnector(this); 97 | break; 98 | } 99 | 100 | ConnectorCommand command = new ConnectorCommand(this); 101 | getProxy().getCommandManager().register(command, command); 102 | 103 | bridge = new Bridge(this); 104 | } 105 | 106 | @Subscribe 107 | public void onProxyInitialization(ProxyShutdownEvent event) { 108 | connector.close(); 109 | } 110 | 111 | @Override 112 | public VelocityConnector getConnector() { 113 | return connector; 114 | } 115 | 116 | /** 117 | * Get the bridge helper class for executing certain actions on other servers 118 | * @return The bridge helper 119 | */ 120 | public Bridge getBridge() { 121 | return bridge; 122 | } 123 | 124 | @Override 125 | public void runAsync(Runnable runnable) { 126 | getProxy().getScheduler().buildTask(this, runnable).schedule(); 127 | } 128 | 129 | @Override 130 | public MessageTarget.Type getSourceType() { 131 | return MessageTarget.Type.PROXY; 132 | } 133 | 134 | @Override 135 | public void logDebug(String message, Throwable... throwables) { 136 | if (debug) { 137 | logger.info("[DEBUG] " + message, throwables.length > 0 ? throwables[0] : null); 138 | } 139 | } 140 | 141 | @Override 142 | public void logInfo(String message, Throwable... throwables) { 143 | logger.info(message, throwables.length > 0 ? throwables[0] : null); 144 | } 145 | 146 | @Override 147 | public void logWarning(String message, Throwable... throwables) { 148 | logger.warn(message, throwables.length > 0 ? throwables[0] : null); 149 | } 150 | 151 | @Override 152 | public void logError(String message, Throwable... throwables) { 153 | logger.error(message, throwables.length > 0 ? throwables[0] : null); 154 | } 155 | 156 | @Override 157 | public String getServerName() { 158 | return PROXY_ID_PREFIX + serverId; 159 | } 160 | 161 | @Override 162 | public String getGlobalGroup() { 163 | return ""; 164 | } 165 | 166 | @Override 167 | public Map getGroups() { 168 | return Collections.emptyMap(); 169 | } 170 | 171 | @Override 172 | public String getName() { 173 | return "ConnectorPlugin"; 174 | } 175 | 176 | public ProxyServer getProxy() { 177 | return proxy; 178 | } 179 | 180 | public PluginConfig getConfig() { 181 | return config; 182 | } 183 | 184 | public InputStream getResourceAsStream(String file) { 185 | return getClass().getClassLoader().getResourceAsStream(file); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/ConnectorCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | 22 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 23 | 24 | public class ConnectorCommand extends SubCommand { 25 | 26 | public ConnectorCommand(VelocityConnectorPlugin plugin) { 27 | super(plugin, "connectorpluginvelocity", "connectorplugin.command", "connectorvelocity", "connectorcommandvelocity", "connpluginvelocity", "cpv"); 28 | registerSubCommand(new TeleportCommand(this)); 29 | registerSubCommand(new TeleportToPlayerCommand(this)); 30 | registerSubCommand(new ServerConsoleCommand(this)); 31 | registerSubCommand(new ServerPlayerCommand(this)); 32 | registerSubCommand(new ProxyConsoleCommand(this)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/ProxyConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.command.CommandSource; 22 | import net.kyori.adventure.text.Component; 23 | import net.kyori.adventure.text.format.NamedTextColor; 24 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.stream.Collectors; 31 | 32 | public class ProxyConsoleCommand extends SubCommand { 33 | 34 | public ProxyConsoleCommand(ConnectorCommand parent) { 35 | super(parent.getPlugin(), "proxycommand ", parent.getPermission() + ".proxycommand", "proxyconsole", "proxyconsolecommand", "proxy", "pcc"); 36 | } 37 | 38 | @Override 39 | public boolean run(CommandSource sender, String alias, String[] args) { 40 | if (args.length < 1) { 41 | return false; 42 | } 43 | 44 | String commandString = String.join(" ", args); 45 | sender.sendMessage(Component.text("Executing '" + commandString + "' on other proxies").color(NamedTextColor.GRAY)); 46 | plugin.getBridge().runProxyConsoleCommand(commandString, m -> sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(m))) 47 | .thenAccept(success -> sender.sendMessage(Component.text(success ? "Successfully executed command!" : "Error while executing the command."))); 48 | return true; 49 | } 50 | 51 | @Override 52 | public List onTabComplete(CommandSource sender, String[] args) { 53 | if (!hasPermission(sender)) { 54 | return Collections.emptyList(); 55 | } 56 | if (args.length == 0) { 57 | return new ArrayList<>(plugin.getProxy().getCommandManager().getAliases()); 58 | } else if (args.length == 1) { 59 | return plugin.getProxy().getCommandManager().getAliases().stream().filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 60 | } 61 | return Collections.emptyList(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/ServerConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.command.CommandSource; 22 | import com.velocitypowered.api.proxy.Player; 23 | import net.kyori.adventure.text.Component; 24 | import net.kyori.adventure.text.format.NamedTextColor; 25 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 26 | 27 | import java.util.Arrays; 28 | import java.util.Collections; 29 | import java.util.List; 30 | import java.util.Locale; 31 | import java.util.Optional; 32 | import java.util.stream.Collectors; 33 | 34 | public class ServerConsoleCommand extends SubCommand { 35 | 36 | public ServerConsoleCommand(ConnectorCommand parent) { 37 | super(parent.getPlugin(), "servercommand ", parent.getPermission() + ".servercommand", "serverconsole", "serverconsolecommand", "server", "scc"); 38 | } 39 | 40 | @Override 41 | public boolean run(CommandSource sender, String alias, String[] args) { 42 | if (args.length < 2) { 43 | return false; 44 | } 45 | 46 | String serverName = args[0]; 47 | if (serverName.startsWith("p:")) { 48 | Optional player = plugin.getProxy().getPlayer(serverName.substring(2)); 49 | if (player.isPresent()) { 50 | if (player.get().getCurrentServer().isPresent()) { 51 | serverName = player.get().getCurrentServer().get().getServerInfo().getName(); 52 | } else { 53 | sender.sendMessage(Component.text("Player '" + player.get().getUsername() + "' is not connected to any server?").color(NamedTextColor.RED)); 54 | return false; 55 | } 56 | } else { 57 | sender.sendMessage(Component.text("The player '" + serverName.substring(2) + "' is not online?").color(NamedTextColor.RED)); 58 | return false; 59 | } 60 | } else 61 | if (!plugin.getProxy().getServer(serverName).isPresent()) { 62 | sender.sendMessage(Component.text("There is no server with the name of '" + serverName + "' on the proxy. Trying to send command anyways...").color(NamedTextColor.GRAY)); 63 | } 64 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 65 | sender.sendMessage(Component.text("Executing '" + commandString + "' on server '" + serverName + "'").color(NamedTextColor.GRAY)); 66 | plugin.getBridge().runServerConsoleCommand(serverName, commandString, m -> sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(m))) 67 | .thenAccept(success -> sender.sendMessage(Component.text(success ? "Successfully executed command!" : "Error while executing the command."))); 68 | return true; 69 | } 70 | 71 | @Override 72 | public List onTabComplete(CommandSource sender, String[] args) { 73 | if (!hasPermission(sender)) { 74 | return Collections.emptyList(); 75 | } 76 | if (args.length == 0) { 77 | return plugin.getProxy().getAllServers().stream().map(s -> s.getServerInfo().getName()).collect(Collectors.toList()); 78 | } else if (args.length == 1) { 79 | return plugin.getProxy().getAllServers().stream().map(s -> s.getServerInfo().getName()).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 80 | } 81 | return Collections.emptyList(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/ServerPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.command.CommandSource; 22 | import com.velocitypowered.api.proxy.Player; 23 | import net.kyori.adventure.text.Component; 24 | import net.kyori.adventure.text.format.NamedTextColor; 25 | 26 | import java.util.Arrays; 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.stream.Collectors; 31 | 32 | public class ServerPlayerCommand extends SubCommand { 33 | 34 | public ServerPlayerCommand(ConnectorCommand parent) { 35 | super(parent.getPlugin(), "serverplayercommand ", parent.getPermission() + ".serverplayercommand", "serverplayer", "player", "spc"); 36 | } 37 | 38 | @Override 39 | public boolean run(CommandSource sender, String alias, String[] args) { 40 | if (args.length < 2) { 41 | return false; 42 | } 43 | 44 | Player player = plugin.getProxy().getPlayer(args[0]).orElse(null); 45 | if (player == null) { 46 | sender.sendMessage(Component.text("No player with the name " + args[0] + " is online on this server!")); 47 | return true; 48 | } 49 | String commandString = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); 50 | sender.sendMessage(Component.text("Executing '" + commandString + "' on the server for player '" + player.getUsername() + "'").color(NamedTextColor.GRAY)); 51 | plugin.getBridge().runServerPlayerCommand(player, commandString).thenAccept(success -> sender.sendMessage(Component.text(success ? "Successfully executed command!" : "Error while executing the command."))); 52 | return true; 53 | } 54 | 55 | @Override 56 | public List onTabComplete(CommandSource sender, String[] args) { 57 | if (!hasPermission(sender)) { 58 | return Collections.emptyList(); 59 | } 60 | if (args.length == 0) { 61 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 62 | } else if (args.length == 1) { 63 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 64 | } 65 | return Collections.emptyList(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/SubCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.mojang.brigadier.tree.CommandNode; 22 | import com.velocitypowered.api.command.CommandMeta; 23 | import com.velocitypowered.api.command.CommandSource; 24 | import com.velocitypowered.api.command.SimpleCommand; 25 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 26 | import net.kyori.adventure.text.Component; 27 | import net.kyori.adventure.text.format.NamedTextColor; 28 | import org.checkerframework.checker.nullness.qual.NonNull; 29 | 30 | import java.util.ArrayList; 31 | import java.util.Arrays; 32 | import java.util.Collection; 33 | import java.util.Collections; 34 | import java.util.LinkedHashMap; 35 | import java.util.List; 36 | import java.util.Locale; 37 | import java.util.Map; 38 | 39 | public abstract class SubCommand implements CommandMeta, SimpleCommand { 40 | protected final VelocityConnectorPlugin plugin; 41 | private final String name; 42 | private final String usage; 43 | private final String permission; 44 | private final Collection aliases; 45 | private Map subCommands = new LinkedHashMap<>(); 46 | private Map subCommandAliases = new LinkedHashMap<>(); 47 | 48 | public SubCommand(VelocityConnectorPlugin plugin, String name) { 49 | this(plugin, name, null); 50 | } 51 | 52 | public SubCommand(VelocityConnectorPlugin plugin, String usage, String permission, String... aliases) { 53 | this.plugin = plugin; 54 | String[] usageParts = usage.split(" ", 2); 55 | this.name = usageParts[0]; 56 | this.usage = usageParts.length > 1 ? usageParts[1] : ""; 57 | this.permission = permission; 58 | this.aliases = new ArrayList<>(); 59 | this.aliases.add(name); 60 | Collections.addAll(this.aliases, aliases); 61 | } 62 | 63 | public void registerSubCommand(SubCommand subCommand) { 64 | subCommands.put(subCommand.getName().toLowerCase(Locale.ROOT), subCommand); 65 | for (String alias : subCommand.getAliases()) { 66 | subCommandAliases.put(alias.toLowerCase(Locale.ROOT), subCommand); 67 | } 68 | } 69 | 70 | public SubCommand getSubCommand(String name) { 71 | SubCommand subCommand = subCommands.get(name.toLowerCase(Locale.ROOT)); 72 | if (subCommand == null) { 73 | return subCommandAliases.get(name.toLowerCase(Locale.ROOT)); 74 | } 75 | return subCommand; 76 | } 77 | 78 | @Override 79 | public void execute(Invocation invocation) { 80 | if (!hasPermission(invocation.source())) { 81 | invocation.source().sendMessage(Component.text("No permission").color(NamedTextColor.RED)); 82 | return; 83 | } 84 | if (!run(invocation.source(), invocation.alias(), invocation.arguments()) && getUsage() != null) { 85 | invocation.source().sendMessage(Component.text("Usage: /" + invocation.alias() + " " + getUsage())); 86 | } 87 | } 88 | 89 | public boolean run(CommandSource sender, String alias, String[] args) { 90 | if (args.length == 0) { 91 | return false; 92 | } 93 | SubCommand subCommand = getSubCommand(args[0]); 94 | if (subCommand != null) { 95 | subCommand.execute(new SubInvocation(sender, alias + " " + args[0], Arrays.copyOfRange(args, 1, args.length))); 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | @Override 102 | public List suggest(Invocation invocation) { 103 | return onTabComplete(invocation.source(), invocation.arguments()); 104 | } 105 | 106 | public List onTabComplete(CommandSource sender, String[] args) { 107 | if (!hasPermission(sender)) { 108 | return Collections.emptyList(); 109 | } 110 | if (args.length == 0 || args[0].isEmpty()) { 111 | return new ArrayList<>(subCommands.keySet()); 112 | } 113 | SubCommand subCommand = getSubCommand(args[0]); 114 | if (subCommand != null && subCommand.hasPermission(sender)) { 115 | return subCommand.onTabComplete(sender, Arrays.copyOfRange(args, 1, args.length)); 116 | } 117 | List completions = new ArrayList<>(); 118 | for (Map.Entry e : subCommands.entrySet()) { 119 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && e.getValue().hasPermission(sender)) { 120 | completions.add(e.getKey()); 121 | } 122 | } 123 | for (Map.Entry e : subCommandAliases.entrySet()) { 124 | if (e.getKey().startsWith(args[0].toLowerCase(Locale.ROOT)) && e.getValue().hasPermission(sender)) { 125 | completions.add(e.getKey()); 126 | } 127 | } 128 | return completions; 129 | } 130 | 131 | @Override 132 | public boolean hasPermission(Invocation invocation) { 133 | return hasPermission(invocation.source()); 134 | } 135 | 136 | public boolean hasPermission(CommandSource source) { 137 | return getPermission() == null || getPermission().isEmpty() || source.hasPermission(getPermission()); 138 | } 139 | 140 | public Map getSubCommands() { 141 | return subCommands; 142 | } 143 | 144 | public VelocityConnectorPlugin getPlugin() { 145 | return plugin; 146 | } 147 | 148 | public String getName() { 149 | return name; 150 | } 151 | 152 | public String getUsage() { 153 | return usage; 154 | } 155 | 156 | public String getPermission() { 157 | return permission; 158 | } 159 | 160 | @Override 161 | public Collection getAliases() { 162 | return aliases; 163 | } 164 | 165 | @Override 166 | public Collection> getHints() { 167 | return Collections.emptySet(); 168 | } 169 | 170 | private class SubInvocation implements Invocation { 171 | private final CommandSource source; 172 | private final String alias; 173 | private final String[] args; 174 | 175 | public SubInvocation(CommandSource source, String alias, String[] args) { 176 | this.source = source; 177 | this.alias = alias; 178 | this.args = args; 179 | } 180 | 181 | @Override 182 | public String alias() { 183 | return alias; 184 | } 185 | 186 | @Override 187 | public CommandSource source() { 188 | return source; 189 | } 190 | 191 | @Override 192 | public String @NonNull [] arguments() { 193 | return args; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/TeleportCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.command.CommandSource; 22 | import com.velocitypowered.api.proxy.Player; 23 | import com.velocitypowered.api.proxy.server.RegisteredServer; 24 | import de.themoep.connectorplugin.LocationInfo; 25 | import net.kyori.adventure.text.Component; 26 | import net.kyori.adventure.text.format.NamedTextColor; 27 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 28 | 29 | import java.util.Collections; 30 | import java.util.List; 31 | import java.util.Locale; 32 | import java.util.stream.Collectors; 33 | 34 | public class TeleportCommand extends SubCommand { 35 | public TeleportCommand(ConnectorCommand parent) { 36 | super(parent.getPlugin(), "teleport [ [ ]]", parent.getPermission() + ".teleport", "tp", "send"); 37 | } 38 | 39 | @Override 40 | public boolean run(CommandSource sender, String alias, String[] args) { 41 | if (args.length < 2) { 42 | return false; 43 | } 44 | 45 | Player player = plugin.getProxy().getPlayer(args[0]).orElse(null); 46 | if (player == null) { 47 | sender.sendMessage(Component.text("No player with the name " + args[0] + " found!").color(NamedTextColor.RED)); 48 | return true; 49 | } 50 | 51 | RegisteredServer server = plugin.getProxy().getServer(args[1]).orElse(null); 52 | if (server == null) { 53 | sender.sendMessage(Component.text("No server with the name " + args[1] + " found!").color(NamedTextColor.RED)); 54 | return true; 55 | } 56 | 57 | if (args.length == 2) { 58 | player.createConnectionRequest(server).connect().thenAccept(result -> { 59 | if (result.isSuccessful()) { 60 | sender.sendMessage(Component.text("Connected player " + player.getUsername() + " to server " + server.getServerInfo().getName()).color(NamedTextColor.GREEN)); 61 | } else { 62 | sender.sendMessage(Component.text("Error while connecting player " + player.getUsername() + " to server " + server.getServerInfo().getName() + ": " + result.getReasonComponent().orElse(Component.empty())).color(NamedTextColor.RED)); 63 | } 64 | }); 65 | return true; 66 | } 67 | 68 | if (args.length == 3) { 69 | plugin.getBridge().teleport(player.getUsername(), server.getServerInfo().getName(), args[2], m -> sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(m))) 70 | .thenAccept(success -> { 71 | if (!success) { 72 | sender.sendMessage(Component.text("Error while teleporting...").color(NamedTextColor.RED)); 73 | } 74 | }); 75 | return true; 76 | } 77 | 78 | if (args.length < 6) { 79 | return false; 80 | } 81 | 82 | try { 83 | LocationInfo location = new LocationInfo( 84 | server.getServerInfo().getName(), 85 | args[2], 86 | Double.parseDouble(args[3]), 87 | Double.parseDouble(args[4]), 88 | Double.parseDouble(args[5]), 89 | args.length > 6 ? Float.parseFloat(args[6]) : 0, 90 | args.length > 7 ? Float.parseFloat(args[7]) : 0 91 | ); 92 | 93 | plugin.getBridge().teleport(player.getUsername(), location, m -> sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(m))) 94 | .thenAccept(success -> { 95 | if (!success) { 96 | sender.sendMessage(Component.text("Error while teleporting...").color(NamedTextColor.RED)); 97 | } 98 | }); 99 | return true; 100 | } catch (IllegalArgumentException e) { 101 | sender.sendMessage(Component.text("Error while parsing input! " + e.getMessage()).color(NamedTextColor.RED)); 102 | return false; 103 | } 104 | } 105 | 106 | @Override 107 | public List onTabComplete(CommandSource sender, String[] args) { 108 | if (!hasPermission(sender)) { 109 | return Collections.emptyList(); 110 | } 111 | if (args.length == 0) { 112 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 113 | } else if (args.length == 1) { 114 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 115 | } else if (args.length == 2) { 116 | return plugin.getProxy().getAllServers().stream().map(s -> s.getServerInfo().getName()).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 117 | } 118 | return Collections.emptyList(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/commands/TeleportToPlayerCommand.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.commands; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.command.CommandSource; 22 | import com.velocitypowered.api.proxy.Player; 23 | import net.kyori.adventure.text.Component; 24 | import net.kyori.adventure.text.format.NamedTextColor; 25 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 26 | 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.Locale; 30 | import java.util.stream.Collectors; 31 | 32 | public class TeleportToPlayerCommand extends SubCommand { 33 | public TeleportToPlayerCommand(ConnectorCommand parent) { 34 | super(parent.getPlugin(), "teleporttoplayer []", parent.getPermission() + ".teleporttoplayer", "teleportplayer"); 35 | } 36 | 37 | @Override 38 | public boolean run(CommandSource sender, String alias, String[] args) { 39 | String playerName; 40 | String targetName; 41 | if (args.length == 1 && sender instanceof Player) { 42 | playerName = ((Player) sender).getUsername(); 43 | targetName = args[0]; 44 | } else if (args.length == 2) { 45 | playerName = args[0]; 46 | targetName = args[1]; 47 | } else { 48 | return false; 49 | } 50 | 51 | Player player = plugin.getProxy().getPlayer(playerName).orElse(null); 52 | if (player == null) { 53 | sender.sendMessage(Component.text("No player with the name " + playerName + " found!").color(NamedTextColor.RED)); 54 | return true; 55 | } 56 | 57 | Player target = plugin.getProxy().getPlayer(targetName).orElse(null); 58 | if (target == null) { 59 | sender.sendMessage(Component.text("No player with the name " + targetName + " found!").color(NamedTextColor.RED)); 60 | return true; 61 | } 62 | 63 | plugin.getBridge().teleport(player.getUsername(), target.getUsername(), m -> sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(m))) 64 | .thenAccept(success -> { 65 | if (!success) { 66 | sender.sendMessage(Component.text("Error while teleporting...").color(NamedTextColor.RED)); 67 | } 68 | }); 69 | return true; 70 | } 71 | 72 | @Override 73 | public List onTabComplete(CommandSource sender, String[] args) { 74 | if (!hasPermission(sender)) { 75 | return Collections.emptyList(); 76 | } 77 | if (args.length == 0) { 78 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 79 | } else if (args.length == 1) { 80 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).filter(s -> s.startsWith(args[0].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 81 | } else if (args.length == 2) { 82 | return plugin.getProxy().getAllPlayers().stream().map(Player::getUsername).filter(s -> s.startsWith(args[1].toLowerCase(Locale.ROOT))).sorted(String::compareToIgnoreCase).collect(Collectors.toList()); 83 | } 84 | return Collections.emptyList(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/connector/MqttConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.MqttConnection; 24 | 25 | public class MqttConnector extends VelocityConnector { 26 | private final MqttConnection connection; 27 | 28 | public MqttConnector(VelocityConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new MqttConnection( 31 | plugin, 32 | plugin.getConfig().getString("mqtt.broker-uri"), 33 | plugin.getConfig().getString("mqtt.client-id", null), 34 | plugin.getConfig().getString("mqtt.username"), 35 | plugin.getConfig().getString("mqtt.password"), 36 | plugin.getConfig().getInt("mqtt.keep-alive"), 37 | this::handle 38 | ); 39 | } 40 | 41 | @Override 42 | protected void sendDataImplementation(String targetData, Message message) { 43 | connection.sendMessage(targetData, message); 44 | } 45 | 46 | @Override 47 | public void close() { 48 | connection.close(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/connector/PluginMessageConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.google.common.collect.Multimap; 22 | import com.google.common.collect.MultimapBuilder; 23 | import com.google.common.io.ByteArrayDataInput; 24 | import com.google.common.io.ByteArrayDataOutput; 25 | import com.google.common.io.ByteStreams; 26 | import com.velocitypowered.api.event.Subscribe; 27 | import com.velocitypowered.api.event.connection.PluginMessageEvent; 28 | import com.velocitypowered.api.event.player.ServerPostConnectEvent; 29 | import com.velocitypowered.api.proxy.Player; 30 | import com.velocitypowered.api.proxy.messages.ChannelIdentifier; 31 | import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; 32 | import com.velocitypowered.api.proxy.server.RegisteredServer; 33 | import de.themoep.connectorplugin.connector.Message; 34 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 35 | import de.themoep.connectorplugin.connector.VersionMismatchException; 36 | 37 | public class PluginMessageConnector extends VelocityConnector { 38 | 39 | private final ChannelIdentifier messageChannel; 40 | private final Multimap messageQueue = MultimapBuilder.hashKeys().linkedListValues().build(); 41 | 42 | public PluginMessageConnector(VelocityConnectorPlugin plugin) { 43 | super(plugin, true); 44 | messageChannel = MinecraftChannelIdentifier.from(plugin.getMessageChannel()); 45 | plugin.getProxy().getChannelRegistrar().register(messageChannel); 46 | } 47 | 48 | @Subscribe 49 | public void onPluginMessage(PluginMessageEvent event) { 50 | if (!event.getResult().isAllowed() || !event.getIdentifier().equals(messageChannel)) { 51 | return; 52 | } 53 | 54 | event.setResult(PluginMessageEvent.ForwardResult.handled()); 55 | if (event.getSource() instanceof Player) { 56 | return; 57 | } 58 | 59 | ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); 60 | String group = in.readUTF(); 61 | 62 | String target = in.readUTF(); 63 | 64 | int messageLength = in.readInt(); 65 | byte[] messageData = new byte[messageLength]; 66 | in.readFully(messageData); 67 | try { 68 | Message message = Message.fromByteArray(group, messageData); 69 | switch (message.getTarget()) { 70 | case ALL_WITH_PLAYERS: 71 | sendToAllWithPlayers(event.getData(), null); 72 | break; 73 | case ALL_QUEUE: 74 | sendToAllAndQueue(event.getData(), null); 75 | break; 76 | case OTHERS_WITH_PLAYERS: 77 | if (((Player) event.getTarget()).getCurrentServer().isPresent()) { 78 | sendToAllWithPlayers(event.getData(), ((Player) event.getTarget()).getCurrentServer().get().getServer()); 79 | } else { 80 | sendToAllWithPlayers(event.getData(), null); 81 | } 82 | break; 83 | case OTHERS_QUEUE: 84 | if (((Player) event.getTarget()).getCurrentServer().isPresent()) { 85 | sendToAllAndQueue(event.getData(), ((Player) event.getTarget()).getCurrentServer().get().getServer()); 86 | } else { 87 | sendToAllAndQueue(event.getData(), null); 88 | } 89 | break; 90 | case SERVER: 91 | if (!target.isEmpty()) { 92 | RegisteredServer server = getTargetServer(target); 93 | if (server != null) { 94 | if (!server.sendPluginMessage(messageChannel, event.getData())) { 95 | messageQueue.put(server.getServerInfo().getName(), event.getData()); 96 | } 97 | } else { 98 | plugin.logDebug(target + " doesn't exist?"); 99 | } 100 | } else { 101 | plugin.logError(message.getTarget() + " message target requires explicit target!"); 102 | } 103 | break; 104 | case PROXY: 105 | case ALL_PROXIES: 106 | handle(target, message); 107 | break; 108 | default: 109 | plugin.logError("Receiving " + message.getTarget() + " is not supported!"); 110 | } 111 | } catch (IllegalArgumentException e) { 112 | plugin.logError("Invalid message target! " + e.getMessage()); 113 | } catch (VersionMismatchException e) { 114 | plugin.logWarning(e.getMessage() + ". Ignoring message!"); 115 | } 116 | } 117 | 118 | private void sendToAllWithPlayers(byte[] data, RegisteredServer excludedServer) { 119 | sendToAll(data, false, excludedServer); 120 | } 121 | 122 | private void sendToAllAndQueue(byte[] data, RegisteredServer excludedServer) { 123 | sendToAll(data, true, excludedServer); 124 | } 125 | 126 | private void sendToAll(byte[] data, boolean queue, RegisteredServer excludedServer) { 127 | for (RegisteredServer server : plugin.getProxy().getAllServers()) { 128 | if (excludedServer == null || excludedServer != server) { 129 | if (!server.sendPluginMessage(messageChannel, data) && queue) { 130 | messageQueue.put(server.getServerInfo().getName(), data); 131 | } 132 | } 133 | } 134 | } 135 | 136 | @Subscribe 137 | public void onPlayerServerConnected(ServerPostConnectEvent event) { 138 | event.getPlayer().getCurrentServer().ifPresent(server -> { 139 | for (byte[] data : messageQueue.removeAll(server.getServerInfo().getName())) { 140 | server.sendPluginMessage(messageChannel, data); 141 | } 142 | }); 143 | } 144 | 145 | @Override 146 | public void sendDataImplementation(String targetData, Message message) { 147 | byte[] messageData = message.writeToByteArray(); 148 | 149 | ByteArrayDataOutput out = ByteStreams.newDataOutput(); 150 | out.writeUTF(message.getGroup()); 151 | out.writeUTF(targetData); 152 | out.writeInt(messageData.length); 153 | out.write(messageData); 154 | byte[] dataToSend = out.toByteArray(); 155 | 156 | RegisteredServer server = getTargetServer(targetData); 157 | 158 | switch (message.getTarget()) { 159 | case ALL_WITH_PLAYERS: 160 | sendToAllWithPlayers(dataToSend, null); 161 | break; 162 | case ALL_QUEUE: 163 | sendToAllAndQueue(dataToSend, null); 164 | break; 165 | case OTHERS_WITH_PLAYERS: 166 | sendToAllWithPlayers(dataToSend, server); 167 | break; 168 | case OTHERS_QUEUE: 169 | sendToAllAndQueue(dataToSend, server); 170 | break; 171 | case SERVER: 172 | if (server != null) { 173 | server.sendPluginMessage(messageChannel, dataToSend); 174 | } else { 175 | throw new UnsupportedOperationException("Could not send data to " + message.getTarget() + " as target server wasn't found from " + targetData + "!"); 176 | } 177 | break; 178 | default: 179 | throw new UnsupportedOperationException("Sending to " + message.getTarget() + " is not supported!"); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/connector/RedisConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 22 | import de.themoep.connectorplugin.connector.Message; 23 | import de.themoep.connectorplugin.connector.RedisConnection; 24 | 25 | public class RedisConnector extends VelocityConnector { 26 | private final RedisConnection connection; 27 | 28 | public RedisConnector(VelocityConnectorPlugin plugin) { 29 | super(plugin, false); 30 | connection = new RedisConnection( 31 | plugin, 32 | plugin.getConfig().getString("redis.uri"), 33 | plugin.getConfig().getString("redis.host"), 34 | plugin.getConfig().getInt("redis.port"), 35 | plugin.getConfig().getInt("redis.db"), 36 | plugin.getConfig().getString("redis.password"), 37 | plugin.getConfig().getLong("redis.timeout"), 38 | this::handle 39 | ); 40 | } 41 | 42 | @Override 43 | protected void sendDataImplementation(String targetData, Message message) { 44 | connection.sendMessage(targetData, message); 45 | } 46 | 47 | @Override 48 | public void close() { 49 | connection.close(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /velocity/src/main/java/de/themoep/connectorplugin/velocity/connector/VelocityConnector.java: -------------------------------------------------------------------------------- 1 | package de.themoep.connectorplugin.velocity.connector; 2 | 3 | /* 4 | * ConnectorPlugin 5 | * Copyright (C) 2021 Max Lee aka Phoenix616 (max@themoep.de) 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Affero General Public License as published 9 | * by the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Affero General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import com.velocitypowered.api.proxy.Player; 22 | import com.velocitypowered.api.proxy.server.RegisteredServer; 23 | import de.themoep.connectorplugin.connector.Message; 24 | import de.themoep.connectorplugin.velocity.VelocityConnectorPlugin; 25 | import de.themoep.connectorplugin.connector.Connector; 26 | 27 | public abstract class VelocityConnector extends Connector { 28 | 29 | public VelocityConnector(VelocityConnectorPlugin plugin, boolean requiresPlayer) { 30 | super(plugin, requiresPlayer); 31 | } 32 | 33 | protected Player getReceiverImplementation(String name) { 34 | return plugin.getProxy().getPlayer(name).orElse(null); 35 | } 36 | 37 | protected RegisteredServer getTargetServer(String target) { 38 | if (target.startsWith(SERVER_PREFIX)) { 39 | return plugin.getProxy().getServer(target.substring(SERVER_PREFIX.length())).orElse(null); 40 | } else if (target.startsWith(PLAYER_PREFIX)) { 41 | Player player = getReceiver(target.substring(PLAYER_PREFIX.length())); 42 | if (player != null && player.getCurrentServer().isPresent()) { 43 | return player.getCurrentServer().get().getServer(); 44 | } 45 | } 46 | return null; 47 | } 48 | 49 | @Override 50 | protected void sendDataImplementation(Object targetData, Message message) { 51 | sendDataImplementation(targetData instanceof String 52 | ? (hasPrefix((String) targetData) 53 | ? (String) targetData 54 | : SERVER_PREFIX + targetData) 55 | : (targetData instanceof Player 56 | ? PLAYER_PREFIX + ((Player) targetData).getUsername() 57 | : ""), message); 58 | } 59 | 60 | protected abstract void sendDataImplementation(String targetData, Message message); 61 | } 62 | -------------------------------------------------------------------------------- /velocity/src/main/resources/velocity-config.yml: -------------------------------------------------------------------------------- 1 | # Plugin debugging 2 | debug: true 3 | 4 | # A unique ID for this proxy 5 | server-id: "change-this" 6 | 7 | # Currently supported types: 8 | # - "plugin_messages" (will not be able to send messages to other proxies or to servers without any players online, messages will be queued) 9 | # - "redis" (does not require any online players) 10 | # - "mqtt" (does not require any online players) 11 | messenger-type: redis 12 | 13 | # Redis messenger settings 14 | redis: 15 | host: localhost 16 | port: 6379 17 | db: 0 18 | timeout: 60 19 | password: password1 20 | 21 | # Mqtt messenger settings 22 | mqtt: 23 | broker-uri: tcp://localhost:1883 24 | keep-alive: 30 25 | username: mqttuser 26 | password: password1 -------------------------------------------------------------------------------- /velocity/src/main/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "connectorplugin", 3 | "name": "ConnectorPlugin", 4 | "version": "${minecraft.plugin.version}", 5 | "main": "de.themoep.connectorplugin.velocity.VelocityConnectorPlugin", 6 | "authors": ["Phoenix616"] 7 | } --------------------------------------------------------------------------------