diff --git a/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyReloadEvent.java b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyReloadEvent.java new file mode 100644 index 000000000..8341fb941 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyReloadEvent.java @@ -0,0 +1,12 @@ +package com.velocitypowered.api.event.proxy; + +/** + * This event is fired when the proxy is reloaded by the user using {@code /velocity reload}. + */ +public class ProxyReloadEvent { + + @Override + public String toString() { + return "ProxyInitializeEvent"; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 52a850e53..2216982a9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -7,6 +7,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginManager; @@ -40,6 +41,7 @@ import com.velocitypowered.proxy.util.VelocityChannelRegistrar; import com.velocitypowered.proxy.util.ratelimit.Ratelimiter; import com.velocitypowered.proxy.util.ratelimit.Ratelimiters; import io.netty.bootstrap.Bootstrap; +import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; @@ -232,6 +234,37 @@ public class VelocityServer implements ProxyServer { return shutdown; } + public boolean reloadConfiguration() throws IOException { + Path configPath = Paths.get("velocity.toml"); + VelocityConfiguration newConfiguration = VelocityConfiguration.read(configPath); + + if (!newConfiguration.validate()) { + return false; + } + + // If we have a new bind address, bind to it + if (!configuration.getBind().equals(newConfiguration.getBind())) { + this.cm.bind(newConfiguration.getBind()); + } + + // Re-register servers + for (Map.Entry entry : newConfiguration.getServers().entrySet()) { + ServerInfo newInfo = + new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())); + Optional rs = servers.getServer(entry.getKey()); + if (!rs.isPresent()) { + servers.register(newInfo); + } else if (!rs.get().getServerInfo().equals(newInfo)) { + throw new IllegalStateException("Unable to replace servers in flight!"); + } + } + + ipAttemptLimiter = Ratelimiters.createWithMilliseconds(newConfiguration.getLoginRatelimit()); + this.configuration = newConfiguration; + eventManager.fireAndForget(new ProxyReloadEvent()); + return true; + } + public void shutdown(boolean explicitExit) { if (eventManager == null || pluginManager == null || cm == null || scheduler == null) { throw new AssertionError(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java index d5c9f7a85..c6c9a99aa 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommand.java @@ -10,6 +10,8 @@ import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.util.ProxyVersion; +import com.velocitypowered.proxy.VelocityServer; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -22,6 +24,8 @@ import net.kyori.text.event.HoverEvent; import net.kyori.text.event.HoverEvent.Action; import net.kyori.text.format.TextColor; import net.kyori.text.format.TextDecoration; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.NonNull; public class VelocityCommand implements Command { @@ -32,10 +36,11 @@ public class VelocityCommand implements Command { * Initializes the command object for /velocity. * @param server the Velocity server */ - public VelocityCommand(ProxyServer server) { + public VelocityCommand(VelocityServer server) { this.subcommands = ImmutableMap.builder() .put("version", new Info(server)) .put("plugins", new Plugins(server)) + .put("reload", new Reload(server)) .build(); } @@ -103,6 +108,39 @@ public class VelocityCommand implements Command { return command.hasPermission(source, actualArgs); } + private static class Reload implements Command { + + private static final Logger logger = LogManager.getLogger(Reload.class); + private final VelocityServer server; + + private Reload(VelocityServer server) { + this.server = server; + } + + @Override + public void execute(CommandSource source, String @NonNull [] args) { + try { + if (server.reloadConfiguration()) { + source.sendMessage(TextComponent.of("Configuration reloaded.", TextColor.GREEN)); + } else { + source.sendMessage(TextComponent.of( + "Unable to reload your configuration. Check the console for more details.", + TextColor.RED)); + } + } catch (Exception e) { + logger.error("Unable to reload configuration", e); + source.sendMessage(TextComponent.of( + "Unable to reload your configuration. Check the console for more details.", + TextColor.RED)); + } + } + + @Override + public boolean hasPermission(CommandSource source, String @NonNull [] args) { + return source.getPermissionValue("velocity.command.reload") == Tristate.TRUE; + } + } + private static class Info implements Command { private final ProxyServer server;