├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── uiDesigner.xml └── vcs.xml ├── BungeeServerManager.iml ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── pw │ └── teg │ └── bsm │ ├── BungeeServerManager.java │ ├── api │ ├── ServerManagerAPI.java │ └── events │ │ ├── ServerAddEvent.java │ │ ├── ServerEvent.java │ │ ├── ServerModifiedEvent.java │ │ └── ServerRemoveEvent.java │ ├── commands │ └── ServerManagerCommand.java │ └── util │ ├── ConfigHelper.java │ └── ServerHelper.java └── resources └── plugin.yml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos,maven,windows,intellij+all 3 | # Edit at https://www.gitignore.io/?templates=macos,maven,windows,intellij+all 4 | 5 | ### Intellij+all ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | .idea/modules.xml 37 | .idea/*.iml 38 | .idea/modules 39 | *.iml 40 | *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | ### Intellij+all Patch ### 76 | # Ignores the whole .idea folder and all .iml files 77 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 78 | 79 | .idea/ 80 | 81 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 82 | 83 | *.iml 84 | modules.xml 85 | .idea/misc.xml 86 | *.ipr 87 | 88 | # Sonarlint plugin 89 | .idea/sonarlint 90 | 91 | ### macOS ### 92 | # General 93 | .DS_Store 94 | .AppleDouble 95 | .LSOverride 96 | 97 | # Icon must end with two \r 98 | Icon 99 | 100 | # Thumbnails 101 | ._* 102 | 103 | # Files that might appear in the root of a volume 104 | .DocumentRevisions-V100 105 | .fseventsd 106 | .Spotlight-V100 107 | .TemporaryItems 108 | .Trashes 109 | .VolumeIcon.icns 110 | .com.apple.timemachine.donotpresent 111 | 112 | # Directories potentially created on remote AFP share 113 | .AppleDB 114 | .AppleDesktop 115 | Network Trash Folder 116 | Temporary Items 117 | .apdisk 118 | 119 | ### Maven ### 120 | target/ 121 | pom.xml.tag 122 | pom.xml.releaseBackup 123 | pom.xml.versionsBackup 124 | pom.xml.next 125 | release.properties 126 | dependency-reduced-pom.xml 127 | buildNumber.properties 128 | .mvn/timing.properties 129 | .mvn/wrapper/maven-wrapper.jar 130 | .flattened-pom.xml 131 | 132 | ### Windows ### 133 | # Windows thumbnail cache files 134 | Thumbs.db 135 | Thumbs.db:encryptable 136 | ehthumbs.db 137 | ehthumbs_vista.db 138 | 139 | # Dump file 140 | *.stackdump 141 | 142 | # Folder config file 143 | [Dd]esktop.ini 144 | 145 | # Recycle Bin used on file shares 146 | $RECYCLE.BIN/ 147 | 148 | # Windows Installer files 149 | *.cab 150 | *.msi 151 | *.msix 152 | *.msm 153 | *.msp 154 | 155 | # Windows shortcuts 156 | *.lnk 157 | 158 | # End of https://www.gitignore.io/api/macos,maven,windows,intellij+all -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | BungeeServerManager -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Android 60 | 61 | 62 | Android Lint 63 | 64 | 65 | Code maturity issues 66 | 67 | 68 | Code style issues 69 | 70 | 71 | Control FlowGroovy 72 | 73 | 74 | Control flow issues 75 | 76 | 77 | Data flow issuesGroovy 78 | 79 | 80 | Error handling 81 | 82 | 83 | General 84 | 85 | 86 | Groovy 87 | 88 | 89 | J2ME issues 90 | 91 | 92 | Java language level issues 93 | 94 | 95 | Java language level migration aids 96 | 97 | 98 | Javadoc issues 99 | 100 | 101 | Memory issues 102 | 103 | 104 | Performance issues 105 | 106 | 107 | Plugin DevKit 108 | 109 | 110 | Portability issues 111 | 112 | 113 | Potentially confusing code constructsGroovy 114 | 115 | 116 | Probable bugs 117 | 118 | 119 | Probable bugsGroovy 120 | 121 | 122 | Security issues 123 | 124 | 125 | Threading issues 126 | 127 | 128 | Threading issuesGroovy 129 | 130 | 131 | Validity issuesGroovy 132 | 133 | 134 | XPath 135 | 136 | 137 | XSLT 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 166 | 167 | 168 | 169 | 170 | 171 | 1.7 172 | 173 | 178 | 179 | 180 | 181 | 182 | 183 | 1.7 184 | 185 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BungeeServerManager.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tavon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BungeeServerManager 2 | Add, remove, and edit servers without restarting BungeeCord! 3 | 4 | Have you ever wanted to add or remove a server, change an ip address or port, or even change the motd without restarting BungeeCord? Well now you can! (yw) 5 | 6 | ### Download 7 | Download here on Spigot: http://www.spigotmc.org/resources/bungeeservermanager.7388/ 8 | 9 | ### Installing 10 | Just drag the plugin file into your BungeeCord's plugin server. 11 | 12 | ### Permissions 13 | There's only one permission: `servermanager.use` 14 | 15 | ### Commands 16 | ![Commands](http://i.imgur.com/Jzng2aC.png) 17 | 18 | ### FAQ 19 | ###### Where's the config?
20 | There is no configuration for this plugin, if you have any suggestions feel free to let me know. 21 | 22 | ###### What happens to players on a server that I remove?
23 | Players that are on a server that you remove are transported to the `fallback-server` set in BungeeCord's config. 24 | 25 | ###### My question isn't in the FAQ, what should I do? 26 | Create an issue with your question. 27 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | UTF-8 9 | 1.7 10 | 1.7 11 | 12 | 13 | pw.teg 14 | BungeeServerManager 15 | 1.1.3-SNAPSHOT 16 | 17 | 18 | 19 | bungeecord-repo 20 | https://oss.sonatype.org/content/repositories/snapshots 21 | 22 | 23 | 24 | 25 | 26 | net.md-5 27 | bungeecord-api 28 | 1.15-SNAPSHOT 29 | 30 | 31 | 32 | 33 | BungeeServerManager 34 | src/main/java 35 | 36 | 37 | true 38 | src/main/resources 39 | 40 | *.yml 41 | *.jar 42 | **/*.yml 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/BungeeServerManager.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm; 2 | 3 | import net.md_5.bungee.api.plugin.Plugin; 4 | import pw.teg.bsm.api.ServerManagerAPI; 5 | import pw.teg.bsm.commands.ServerManagerCommand; 6 | 7 | public class BungeeServerManager extends Plugin { 8 | 9 | private static BungeeServerManager instance; 10 | private static ServerManagerAPI api; 11 | 12 | @Override 13 | public void onEnable() { 14 | instance = this; 15 | 16 | api = new ServerManagerAPI(); 17 | 18 | getProxy().getPluginManager().registerCommand(this, new ServerManagerCommand()); 19 | 20 | } 21 | 22 | public static BungeeServerManager get() { 23 | return instance; 24 | } 25 | 26 | public static ServerManagerAPI getApi() { 27 | return api; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/api/ServerManagerAPI.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.api; 2 | 3 | import net.md_5.bungee.api.ProxyServer; 4 | import net.md_5.bungee.api.config.ServerInfo; 5 | import pw.teg.bsm.api.events.ServerAddEvent; 6 | import pw.teg.bsm.api.events.ServerRemoveEvent; 7 | import pw.teg.bsm.util.ServerHelper; 8 | 9 | import java.net.InetSocketAddress; 10 | import java.util.Collection; 11 | 12 | public class ServerManagerAPI { 13 | 14 | public boolean addServer(String name, InetSocketAddress ipAddress, String motd, boolean restricted) { 15 | if (ServerHelper.getServerInfo(name) != null) { 16 | return false; 17 | } 18 | 19 | ServerInfo serverInfo = ProxyServer.getInstance().constructServerInfo(name, ipAddress, motd, restricted); 20 | ServerAddEvent addEvent = new ServerAddEvent(serverInfo, null); 21 | 22 | if (addEvent.isCancelled()) { 23 | return false; 24 | } 25 | 26 | ServerHelper.addServer(addEvent.getServerModified()); 27 | return true; 28 | } 29 | 30 | public boolean removeServer(String name) { 31 | ServerInfo serverInfo = ServerHelper.getServerInfo(name); 32 | 33 | if (serverInfo == null) { 34 | return false; 35 | } 36 | 37 | ServerRemoveEvent addEvent = new ServerRemoveEvent(serverInfo, null); 38 | 39 | if (addEvent.isCancelled()) { 40 | return false; 41 | } 42 | 43 | ServerHelper.removeServer(addEvent.getServerModified().getName()); 44 | return true; 45 | } 46 | 47 | public boolean serverExists(String name) { 48 | return ServerHelper.serverExists(name); 49 | } 50 | 51 | public Collection listServers() { 52 | return ServerHelper.getServers().values(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/api/events/ServerAddEvent.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.api.events; 2 | 3 | import net.md_5.bungee.api.CommandSender; 4 | import net.md_5.bungee.api.config.ServerInfo; 5 | import net.md_5.bungee.api.plugin.Cancellable; 6 | 7 | public class ServerAddEvent extends ServerEvent implements Cancellable { 8 | 9 | private CommandSender sender; 10 | private boolean cancelled; 11 | 12 | public ServerAddEvent(ServerInfo serverModified, CommandSender sender) { 13 | super(serverModified); 14 | this.sender = sender; 15 | } 16 | 17 | public CommandSender getSender() { 18 | return sender; 19 | } 20 | 21 | @Override 22 | public boolean isCancelled() { 23 | return cancelled; 24 | } 25 | 26 | @Override 27 | public void setCancelled(boolean cancelled) { 28 | this.cancelled = cancelled; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/api/events/ServerEvent.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.api.events; 2 | 3 | import net.md_5.bungee.api.config.ServerInfo; 4 | import net.md_5.bungee.api.plugin.Event; 5 | 6 | public abstract class ServerEvent extends Event { 7 | 8 | private ServerInfo serverModified; 9 | 10 | public ServerEvent(ServerInfo serverModified) { 11 | this.serverModified = serverModified; 12 | } 13 | 14 | public ServerInfo getServerModified() { 15 | return serverModified; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/api/events/ServerModifiedEvent.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.api.events; 2 | 3 | import net.md_5.bungee.api.CommandSender; 4 | import net.md_5.bungee.api.config.ServerInfo; 5 | import net.md_5.bungee.api.plugin.Cancellable; 6 | 7 | public class ServerModifiedEvent extends ServerEvent implements Cancellable { 8 | 9 | private CommandSender sender; 10 | private ServerField modified; 11 | private T newValue; 12 | private boolean cancelled; 13 | 14 | public ServerModifiedEvent(ServerInfo serverModified, CommandSender sender, ServerField modified, T newValue) { 15 | super(serverModified); 16 | this.sender = sender; 17 | this.modified = modified; 18 | this.newValue = newValue; 19 | } 20 | 21 | public CommandSender getSender() { 22 | return sender; 23 | } 24 | 25 | public ServerField getModified() { 26 | return modified; 27 | } 28 | 29 | public T getNewValue() { 30 | return newValue; 31 | } 32 | 33 | public void setNewValue(T newValue) { 34 | this.newValue = newValue; 35 | } 36 | 37 | @Override 38 | public boolean isCancelled() { 39 | return cancelled; 40 | } 41 | 42 | @Override 43 | public void setCancelled(boolean cancelled) { 44 | this.cancelled = cancelled; 45 | } 46 | 47 | public enum ServerField { 48 | NAME, IP, MOTD, RESTRICTED 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/api/events/ServerRemoveEvent.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.api.events; 2 | 3 | import net.md_5.bungee.api.CommandSender; 4 | import net.md_5.bungee.api.config.ServerInfo; 5 | import net.md_5.bungee.api.plugin.Cancellable; 6 | 7 | public class ServerRemoveEvent extends ServerEvent implements Cancellable { 8 | 9 | private CommandSender sender; 10 | private boolean cancelled; 11 | 12 | public ServerRemoveEvent(ServerInfo serverModified, CommandSender sender) { 13 | super(serverModified); 14 | this.sender = sender; 15 | } 16 | 17 | public CommandSender getSender() { 18 | return sender; 19 | } 20 | 21 | @Override 22 | public boolean isCancelled() { 23 | return cancelled; 24 | } 25 | 26 | @Override 27 | public void setCancelled(boolean cancelled) { 28 | this.cancelled = cancelled; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/commands/ServerManagerCommand.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.commands; 2 | 3 | import com.google.common.base.Joiner; 4 | import net.md_5.bungee.Util; 5 | import net.md_5.bungee.api.ChatColor; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.ProxyServer; 8 | import net.md_5.bungee.api.chat.*; 9 | import net.md_5.bungee.api.config.ListenerInfo; 10 | import net.md_5.bungee.api.config.ServerInfo; 11 | import net.md_5.bungee.api.plugin.Command; 12 | import pw.teg.bsm.BungeeServerManager; 13 | import pw.teg.bsm.api.events.ServerAddEvent; 14 | import pw.teg.bsm.api.events.ServerModifiedEvent; 15 | import pw.teg.bsm.api.events.ServerRemoveEvent; 16 | import pw.teg.bsm.util.ConfigHelper; 17 | import pw.teg.bsm.util.ServerHelper; 18 | 19 | import java.net.InetSocketAddress; 20 | import java.net.SocketAddress; 21 | import java.util.*; 22 | 23 | public class ServerManagerCommand extends Command { 24 | 25 | private final String prefix = ChatColor.GRAY + "[" + ChatColor.GREEN + "ServerManager" + ChatColor.GRAY + "] "; 26 | 27 | public ServerManagerCommand() { 28 | super("servermanager", "servermanager.use", "svm"); 29 | } 30 | 31 | @Override 32 | public void execute(CommandSender sender, String[] args) { 33 | if (args.length == 0) { 34 | sendHelp(sender); 35 | return; 36 | } 37 | 38 | if (args[0].equalsIgnoreCase("help")) { 39 | sendHelp(sender); 40 | return; 41 | } 42 | 43 | if (args[0].equalsIgnoreCase("list")) { 44 | if (ServerHelper.getServers().isEmpty()) { 45 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "No servers.")); 46 | return; 47 | } 48 | 49 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Servers: " + ChatColor.GREEN + Joiner.on(ChatColor.GRAY + ", " + ChatColor.GREEN).join(ServerHelper.getServers().keySet()))); 50 | return; 51 | } 52 | 53 | if (args[0].equalsIgnoreCase("info")) { 54 | if (args.length < 2) { 55 | sendUsage(sender, "/svm info "); 56 | return; 57 | } 58 | 59 | if (!ServerHelper.serverExists(args[1])) { 60 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "The server " + ChatColor.GREEN + args[1] + ChatColor.GRAY + " does not exist.")); 61 | return; 62 | } 63 | 64 | ServerInfo info = ServerHelper.getServerInfo(args[1]); 65 | 66 | if (info == null) { 67 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "The server " + ChatColor.GREEN + args[1] + ChatColor.GRAY + " does not exist.")); 68 | return; 69 | } 70 | 71 | Set forcedHosts = new HashSet<>(); 72 | 73 | for (ListenerInfo listenerInfo : BungeeServerManager.get().getProxy().getConfig().getListeners()) { 74 | for (Map.Entry entry : listenerInfo.getForcedHosts().entrySet()) { 75 | if (entry.getValue().equalsIgnoreCase(info.getName())) { 76 | forcedHosts.add(entry.getKey()); 77 | } 78 | } 79 | } 80 | 81 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GRAY + "--- " + ChatColor.GREEN + info.getName() + " Info" + ChatColor.GRAY + " ---")); 82 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Name: " + ChatColor.GRAY + info.getName())); 83 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Address: " + ChatColor.GRAY + ConfigHelper.socketAddressToString(info.getSocketAddress()))); 84 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Motd: " + ChatColor.GRAY + info.getMotd())); 85 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Restricted: " + ChatColor.GRAY + info.isRestricted())); 86 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Player Count: " + ChatColor.GRAY + info.getPlayers().size())); 87 | if (!forcedHosts.isEmpty()) { 88 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GREEN + "Forced Hosts: " + ChatColor.GRAY + Joiner.on(", ").join(forcedHosts))); 89 | } 90 | sender.sendMessage(TextComponent.fromLegacyText(ChatColor.GRAY + "--------")); 91 | return; 92 | } 93 | 94 | if (args[0].equalsIgnoreCase("add")) { 95 | if (args.length < 2) { 96 | sendUsage(sender, "/svm add [hostname]"); 97 | return; 98 | } 99 | 100 | if (ServerHelper.serverExists(args[1])) { 101 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "The server " + ChatColor.GREEN + args[1] + ChatColor.GRAY + " already exists.")); 102 | return; 103 | } 104 | 105 | SocketAddress address = new InetSocketAddress(22565); 106 | boolean customAddress = false; 107 | 108 | if (args.length >= 3) { 109 | address = getIp(args[2]); 110 | customAddress = true; 111 | 112 | if (address == null) { 113 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Invalid address " + ChatColor.GREEN + args[2] + ChatColor.GRAY + ". Here's an example: " + ChatColor.GREEN + "127.0.0.1:25565")); 114 | return; 115 | } 116 | } 117 | 118 | ServerInfo info = ProxyServer.getInstance().constructServerInfo(args[1], address, "", false); 119 | ServerAddEvent addEvent = new ServerAddEvent(info, sender); 120 | 121 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(addEvent); 122 | 123 | if (addEvent.isCancelled()) { 124 | return; 125 | } 126 | 127 | ServerHelper.addServer(addEvent.getServerModified()); 128 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Added a server with the name " + 129 | ChatColor.GREEN + addEvent.getServerModified().getName() + (customAddress ? " and address " + 130 | ChatColor.GREEN + args[2] + ChatColor.GREEN + "." : ""))); 131 | return; 132 | } 133 | 134 | if (args[0].equalsIgnoreCase("remove")) { 135 | if (args.length < 2) { 136 | sendUsage(sender, "/svm remove "); 137 | return; 138 | } 139 | 140 | if (!ServerHelper.serverExists(args[1])) { 141 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "The server " + ChatColor.GREEN + args[1] + ChatColor.GRAY + " does not exist.")); 142 | return; 143 | } 144 | 145 | ServerRemoveEvent removeEvent = new ServerRemoveEvent(ServerHelper.getServerInfo(args[1]), sender); 146 | 147 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(removeEvent); 148 | 149 | if (removeEvent.isCancelled()) { 150 | return; 151 | } 152 | 153 | ServerHelper.removeServer(removeEvent.getServerModified().getName()); 154 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Removed the server " + ChatColor.GREEN + removeEvent.getServerModified().getName())); 155 | return; 156 | } 157 | 158 | if (args[0].equalsIgnoreCase("edit")) { 159 | if (args.length < 2) { 160 | sendUsage(sender, "/svm edit "); 161 | return; 162 | } 163 | 164 | ServerInfo info = ServerHelper.getServerInfo(args[1]); 165 | 166 | if (info == null) { 167 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "The server " + ChatColor.GREEN + args[1] + ChatColor.GRAY + " does not exist.")); 168 | return; 169 | } 170 | 171 | if (args.length == 2) { 172 | sendEditMenu(sender, info.getName()); 173 | return; 174 | } 175 | 176 | if (args[2].equalsIgnoreCase("name")) { 177 | if (args.length < 4) { 178 | sendUsage(sender, "/svm edit " + info.getName() + " name "); 179 | return; 180 | } 181 | 182 | ServerModifiedEvent modifiedEvent = new ServerModifiedEvent<>(info, sender, ServerModifiedEvent.ServerField.NAME, args[3]); 183 | 184 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(modifiedEvent); 185 | 186 | if (modifiedEvent.isCancelled()) { 187 | return; 188 | } 189 | 190 | info = modifiedEvent.getServerModified(); 191 | 192 | ServerHelper.removeServer(info.getName()); 193 | ServerHelper.addServer(ProxyServer.getInstance().constructServerInfo(modifiedEvent.getNewValue(), info.getSocketAddress(), info.getMotd(), false)); 194 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Renamed " + ChatColor.GREEN + info.getName() + ChatColor.GRAY + " to " + ChatColor.GREEN + modifiedEvent.getNewValue() + ChatColor.GRAY + ".")); 195 | return; 196 | } 197 | 198 | if (args[2].equalsIgnoreCase("ip")) { 199 | if (args.length < 4) { 200 | sendUsage(sender, "/svm edit " + info.getName() + " ip "); 201 | return; 202 | } 203 | 204 | SocketAddress address = getIp(args[3]); 205 | 206 | if (address == null) { 207 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Invalid address " + ChatColor.GREEN + args[3] + ChatColor.GRAY + ". Here's an example: " + ChatColor.GREEN + "127.0.0.1:25565")); 208 | return; 209 | } 210 | 211 | ServerModifiedEvent modifiedEvent = new ServerModifiedEvent<>(ServerHelper.getServerInfo(args[1]), sender, ServerModifiedEvent.ServerField.IP, address); 212 | 213 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(modifiedEvent); 214 | 215 | if (modifiedEvent.isCancelled()) { 216 | return; 217 | } 218 | 219 | info = modifiedEvent.getServerModified(); 220 | 221 | ServerHelper.removeServer(info.getName()); 222 | ServerHelper.addServer(ProxyServer.getInstance().constructServerInfo(info.getName(), modifiedEvent.getNewValue(), info.getMotd(), false)); 223 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Set the address of " + ChatColor.GREEN + info.getName() + ChatColor.GRAY + " to " + ChatColor.GREEN + args[3] + ChatColor.GRAY + ".")); 224 | return; 225 | } 226 | 227 | if (args[2].equalsIgnoreCase("motd")) { 228 | if (args.length < 4) { 229 | sendUsage(sender, "/svm edit " + info.getName() + " motd "); 230 | return; 231 | } 232 | 233 | StringBuilder builder = new StringBuilder(); 234 | 235 | for (int i = 3; i < args.length; i++) { 236 | builder.append(args[i]).append(" "); 237 | } 238 | 239 | ServerModifiedEvent modifiedEvent = new ServerModifiedEvent<>(info, sender, ServerModifiedEvent.ServerField.MOTD, ChatColor.translateAlternateColorCodes('&', builder.toString().trim())); 240 | 241 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(modifiedEvent); 242 | 243 | if (modifiedEvent.isCancelled()) { 244 | return; 245 | } 246 | 247 | info = modifiedEvent.getServerModified(); 248 | 249 | ServerHelper.removeServer(info.getName()); 250 | ServerHelper.addServer(ProxyServer.getInstance().constructServerInfo(info.getName(), info.getSocketAddress(), modifiedEvent.getNewValue(), false)); 251 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Set the motd of " + ChatColor.GREEN + info.getName() + ChatColor.GRAY + " to " + ChatColor.GREEN + ChatColor.translateAlternateColorCodes('&', builder.toString().trim()) + ChatColor.GRAY + ".")); 252 | return; 253 | } 254 | 255 | if (args[2].equalsIgnoreCase("restricted")) { 256 | if (args.length < 4) { 257 | sendUsage(sender, "/svm edit " + info.getName() + " restricted "); 258 | return; 259 | } 260 | 261 | ServerModifiedEvent modifiedEvent = new ServerModifiedEvent<>(info, sender, ServerModifiedEvent.ServerField.RESTRICTED, Boolean.parseBoolean(args[3])); 262 | 263 | BungeeServerManager.get().getProxy().getPluginManager().callEvent(modifiedEvent); 264 | 265 | if (modifiedEvent.isCancelled()) { 266 | return; 267 | } 268 | 269 | info = modifiedEvent.getServerModified(); 270 | 271 | ServerHelper.removeServer(info.getName()); 272 | ServerHelper.addServer(ProxyServer.getInstance().constructServerInfo(info.getName(), info.getSocketAddress(), info.getMotd(), modifiedEvent.getNewValue())); 273 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Set the restriction of " + ChatColor.GREEN + info.getName() + ChatColor.GRAY + " to " + ChatColor.GREEN + Boolean.parseBoolean(args[3]) + ChatColor.GRAY + ".")); 274 | return; 275 | } 276 | 277 | if (args[2].equalsIgnoreCase("domain")) { 278 | if (args.length < 5 || (!args[3].equalsIgnoreCase("add") && !args[3].equalsIgnoreCase("remove"))) { 279 | sendUsage(sender, "/svm edit " + info.getName() + " domain "); 280 | return; 281 | } 282 | 283 | boolean addingDomain = args[3].equalsIgnoreCase("add"); 284 | final SocketAddress address = getIp(args[4]); 285 | 286 | if (address == null) { 287 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Invalid address " + ChatColor.GREEN + args[4] + ChatColor.GRAY + ". Here's an example: " + ChatColor.GREEN + "pvp.md-5.net")); 288 | return; 289 | } 290 | 291 | String socketAddressStr = ConfigHelper.socketAddressToString(address, false); 292 | 293 | if (addingDomain) { 294 | ConfigHelper.addForcedHost(address, info); 295 | 296 | for (ListenerInfo listenerInfo : BungeeServerManager.get().getProxy().getConfig().getListeners()) { 297 | listenerInfo.getForcedHosts().put(socketAddressStr, info.getName()); 298 | } 299 | 300 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Added forced host of " + ChatColor.GREEN + 301 | socketAddressStr + ChatColor.GRAY + " for server " + ChatColor.GREEN + 302 | info.getName() + ChatColor.GRAY + ".")); 303 | } else { 304 | boolean found = false; 305 | 306 | ConfigHelper.removeForcedHost(address, info); 307 | 308 | for (ListenerInfo listenerInfo : BungeeServerManager.get().getProxy().getConfig().getListeners()) { 309 | String targetServer = listenerInfo.getForcedHosts().get(socketAddressStr); 310 | 311 | if (targetServer != null && targetServer.equalsIgnoreCase(info.getName())) { 312 | listenerInfo.getForcedHosts().remove(socketAddressStr); 313 | found = true; 314 | } 315 | } 316 | 317 | if (found) { 318 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Removed forced host of " + 319 | ChatColor.GREEN + socketAddressStr + ChatColor.GRAY + " for server " + 320 | ChatColor.GREEN + info.getName() + ChatColor.GRAY + ".")); 321 | } else { 322 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Could not find forced host of " + 323 | ChatColor.GREEN + socketAddressStr + ChatColor.GRAY + " for server " + 324 | ChatColor.GREEN + info.getName() + ChatColor.GRAY + ".")); 325 | } 326 | } 327 | return; 328 | } 329 | 330 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Unknown argument " + ChatColor.GREEN + args[2] + ChatColor.GRAY + " use " + ChatColor.GREEN + "/svm edit " + info.getName() + ChatColor.GRAY + " for help.")); 331 | return; 332 | } 333 | 334 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Unknown argument " + ChatColor.GREEN + args[0] + ChatColor.GRAY + " use " + ChatColor.GREEN + "/svm help" + ChatColor.GRAY + " for help.")); 335 | } 336 | 337 | private void sendHelp(CommandSender sender) { 338 | sender.sendMessage(TextComponent.fromLegacyText(prefix + "Help:")); 339 | sender.sendMessage(getHelpString("/svm help", "Display this help menu")); 340 | sender.sendMessage(getHelpString("/svm list", "List servers")); 341 | sender.sendMessage(getHelpString("/svm info ", "Display info about a server")); 342 | sender.sendMessage(getHelpString("/svm add [hostname]", "Add a server to BungeeCord")); 343 | sender.sendMessage(getHelpString("/svm remove ", "Remove a server from BungeeCord")); 344 | sender.sendMessage(getHelpString("/svm edit ", "Edit a server's information")); 345 | } 346 | 347 | private BaseComponent[] getHelpString(String command, String info) { 348 | return new ComponentBuilder(ChatColor.GREEN + command + ChatColor.GRAY + " - " + info) 349 | .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)) 350 | .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(ChatColor.GREEN + "Click to execute this command"))) 351 | .create(); 352 | } 353 | 354 | private void sendUsage(CommandSender sender, String usage) { 355 | sender.sendMessage(new ComponentBuilder(prefix + "Correct usage: " + ChatColor.GREEN + usage) 356 | .event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "command")) 357 | .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(ChatColor.GREEN + "Click to execute this command"))) 358 | .create()); 359 | } 360 | 361 | private void sendEditMenu(CommandSender sender, String serverName) { 362 | sender.sendMessage(TextComponent.fromLegacyText(prefix + " Edit Help:")); 363 | sender.sendMessage(getHelpString("/svm edit " + serverName + " name ", "Change this server's name")); 364 | sender.sendMessage(getHelpString("/svm edit " + serverName + " ip ", "Change this server's address")); 365 | sender.sendMessage(getHelpString("/svm edit " + serverName + " motd ", "Change this server's motd")); 366 | sender.sendMessage(getHelpString("/svm edit " + serverName + " restricted ", "Change this server's restricted flag")); 367 | sender.sendMessage(getHelpString("/svm edit " + serverName + " domain ", "Add or remove a forced host for this server")); 368 | } 369 | 370 | private SocketAddress getIp(String input) { 371 | try { 372 | return Util.getAddr(input); 373 | } catch (IllegalArgumentException exec) { 374 | return null; 375 | } 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/util/ConfigHelper.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.util; 2 | 3 | import io.netty.channel.unix.DomainSocketAddress; 4 | import net.md_5.bungee.api.ChatColor; 5 | import net.md_5.bungee.api.ProxyServer; 6 | import net.md_5.bungee.api.config.ServerInfo; 7 | import net.md_5.bungee.config.Configuration; 8 | import net.md_5.bungee.config.YamlConfiguration; 9 | import pw.teg.bsm.BungeeServerManager; 10 | 11 | import java.io.*; 12 | import java.net.InetSocketAddress; 13 | import java.net.SocketAddress; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | public class ConfigHelper { 20 | 21 | private static File file; 22 | private static Configuration bungeeConfig; 23 | private static boolean locked; // TODO: This is dumb. Writes are lost when locked 24 | 25 | static { 26 | // Plugins are loaded before the config (on first run) so uhhhhh, here we go 27 | setupConfig(); 28 | 29 | if (locked) { 30 | ProxyServer.getInstance().getScheduler().schedule(BungeeServerManager.get(), new Runnable() { 31 | @Override 32 | public void run() { 33 | setupConfig(); 34 | } 35 | }, 5L, TimeUnit.SECONDS); 36 | } 37 | } 38 | 39 | public static String socketAddressToString(SocketAddress socketAddress) { 40 | return socketAddressToString(socketAddress, true); 41 | } 42 | 43 | public static String socketAddressToString(SocketAddress socketAddress, boolean appendPort) { 44 | String addressString; 45 | 46 | if (socketAddress instanceof DomainSocketAddress) { 47 | addressString = "unix:" + ((DomainSocketAddress) socketAddress).path(); 48 | } else if (socketAddress instanceof InetSocketAddress) { 49 | InetSocketAddress inetAddress = (InetSocketAddress) socketAddress; 50 | 51 | addressString = inetAddress.getHostString(); 52 | 53 | if (appendPort) { 54 | addressString += ":" + inetAddress.getPort(); 55 | } 56 | } else { 57 | addressString = socketAddress.toString(); 58 | } 59 | 60 | return addressString; 61 | } 62 | 63 | public static void addToConfig(ServerInfo serverInfo) { 64 | if (locked) { 65 | return; 66 | } 67 | 68 | bungeeConfig.set("servers." + serverInfo.getName() + ".motd", serverInfo.getMotd().replace(ChatColor.COLOR_CHAR, '&')); 69 | bungeeConfig.set("servers." + serverInfo.getName() + ".address", socketAddressToString(serverInfo.getSocketAddress())); 70 | bungeeConfig.set("servers." + serverInfo.getName() + ".restricted", serverInfo.isRestricted()); 71 | saveConfig(); 72 | } 73 | 74 | public static void removeFromConfig(ServerInfo serverInfo) { 75 | removeFromConfig(serverInfo.getName()); 76 | } 77 | 78 | public static void removeFromConfig(String name) { 79 | if (locked) { 80 | return; 81 | } 82 | 83 | bungeeConfig.set("servers." + name, null); 84 | saveConfig(); 85 | } 86 | 87 | public static void addForcedHost(SocketAddress host, ServerInfo server) { 88 | changeForcedHost(host, false, server); 89 | } 90 | 91 | public static void removeForcedHost(SocketAddress host, ServerInfo server) { 92 | changeForcedHost(host, true, server); 93 | } 94 | 95 | private static void changeForcedHost(SocketAddress host, boolean remove, ServerInfo server) { 96 | if (locked) { 97 | return; 98 | } 99 | 100 | List listeners = bungeeConfig.getList("listeners"); 101 | List listenersCopy = new ArrayList<>(listeners); 102 | 103 | for (int i = 0; i < listeners.size(); i++) { 104 | Object listenerObj = listeners.get(i); 105 | 106 | if (!(listenerObj instanceof Map)) { 107 | continue; 108 | } 109 | 110 | @SuppressWarnings("unchecked") 111 | Map listenerMap = (Map) listenerObj; 112 | 113 | Object forcedHostsObj = listenerMap.get("forced_hosts"); 114 | 115 | if (!(forcedHostsObj instanceof Map)) { 116 | continue; 117 | } 118 | 119 | @SuppressWarnings("unchecked") 120 | Map forcedHostsMap = (Map) forcedHostsObj; 121 | 122 | String socketAddressStr = socketAddressToString(host, false); 123 | 124 | if (remove) { 125 | if (forcedHostsMap.get(socketAddressStr) != null && forcedHostsMap.get(socketAddressStr).equalsIgnoreCase(server.getName())) { 126 | forcedHostsMap.remove(socketAddressStr); 127 | } 128 | } else { 129 | forcedHostsMap.put(socketAddressStr, server.getName()); 130 | } 131 | 132 | listenerMap.put("forced_hosts", forcedHostsMap); 133 | listenersCopy.set(i, listenerMap); 134 | } 135 | 136 | bungeeConfig.set("listeners", listenersCopy); 137 | saveConfig(); 138 | } 139 | 140 | private static void saveConfig() { 141 | if (locked) { 142 | return; 143 | } 144 | 145 | try { 146 | YamlConfiguration.getProvider(YamlConfiguration.class).save(bungeeConfig, file); 147 | } catch (IOException e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | 152 | private static void setupConfig() { 153 | FileInputStream fis = null; 154 | InputStreamReader isr = null; 155 | try { 156 | file = new File(ProxyServer.getInstance().getPluginsFolder().getParentFile(), "config.yml"); 157 | 158 | fis = new FileInputStream(file); 159 | isr = new InputStreamReader(fis); 160 | 161 | bungeeConfig = YamlConfiguration.getProvider(YamlConfiguration.class).load(isr); 162 | } catch (IOException e) { 163 | e.printStackTrace(); 164 | } finally { 165 | try { 166 | if (fis != null) { 167 | fis.close(); 168 | } 169 | 170 | if (isr != null) { 171 | isr.close(); 172 | } 173 | } catch (IOException ignored) {} 174 | } 175 | 176 | locked = bungeeConfig == null; 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/pw/teg/bsm/util/ServerHelper.java: -------------------------------------------------------------------------------- 1 | package pw.teg.bsm.util; 2 | 3 | import net.md_5.bungee.api.ProxyServer; 4 | import net.md_5.bungee.api.config.ServerInfo; 5 | import net.md_5.bungee.api.connection.ProxiedPlayer; 6 | 7 | import java.util.Map; 8 | 9 | public class ServerHelper { 10 | 11 | public static boolean serverExists(String name) { 12 | return getServerInfo(name) != null; 13 | } 14 | 15 | public static ServerInfo getServerInfo(String name) { 16 | return getServers().get(name); 17 | } 18 | 19 | public static void addServer(ServerInfo serverInfo) { 20 | if (serverExists(serverInfo.getName())) { 21 | return; 22 | } 23 | 24 | getServers().put(serverInfo.getName(), serverInfo); 25 | ConfigHelper.addToConfig(serverInfo); 26 | } 27 | 28 | public static void removeServer(String name) { 29 | if (!serverExists(name)) { 30 | return; 31 | } 32 | 33 | ServerInfo info = getServerInfo(name); 34 | 35 | for (ProxiedPlayer p : info.getPlayers()) { 36 | p.connect(getServers().get(p.getPendingConnection().getListener().getFallbackServer())); 37 | } 38 | 39 | getServers().remove(name); 40 | ConfigHelper.removeFromConfig(name); 41 | } 42 | 43 | public static Map getServers() { 44 | return ProxyServer.getInstance().getServers(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ${project.artifactId} 2 | main: pw.teg.bsm.BungeeServerManager 3 | version: ${project.version} 4 | author: Teg --------------------------------------------------------------------------------