Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Merge branch 'dev/3.0.0' into forwarding-mode
# Conflicts: # proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java
Dieser Commit ist enthalten in:
Commit
f099d81ae7
@ -67,7 +67,7 @@ tasks {
|
|||||||
"https://google.github.io/guice/api-docs/${libs.guice.get().version}/javadoc/",
|
"https://google.github.io/guice/api-docs/${libs.guice.get().version}/javadoc/",
|
||||||
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
"https://docs.oracle.com/en/java/javase/17/docs/api/",
|
||||||
"https://jd.advntr.dev/api/${libs.adventure.bom.get().version}/",
|
"https://jd.advntr.dev/api/${libs.adventure.bom.get().version}/",
|
||||||
"https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine"
|
"https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/${libs.caffeine.get().version}/"
|
||||||
)
|
)
|
||||||
|
|
||||||
o.tags(
|
o.tags(
|
||||||
|
@ -44,7 +44,7 @@ public interface CommandManager {
|
|||||||
* @param otherAliases additional aliases
|
* @param otherAliases additional aliases
|
||||||
* @throws IllegalArgumentException if one of the given aliases is already registered, or
|
* @throws IllegalArgumentException if one of the given aliases is already registered, or
|
||||||
* the given command does not implement a registrable {@link Command} subinterface
|
* the given command does not implement a registrable {@link Command} subinterface
|
||||||
* @see Command for a list of registrable {@link Command} subinterfaces
|
* @see Command for a list of registrable Command subinterfaces
|
||||||
*/
|
*/
|
||||||
default void register(String alias, Command command, String... otherAliases) {
|
default void register(String alias, Command command, String... otherAliases) {
|
||||||
register(metaBuilder(alias).aliases(otherAliases).build(), command);
|
register(metaBuilder(alias).aliases(otherAliases).build(), command);
|
||||||
@ -65,7 +65,7 @@ public interface CommandManager {
|
|||||||
* @param command the command to register
|
* @param command the command to register
|
||||||
* @throws IllegalArgumentException if one of the given aliases is already registered, or
|
* @throws IllegalArgumentException if one of the given aliases is already registered, or
|
||||||
* the given command does not implement a registrable {@link Command} subinterface
|
* the given command does not implement a registrable {@link Command} subinterface
|
||||||
* @see Command for a list of registrable {@link Command} subinterfaces
|
* @see Command for a list of registrable Command subinterfaces
|
||||||
*/
|
*/
|
||||||
void register(CommandMeta meta, Command command);
|
void register(CommandMeta meta, Command command);
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2023 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.player;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
|
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
||||||
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is fired when the downstream server tries to remove a resource pack from player
|
||||||
|
* or clear all of them. The proxy will wait on this event to finish before forwarding the
|
||||||
|
* action to the user. If this event is denied, no resource packs will be removed from player.
|
||||||
|
*/
|
||||||
|
@AwaitingEvent
|
||||||
|
public class ServerResourcePackRemoveEvent implements ResultedEvent<ResultedEvent.GenericResult> {
|
||||||
|
|
||||||
|
private GenericResult result;
|
||||||
|
private final @MonotonicNonNull UUID packId;
|
||||||
|
private final ServerConnection serverConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates this event.
|
||||||
|
*/
|
||||||
|
public ServerResourcePackRemoveEvent(UUID packId, ServerConnection serverConnection) {
|
||||||
|
this.result = ResultedEvent.GenericResult.allowed();
|
||||||
|
this.packId = packId;
|
||||||
|
this.serverConnection = serverConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the id of the resource pack, if it's null all the resource packs
|
||||||
|
* from player will be cleared.
|
||||||
|
*
|
||||||
|
* @return the id
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public UUID getPackId() {
|
||||||
|
return packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server that tries to remove a resource pack from player or clear all of them.
|
||||||
|
*
|
||||||
|
* @return the server connection
|
||||||
|
*/
|
||||||
|
public ServerConnection getServerConnection() {
|
||||||
|
return serverConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GenericResult getResult() {
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResult(GenericResult result) {
|
||||||
|
this.result = Preconditions.checkNotNull(result, "result");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.player.configuration;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is executed when a player entered the configuration state and can be configured by Velocity.
|
||||||
|
* <p>Velocity will wait for this event before continuing/ending the configuration state.</p>
|
||||||
|
*
|
||||||
|
* @param player The player who can be configured.
|
||||||
|
* @param server The server that is currently configuring the player.
|
||||||
|
* @since 3.3.0
|
||||||
|
* @sinceMinecraft 1.20.2
|
||||||
|
*/
|
||||||
|
@AwaitingEvent
|
||||||
|
public record PlayerConfigurationEvent(@NotNull Player player, ServerConnection server) {
|
||||||
|
}
|
@ -7,21 +7,23 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.player.configuration;
|
package com.velocitypowered.api.event.player.configuration;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.ServerConnection;
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is executed when a player with version 1.20.2 or higher enters the configuration phase.
|
* This event is executed when a player is about to enter the configuration state.
|
||||||
* <p>From this moment on, until the {@link PlayerFinishedConfigurationEvent} is executed,
|
* It is <b>not</b> called for the initial configuration of a player after login.
|
||||||
* the {@linkplain Player#getProtocolState()} method is guaranteed
|
* <p>Velocity will wait for this event before asking the client to enter configuration state.
|
||||||
* to return {@link ProtocolState#CONFIGURATION}.</p>
|
* However due to backend server being unable to keep the connection alive during state changes,
|
||||||
|
* Velocity will only wait for a maximum of 5 seconds.</p>
|
||||||
*
|
*
|
||||||
* @param player The player that has entered the configuration phase.
|
* @param player The player who is about to enter configuration state.
|
||||||
* @param server The server that will now (re-)configure the player.
|
* @param server The server that wants to reconfigure the player.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
* @sinceMinecraft 1.20.2
|
* @sinceMinecraft 1.20.2
|
||||||
*/
|
*/
|
||||||
|
@AwaitingEvent
|
||||||
public record PlayerEnterConfigurationEvent(@NotNull Player player, ServerConnection server) {
|
public record PlayerEnterConfigurationEvent(@NotNull Player player, ServerConnection server) {
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.player.configuration;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is executed when a player has entered the configuration state.
|
||||||
|
* <p>From this moment on, until the {@link PlayerFinishedConfigurationEvent} is executed,
|
||||||
|
* the {@linkplain Player#getProtocolState()} method is guaranteed
|
||||||
|
* to return {@link ProtocolState#CONFIGURATION}.</p>
|
||||||
|
*
|
||||||
|
* @param player The player who has entered the configuration state.
|
||||||
|
* @param server The server that will now (re-)configure the player.
|
||||||
|
* @since 3.3.0
|
||||||
|
* @sinceMinecraft 1.20.2
|
||||||
|
*/
|
||||||
|
public record PlayerEnteredConfigurationEvent(@NotNull Player player, ServerConnection server) {
|
||||||
|
}
|
@ -13,11 +13,14 @@ import com.velocitypowered.api.proxy.ServerConnection;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is executed when the player is about to finish the Configuration state.
|
* This event is executed when a player is about to finish the configuration state.
|
||||||
* <p>Velocity will wait for this event to finish the configuration phase on the client.</p>
|
* <p>Velocity will wait for this event before asking the client to finish the configuration state.
|
||||||
|
* However due to backend server being unable to keep the connection alive during state changes,
|
||||||
|
* Velocity will only wait for a maximum of 5 seconds. If you need to hold a player in configuration
|
||||||
|
* state, use the {@link PlayerConfigurationEvent}.</p>
|
||||||
*
|
*
|
||||||
* @param player The player who is about to complete the configuration phase.
|
* @param player The player who is about to finish the configuration phase.
|
||||||
* @param server The server that is currently (re-)configuring the player.
|
* @param server The server that has (re-)configured the player.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
* @sinceMinecraft 1.20.2
|
* @sinceMinecraft 1.20.2
|
||||||
*/
|
*/
|
||||||
|
@ -13,11 +13,11 @@ import com.velocitypowered.api.proxy.ServerConnection;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event executed when a player of version 1.20.2 or higher finishes the Configuration state.
|
* This event is executed when a player has finished the configuration state.
|
||||||
* <p>From this moment on, the {@link Player#getProtocolState()} method
|
* <p>From this moment on, the {@link Player#getProtocolState()} method
|
||||||
* will return {@link ProtocolState#PLAY}.</p>
|
* will return {@link ProtocolState#PLAY}.</p>
|
||||||
*
|
*
|
||||||
* @param player The player who has completed the Configuration state
|
* @param player The player who has finished the configuration state.
|
||||||
* @param server The server that has (re-)configured the player.
|
* @param server The server that has (re-)configured the player.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
* @sinceMinecraft 1.20.2
|
* @sinceMinecraft 1.20.2
|
||||||
|
@ -11,7 +11,7 @@ shadow = "io.github.goooler.shadow:8.1.5"
|
|||||||
spotless = "com.diffplug.spotless:6.25.0"
|
spotless = "com.diffplug.spotless:6.25.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
adventure-bom = "net.kyori:adventure-bom:4.16.0"
|
adventure-bom = "net.kyori:adventure-bom:4.17.0"
|
||||||
adventure-facet = "net.kyori:adventure-platform-facet:4.3.2"
|
adventure-facet = "net.kyori:adventure-platform-facet:4.3.2"
|
||||||
asm = "org.ow2.asm:asm:9.6"
|
asm = "org.ow2.asm:asm:9.6"
|
||||||
auto-service = "com.google.auto.service:auto-service:1.0.1"
|
auto-service = "com.google.auto.service:auto-service:1.0.1"
|
||||||
|
@ -29,6 +29,7 @@ import com.velocitypowered.api.event.connection.PreTransferEvent;
|
|||||||
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
||||||
|
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
@ -258,14 +259,26 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(RemoveResourcePackPacket packet) {
|
public boolean handle(RemoveResourcePackPacket packet) {
|
||||||
final ConnectedPlayer player = serverConn.getPlayer();
|
final ServerResourcePackRemoveEvent event = new ServerResourcePackRemoveEvent(
|
||||||
final ResourcePackHandler handler = player.resourcePackHandler();
|
packet.getId(), this.serverConn);
|
||||||
if (packet.getId() != null) {
|
server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackRemoveEvent -> {
|
||||||
handler.remove(packet.getId());
|
if (playerConnection.isClosed()) {
|
||||||
} else {
|
return;
|
||||||
handler.clearAppliedResourcePacks();
|
}
|
||||||
}
|
if (serverResourcePackRemoveEvent.getResult().isAllowed()) {
|
||||||
playerConnection.write(packet);
|
final ConnectedPlayer player = serverConn.getPlayer();
|
||||||
|
final ResourcePackHandler handler = player.resourcePackHandler();
|
||||||
|
if (packet.getId() != null) {
|
||||||
|
handler.remove(packet.getId());
|
||||||
|
} else {
|
||||||
|
handler.clearAppliedResourcePacks();
|
||||||
|
}
|
||||||
|
playerConnection.write(packet);
|
||||||
|
}
|
||||||
|
}, playerConnection.eventLoop()).exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling resource pack remove for {}", playerConnection, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +260,13 @@ public class BungeeCordMessageResponder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processKickRaw(ByteBufDataInput in) {
|
||||||
|
proxy.getPlayer(in.readUTF()).ifPresent(player -> {
|
||||||
|
String kickReason = in.readUTF();
|
||||||
|
player.disconnect(GsonComponentSerializer.gson().deserialize(kickReason));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void processForwardToPlayer(ByteBufDataInput in) {
|
private void processForwardToPlayer(ByteBufDataInput in) {
|
||||||
Optional<Player> player = proxy.getPlayer(in.readUTF());
|
Optional<Player> player = proxy.getPlayer(in.readUTF());
|
||||||
if (player.isPresent()) {
|
if (player.isPresent()) {
|
||||||
@ -372,6 +379,9 @@ public class BungeeCordMessageResponder {
|
|||||||
case "KickPlayer":
|
case "KickPlayer":
|
||||||
this.processKick(in);
|
this.processKick(in);
|
||||||
break;
|
break;
|
||||||
|
case "KickPlayerRaw":
|
||||||
|
this.processKickRaw(in);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing, unknown command
|
// Do nothing, unknown command
|
||||||
break;
|
break;
|
||||||
|
@ -21,6 +21,7 @@ import com.velocitypowered.api.event.connection.PreTransferEvent;
|
|||||||
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
||||||
|
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
||||||
@ -30,6 +31,7 @@ import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
|||||||
import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler;
|
import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
|
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
|
||||||
|
import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
|
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
|
||||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
||||||
@ -41,6 +43,7 @@ import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket;
|
import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.packet.RemoveResourcePackPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket;
|
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket;
|
import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.TransferPacket;
|
import com.velocitypowered.proxy.protocol.packet.TransferPacket;
|
||||||
@ -192,6 +195,33 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(RemoveResourcePackPacket packet) {
|
||||||
|
final MinecraftConnection playerConnection = this.serverConn.getPlayer().getConnection();
|
||||||
|
|
||||||
|
final ServerResourcePackRemoveEvent event = new ServerResourcePackRemoveEvent(
|
||||||
|
packet.getId(), this.serverConn);
|
||||||
|
server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackRemoveEvent -> {
|
||||||
|
if (playerConnection.isClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (serverResourcePackRemoveEvent.getResult().isAllowed()) {
|
||||||
|
final ConnectedPlayer player = serverConn.getPlayer();
|
||||||
|
final ResourcePackHandler handler = player.resourcePackHandler();
|
||||||
|
if (packet.getId() != null) {
|
||||||
|
handler.remove(packet.getId());
|
||||||
|
} else {
|
||||||
|
handler.clearAppliedResourcePacks();
|
||||||
|
}
|
||||||
|
playerConnection.write(packet);
|
||||||
|
}
|
||||||
|
}, playerConnection.eventLoop()).exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling resource pack remove for {}", playerConnection, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(FinishedUpdatePacket packet) {
|
public boolean handle(FinishedUpdatePacket packet) {
|
||||||
final MinecraftConnection smc = serverConn.ensureConnected();
|
final MinecraftConnection smc = serverConn.ensureConnected();
|
||||||
|
@ -19,6 +19,7 @@ package com.velocitypowered.proxy.connection.backend;
|
|||||||
|
|
||||||
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent;
|
import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent;
|
||||||
|
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
@ -144,8 +145,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
public boolean handle(ServerLoginSuccessPacket packet) {
|
public boolean handle(ServerLoginSuccessPacket packet) {
|
||||||
if (server.getConfiguration().getServerPlayerInfoForwardingMode(serverConn.getServerInfo().getName()) == PlayerInfoForwarding.MODERN
|
if (server.getConfiguration().getServerPlayerInfoForwardingMode(serverConn.getServerInfo().getName()) == PlayerInfoForwarding.MODERN
|
||||||
&& !informationForwarded) {
|
&& !informationForwarded) {
|
||||||
resultFuture.complete(ConnectionRequestResults.forDisconnect(MODERN_IP_FORWARDING_FAILURE,
|
resultFuture.complete(ConnectionRequestResults.forDisconnect(MODERN_IP_FORWARDING_FAILURE, serverConn.getServer()));
|
||||||
serverConn.getServer()));
|
|
||||||
serverConn.disconnect();
|
serverConn.disconnect();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -156,12 +156,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
// Move into the PLAY phase.
|
// Move into the PLAY phase.
|
||||||
MinecraftConnection smc = serverConn.ensureConnected();
|
MinecraftConnection smc = serverConn.ensureConnected();
|
||||||
if (smc.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
if (smc.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||||
smc.setActiveSessionHandler(StateRegistry.PLAY,
|
smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture));
|
||||||
new TransitionSessionHandler(server, serverConn, resultFuture));
|
|
||||||
} else {
|
} else {
|
||||||
smc.write(new LoginAcknowledgedPacket());
|
smc.write(new LoginAcknowledgedPacket());
|
||||||
smc.setActiveSessionHandler(StateRegistry.CONFIG,
|
smc.setActiveSessionHandler(StateRegistry.CONFIG, new ConfigSessionHandler(server, serverConn, resultFuture));
|
||||||
new ConfigSessionHandler(server, serverConn, resultFuture));
|
|
||||||
ConnectedPlayer player = serverConn.getPlayer();
|
ConnectedPlayer player = serverConn.getPlayer();
|
||||||
if (player.getClientSettingsPacket() != null) {
|
if (player.getClientSettingsPacket() != null) {
|
||||||
smc.write(player.getClientSettingsPacket());
|
smc.write(player.getClientSettingsPacket());
|
||||||
@ -169,6 +167,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) {
|
if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) {
|
||||||
smc.setAutoReading(false);
|
smc.setAutoReading(false);
|
||||||
clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop());
|
clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop());
|
||||||
|
} else {
|
||||||
|
// Initial login - the player is already in configuration state.
|
||||||
|
server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,14 +178,14 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_data"));
|
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_player_data"));
|
||||||
} else {
|
} else {
|
||||||
loginState = State.ACKNOWLEDGED;
|
loginState = State.ACKNOWLEDGED;
|
||||||
mcConnection.setActiveSessionHandler(StateRegistry.CONFIG,
|
mcConnection.setActiveSessionHandler(StateRegistry.CONFIG, new ClientConfigSessionHandler(server, connectedPlayer));
|
||||||
new ClientConfigSessionHandler(server, connectedPlayer));
|
|
||||||
|
|
||||||
server.getEventManager().fire(new PostLoginEvent(connectedPlayer))
|
server.getEventManager().fire(new PostLoginEvent(connectedPlayer)).thenCompose(ignored -> {
|
||||||
.thenCompose((ignored) -> connectToInitialServer(connectedPlayer)).exceptionally((ex) -> {
|
return connectToInitialServer(connectedPlayer);
|
||||||
logger.error("Exception while connecting {} to initial server", connectedPlayer, ex);
|
}).exceptionally((ex) -> {
|
||||||
return null;
|
logger.error("Exception while connecting {} to initial server", connectedPlayer, ex);
|
||||||
});
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -224,8 +224,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
player.disconnect0(reason.get(), true);
|
player.disconnect0(reason.get(), true);
|
||||||
} else {
|
} else {
|
||||||
if (!server.registerConnection(player)) {
|
if (!server.registerConnection(player)) {
|
||||||
player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"),
|
player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"), true);
|
||||||
true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,13 +237,13 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
loginState = State.SUCCESS_SENT;
|
loginState = State.SUCCESS_SENT;
|
||||||
if (inbound.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
if (inbound.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||||
loginState = State.ACKNOWLEDGED;
|
loginState = State.ACKNOWLEDGED;
|
||||||
mcConnection.setActiveSessionHandler(StateRegistry.PLAY,
|
mcConnection.setActiveSessionHandler(StateRegistry.PLAY, new InitialConnectSessionHandler(player, server));
|
||||||
new InitialConnectSessionHandler(player, server));
|
server.getEventManager().fire(new PostLoginEvent(player)).thenCompose((ignored) -> {
|
||||||
server.getEventManager().fire(new PostLoginEvent(player))
|
return connectToInitialServer(player);
|
||||||
.thenCompose((ignored) -> connectToInitialServer(player)).exceptionally((ex) -> {
|
}).exceptionally((ex) -> {
|
||||||
logger.error("Exception while connecting {} to initial server", player, ex);
|
logger.error("Exception while connecting {} to initial server", player, ex);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, mcConnection.eventLoop()).exceptionally((ex) -> {
|
}, mcConnection.eventLoop()).exceptionally((ex) -> {
|
||||||
|
@ -19,6 +19,7 @@ package com.velocitypowered.proxy.connection.client;
|
|||||||
|
|
||||||
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
||||||
|
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
@ -48,8 +49,6 @@ import net.kyori.adventure.text.Component;
|
|||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the client config stage.
|
* Handles the client config stage.
|
||||||
@ -61,6 +60,7 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private String brandChannel = null;
|
private String brandChannel = null;
|
||||||
|
|
||||||
|
private CompletableFuture<?> configurationFuture;
|
||||||
private CompletableFuture<Void> configSwitchFuture;
|
private CompletableFuture<Void> configSwitchFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,11 +81,7 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(final KeepAlivePacket packet) {
|
public boolean handle(final KeepAlivePacket packet) {
|
||||||
final VelocityServerConnection serverConnection = player.getConnectedServer();
|
player.forwardKeepAlive(packet);
|
||||||
if (!this.sendKeepAliveToBackend(serverConnection, packet)) {
|
|
||||||
final VelocityServerConnection connectionInFlight = player.getConnectionInFlight();
|
|
||||||
this.sendKeepAliveToBackend(connectionInFlight, packet);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +102,7 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(FinishedUpdatePacket packet) {
|
public boolean handle(FinishedUpdatePacket packet) {
|
||||||
player.getConnection()
|
player.getConnection().setActiveSessionHandler(StateRegistry.PLAY, new ClientPlaySessionHandler(server, player));
|
||||||
.setActiveSessionHandler(StateRegistry.PLAY, new ClientPlaySessionHandler(server, player));
|
|
||||||
|
|
||||||
configSwitchFuture.complete(null);
|
configSwitchFuture.complete(null);
|
||||||
return true;
|
return true;
|
||||||
@ -141,12 +136,14 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(KnownPacksPacket packet) {
|
public boolean handle(KnownPacksPacket packet) {
|
||||||
if (player.getConnectionInFlight() != null) {
|
callConfigurationEvent().thenRun(() -> {
|
||||||
player.getConnectionInFlight().ensureConnected().write(packet);
|
player.getConnectionInFlightOrConnectedServer().ensureConnected().write(packet);
|
||||||
return true;
|
}).exceptionally(ex -> {
|
||||||
}
|
logger.error("Error forwarding known packs response to backend:", ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -209,26 +206,25 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exception(Throwable throwable) {
|
public void exception(Throwable throwable) {
|
||||||
player.disconnect(
|
player.disconnect(Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED));
|
||||||
Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean sendKeepAliveToBackend(
|
/**
|
||||||
final @Nullable VelocityServerConnection serverConnection,
|
* Calls the {@link PlayerConfigurationEvent}.
|
||||||
final @NotNull KeepAlivePacket packet
|
* For 1.20.5+ backends this is done when the client responds to
|
||||||
) {
|
* the known packs request. The response is delayed until the event
|
||||||
if (serverConnection != null) {
|
* has been called.
|
||||||
final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId());
|
* For 1.20.2-1.20.4 servers this is done when the client acknowledges
|
||||||
if (sentTime != null) {
|
* the end of the configuration.
|
||||||
final MinecraftConnection smc = serverConnection.getConnection();
|
* This is handled differently because for 1.20.5+ servers can't keep
|
||||||
if (smc != null) {
|
* their connection alive between states and older servers don't have
|
||||||
player.setPing(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - sentTime));
|
* the known packs transaction.
|
||||||
smc.write(packet);
|
*/
|
||||||
return true;
|
private CompletableFuture<?> callConfigurationEvent() {
|
||||||
}
|
if (configurationFuture != null) {
|
||||||
}
|
return configurationFuture;
|
||||||
}
|
}
|
||||||
return false;
|
return configurationFuture = server.getEventManager().fire(new PlayerConfigurationEvent(player, player.getConnectionInFlightOrConnectedServer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,11 +244,17 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
smc.write(brandPacket);
|
smc.write(brandPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn)).thenAcceptAsync(event -> {
|
callConfigurationEvent().thenCompose(v -> {
|
||||||
|
return server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn))
|
||||||
|
.completeOnTimeout(null, 5, TimeUnit.SECONDS);
|
||||||
|
}).thenRunAsync(() -> {
|
||||||
player.getConnection().write(FinishedUpdatePacket.INSTANCE);
|
player.getConnection().write(FinishedUpdatePacket.INSTANCE);
|
||||||
player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
|
player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
|
||||||
server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
|
server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
|
||||||
}, player.getConnection().eventLoop());
|
}, player.getConnection().eventLoop()).exceptionally(ex -> {
|
||||||
|
logger.error("Error finishing configuration state:", ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
return configSwitchFuture;
|
return configSwitchFuture;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
|||||||
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
|
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
||||||
import com.velocitypowered.api.event.player.TabCompleteEvent;
|
import com.velocitypowered.api.event.player.TabCompleteEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||||
@ -86,7 +86,6 @@ import java.util.Queue;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
@ -178,17 +177,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(KeepAlivePacket packet) {
|
public boolean handle(KeepAlivePacket packet) {
|
||||||
final VelocityServerConnection serverConnection = player.getConnectedServer();
|
player.forwardKeepAlive(packet);
|
||||||
if (serverConnection != null) {
|
|
||||||
final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId());
|
|
||||||
if (sentTime != null) {
|
|
||||||
final MinecraftConnection smc = serverConnection.getConnection();
|
|
||||||
if (smc != null) {
|
|
||||||
player.setPing(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - sentTime));
|
|
||||||
smc.write(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +397,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
// Complete client switch
|
// Complete client switch
|
||||||
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
|
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
|
||||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||||
server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(player, serverConnection));
|
server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConnection));
|
||||||
if (serverConnection != null) {
|
if (serverConnection != null) {
|
||||||
MinecraftConnection smc = serverConnection.ensureConnected();
|
MinecraftConnection smc = serverConnection.ensureConnected();
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
|
@ -111,6 +111,7 @@ import java.util.UUID;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import net.kyori.adventure.audience.MessageType;
|
import net.kyori.adventure.audience.MessageType;
|
||||||
import net.kyori.adventure.bossbar.BossBar;
|
import net.kyori.adventure.bossbar.BossBar;
|
||||||
import net.kyori.adventure.identity.Identity;
|
import net.kyori.adventure.identity.Identity;
|
||||||
@ -634,6 +635,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
return connectionInFlight;
|
return connectionInFlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VelocityServerConnection getConnectionInFlightOrConnectedServer() {
|
||||||
|
return connectionInFlight != null ? connectionInFlight : connectedServer;
|
||||||
|
}
|
||||||
|
|
||||||
public void resetInFlightConnection() {
|
public void resetInFlightConnection() {
|
||||||
connectionInFlight = null;
|
connectionInFlight = null;
|
||||||
}
|
}
|
||||||
@ -1239,21 +1244,46 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forwards the keep alive packet to the backend server it belongs to.
|
||||||
|
* This is either the connection in flight or the connected server.
|
||||||
|
*/
|
||||||
|
public boolean forwardKeepAlive(final KeepAlivePacket packet) {
|
||||||
|
if (!this.sendKeepAliveToBackend(connectedServer, packet)) {
|
||||||
|
return this.sendKeepAliveToBackend(connectionInFlight, packet);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendKeepAliveToBackend(final @Nullable VelocityServerConnection serverConnection, final @NotNull KeepAlivePacket packet) {
|
||||||
|
if (serverConnection != null) {
|
||||||
|
final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId());
|
||||||
|
if (sentTime != null) {
|
||||||
|
final MinecraftConnection smc = serverConnection.getConnection();
|
||||||
|
if (smc != null) {
|
||||||
|
setPing(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - sentTime));
|
||||||
|
smc.write(packet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches the connection to the client into config state.
|
* Switches the connection to the client into config state.
|
||||||
*/
|
*/
|
||||||
public void switchToConfigState() {
|
public void switchToConfigState() {
|
||||||
CompletableFuture.runAsync(() -> {
|
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
|
||||||
connection.write(StartUpdatePacket.INSTANCE);
|
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
|
||||||
connection.getChannel().pipeline()
|
connection.write(StartUpdatePacket.INSTANCE);
|
||||||
.get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
|
connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
|
||||||
// Make sure we don't send any play packets to the player after update start
|
// Make sure we don't send any play packets to the player after update start
|
||||||
connection.addPlayPacketQueueHandler();
|
connection.addPlayPacketQueueHandler();
|
||||||
server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(this, connectionInFlight));
|
}, connection.eventLoop()).exceptionally((ex) -> {
|
||||||
}, connection.eventLoop()).exceptionally((ex) -> {
|
logger.error("Error switching player connection to config state", ex);
|
||||||
logger.error("Error switching player connection to config state", ex);
|
return null;
|
||||||
return null;
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,13 +43,13 @@ import java.nio.file.Path;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@ -86,35 +86,48 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
checkNotNull(directory, "directory");
|
checkNotNull(directory, "directory");
|
||||||
checkArgument(directory.toFile().isDirectory(), "provided path isn't a directory");
|
checkArgument(directory.toFile().isDirectory(), "provided path isn't a directory");
|
||||||
|
|
||||||
List<PluginDescription> found = new ArrayList<>();
|
Map<String, PluginDescription> foundCandidates = new LinkedHashMap<>();
|
||||||
JavaPluginLoader loader = new JavaPluginLoader(server, directory);
|
JavaPluginLoader loader = new JavaPluginLoader(server, directory);
|
||||||
|
|
||||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory,
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory,
|
||||||
p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
p -> p.toFile().isFile() && p.toString().endsWith(".jar"))) {
|
||||||
for (Path path : stream) {
|
for (Path path : stream) {
|
||||||
try {
|
try {
|
||||||
found.add(loader.loadCandidate(path));
|
PluginDescription candidate = loader.loadCandidate(path);
|
||||||
|
|
||||||
|
// If we found a duplicate candidate (with the same ID), don't load it.
|
||||||
|
PluginDescription maybeExistingCandidate = foundCandidates.putIfAbsent(
|
||||||
|
candidate.getId(), candidate);
|
||||||
|
|
||||||
|
if (maybeExistingCandidate != null) {
|
||||||
|
logger.error("Refusing to load plugin at path {} since we already "
|
||||||
|
+ "loaded a plugin with the same ID {} from {}",
|
||||||
|
candidate.getSource().map(Objects::toString).orElse("<UNKNOWN>"),
|
||||||
|
candidate.getId(),
|
||||||
|
maybeExistingCandidate.getSource().map(Objects::toString).orElse("<UNKNOWN>"));
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
logger.error("Unable to load plugin {}", path, e);
|
logger.error("Unable to load plugin {}", path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found.isEmpty()) {
|
if (foundCandidates.isEmpty()) {
|
||||||
// No plugins found
|
// No plugins found
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(found);
|
List<PluginDescription> sortedPlugins = PluginDependencyUtils.sortCandidates(
|
||||||
|
new ArrayList<>(foundCandidates.values()));
|
||||||
|
|
||||||
Set<String> loadedPluginsById = new HashSet<>();
|
Map<String, PluginDescription> loadedCandidates = new HashMap<>();
|
||||||
Map<PluginContainer, Module> pluginContainers = new LinkedHashMap<>();
|
Map<PluginContainer, Module> pluginContainers = new LinkedHashMap<>();
|
||||||
// Now load the plugins
|
// Now load the plugins
|
||||||
pluginLoad:
|
pluginLoad:
|
||||||
for (PluginDescription candidate : sortedPlugins) {
|
for (PluginDescription candidate : sortedPlugins) {
|
||||||
// Verify dependencies
|
// Verify dependencies
|
||||||
for (PluginDependency dependency : candidate.getDependencies()) {
|
for (PluginDependency dependency : candidate.getDependencies()) {
|
||||||
if (!dependency.isOptional() && !loadedPluginsById.contains(dependency.getId())) {
|
if (!dependency.isOptional() && !loadedCandidates.containsKey(dependency.getId())) {
|
||||||
logger.error("Can't load plugin {} due to missing dependency {}", candidate.getId(),
|
logger.error("Can't load plugin {} due to missing dependency {}", candidate.getId(),
|
||||||
dependency.getId());
|
dependency.getId());
|
||||||
continue pluginLoad;
|
continue pluginLoad;
|
||||||
@ -125,7 +138,7 @@ public class VelocityPluginManager implements PluginManager {
|
|||||||
PluginDescription realPlugin = loader.createPluginFromCandidate(candidate);
|
PluginDescription realPlugin = loader.createPluginFromCandidate(candidate);
|
||||||
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
VelocityPluginContainer container = new VelocityPluginContainer(realPlugin);
|
||||||
pluginContainers.put(container, loader.createModule(container));
|
pluginContainers.put(container, loader.createModule(container));
|
||||||
loadedPluginsById.add(realPlugin.getId());
|
loadedCandidates.put(realPlugin.getId(), realPlugin);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
logger.error("Can't create module for plugin {}", candidate.getId(), e);
|
logger.error("Can't create module for plugin {}", candidate.getId(), e);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import com.velocitypowered.proxy.protocol.StateRegistry;
|
|||||||
import io.netty.channel.ChannelDuplexHandler;
|
import io.netty.channel.ChannelDuplexHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.internal.PlatformDependent;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler {
|
public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler {
|
||||||
|
|
||||||
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
||||||
private final Queue<Object> queue = PlatformDependent.newMpscQueue();
|
private final Queue<Object> queue = new ArrayDeque<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides registries for client & server bound packets.
|
* Provides registries for client & server bound packets.
|
||||||
|
@ -25,7 +25,7 @@ import io.netty.channel.ChannelDuplexHandler;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.channel.ChannelPromise;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.internal.PlatformDependent;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class PlayPacketQueueOutboundHandler extends ChannelDuplexHandler {
|
public class PlayPacketQueueOutboundHandler extends ChannelDuplexHandler {
|
||||||
|
|
||||||
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
|
||||||
private final Queue<MinecraftPacket> queue = PlatformDependent.newMpscQueue();
|
private final Queue<MinecraftPacket> queue = new ArrayDeque<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides registries for client & server bound packets.
|
* Provides registries for client & server bound packets.
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren