From 37f622f22633bca83921fd7c37835e9a19eeb9ba Mon Sep 17 00:00:00 2001 From: DartCZ Date: Mon, 22 Sep 2025 23:27:25 +0200 Subject: [PATCH] feat: add ProxyPreShutdownEvent before players are disconnected (#1626) * feat: delay player disconnect until ProxyShutdownEvent completes * fix: added back empty line * feat: added ProxyPreShutdownEvent * feat: CR changes * chore: fixed license, annotated with Beta annotation * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * chore: consolidated log message * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: powercas_gamer * Update api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java Co-authored-by: powercas_gamer * feat: make ProxyPreShutdownEvent timeout configurable via system property * fix: cs * Document velocity.pre-shutdown-timeout system property --------- Co-authored-by: Timon Seidel Co-authored-by: powercas_gamer Co-authored-by: Adrian Gonzales --- .../event/proxy/ProxyPreShutdownEvent.java | 33 +++++++++++++++++++ .../velocitypowered/proxy/VelocityServer.java | 17 ++++++++++ 2 files changed, 50 insertions(+) create mode 100644 api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java diff --git a/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java new file mode 100644 index 00000000..893942c0 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.event.proxy; + +import com.google.common.annotations.Beta; +import com.velocitypowered.api.event.annotation.AwaitingEvent; + +/** + * This event is fired by the proxy after it has stopped accepting new connections, + * but before players are disconnected. + * This is the last point at which you can interact with currently connected players, + * for example to transfer them to another proxy or perform other cleanup tasks. + * + * @implNote Velocity will wait for all event listeners to complete before disconnecting players, + * but note that the event will time out after the configured value of the + * velocity.pre-shutdown-timeout system property, default 10 seconds, + * in seconds to prevent shutdown from hanging indefinitely + * @since 3.4.0 + */ +@Beta +@AwaitingEvent +public final class ProxyPreShutdownEvent { + + @Override + public String toString() { + return "ProxyPreShutdownEvent"; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 7d55ae23..ccfc5f14 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -24,6 +24,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyPreShutdownEvent; import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.network.ProtocolVersion; @@ -150,6 +151,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { ) .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .create(); + private static final int PRE_SHUTDOWN_TIMEOUT = + Integer.getInteger("velocity.pre-shutdown-timeout", 10); private final ConnectionManager cm; private final ProxyOptions options; @@ -579,6 +582,20 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { // done first to refuse new connections cm.shutdown(); + try { + eventManager.fire(new ProxyPreShutdownEvent()) + .toCompletableFuture() + .get(PRE_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS); + } catch (TimeoutException ignored) { + logger.warn("Your plugins took over {} seconds during pre shutdown.", + PRE_SHUTDOWN_TIMEOUT); + } catch (ExecutionException ee) { + logger.error("Exception in ProxyPreShutdownEvent handler; continuing shutdown.", ee); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + logger.warn("Interrupted while waiting for ProxyPreShutdownEvent; continuing shutdown."); + } + ImmutableList players = ImmutableList.copyOf(connectionsByUuid.values()); for (ConnectedPlayer player : players) { player.disconnect(reason);