3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-16 21:10:30 +01:00

Allow to enable online mode for player connection (#51)

Dieser Commit ist enthalten in:
Slava Maspanov 2018-08-25 03:55:15 +03:00 committet von Andrew Steinborn
Ursprung e6e3ccaa95
Commit a3c4522ca0
8 geänderte Dateien mit 174 neuen und 38 gelöschten Zeilen

3
.gitignore vendored
Datei anzeigen

@ -92,6 +92,9 @@ modules.xml
**/.settings/ **/.settings/
**/bin/ **/bin/
# NetBeans Gradle#
.nb-gradle/
# Mobile Tools for Java (J2ME) # Mobile Tools for Java (J2ME)
.mtj.tmp/ .mtj.tmp/

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.api.event; package com.velocitypowered.api.event;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import net.kyori.text.Component; import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers; import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -72,7 +73,7 @@ public interface ResultedEvent<R extends ResultedEvent.Result> {
private final boolean allowed; private final boolean allowed;
private final @Nullable Component reason; private final @Nullable Component reason;
private ComponentResult(boolean allowed, @Nullable Component reason) { protected ComponentResult(boolean allowed, @Nullable Component reason) {
this.allowed = allowed; this.allowed = allowed;
this.reason = reason; this.reason = reason;
} }

Datei anzeigen

@ -2,22 +2,28 @@ package com.velocitypowered.api.event.connection;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.event.ResultedEvent.ComponentResult;
import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.InboundConnection;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* This event is fired when a player has initiated a connection with the proxy but before the proxy authenticates the * This event is fired when a player has initiated a connection with the proxy but before the proxy authenticates the
* player with Mojang or before the player's proxy connection is fully established (for offline mode). * player with Mojang or before the player's proxy connection is fully established (for offline mode).
*/ */
public class PreLoginEvent implements ResultedEvent<ResultedEvent.ComponentResult> { public class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLoginComponentResult> {
private final InboundConnection connection; private final InboundConnection connection;
private final String username; private final String username;
private ComponentResult result; private PreLoginComponentResult result;
public PreLoginEvent(InboundConnection connection, String username) { public PreLoginEvent(InboundConnection connection, String username) {
this.connection = Preconditions.checkNotNull(connection, "connection"); this.connection = Preconditions.checkNotNull(connection, "connection");
this.username = Preconditions.checkNotNull(username, "username"); this.username = Preconditions.checkNotNull(username, "username");
this.result = ComponentResult.allowed(); this.result = PreLoginComponentResult.allowed();
} }
public InboundConnection getConnection() { public InboundConnection getConnection() {
@ -29,12 +35,12 @@ public class PreLoginEvent implements ResultedEvent<ResultedEvent.ComponentResul
} }
@Override @Override
public ComponentResult getResult() { public PreLoginComponentResult getResult() {
return result; return result;
} }
@Override @Override
public void setResult(@NonNull ComponentResult result) { public void setResult(@NonNull PreLoginComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result"); this.result = Preconditions.checkNotNull(result, "result");
} }
@ -46,4 +52,56 @@ public class PreLoginEvent implements ResultedEvent<ResultedEvent.ComponentResul
", result=" + result + ", result=" + result +
'}'; '}';
} }
/**
* Represents an "allowed/allowed with online mode/denied" result with a reason allowed for denial.
*/
public static class PreLoginComponentResult extends ComponentResult {
private static final PreLoginComponentResult ALLOWED = new PreLoginComponentResult((Component) null);
private static final PreLoginComponentResult FORCE_ONLINEMODE = new PreLoginComponentResult(true);
private final boolean onlineMode;
/**
* Allows to enable a online mode for the player connection, when Velocity running in offline mode
* Does not have any sense if velocity running in onlineMode;
* @param allowedOnlineMode if true, offline uuid will be used for player connection if Velocity run in offlineMode
*/
private PreLoginComponentResult(boolean allowedOnlineMode) {
super(true, null);
this.onlineMode = allowedOnlineMode;
}
private PreLoginComponentResult(@Nullable Component reason) {
super(reason == null, reason);
// Don't care about this
this.onlineMode = false;
}
public boolean isOnlineModeAllowed() {
return this.onlineMode;
}
@Override
public String toString() {
if (isOnlineModeAllowed()) {
return "allowed with online mode";
}
return super.toString();
}
public static PreLoginComponentResult allowed() {
return ALLOWED;
}
public static PreLoginComponentResult forceOnlineMode() {
return FORCE_ONLINEMODE;
}
public static PreLoginComponentResult denied(@NonNull Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new PreLoginComponentResult(reason);
}
}
} }

Datei anzeigen

@ -0,0 +1,53 @@
package com.velocitypowered.api.event.player.gameprofile;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.util.GameProfile;
public class GameProfileRequestEvent {
private final String username;
private final GameProfile originalProfile;
private final boolean onlineMode;
private GameProfile gameProfile;
public GameProfileRequestEvent(GameProfile originalProfile, boolean onlinemode) {
this.originalProfile = Preconditions.checkNotNull(originalProfile, "profile");
this.username = originalProfile.getName();
this.onlineMode = onlinemode;
}
public String getUsername() {
return username;
}
public GameProfile getOriginalProfile() {
return originalProfile;
}
public boolean isOnlineMode() {
return onlineMode;
}
/**
*
* @return a GameProfile, can be null
*/
public GameProfile getGameProfile() {
return gameProfile;
}
public void setGameProfile(@Nullable GameProfile gameProfile) {
this.gameProfile = gameProfile;
}
@Override
public String toString() {
return "GameProfileRequestEvent{"+
"username=" + username +
", gameProfile=" + gameProfile +
"}";
}
}

Datei anzeigen

@ -1,13 +1,16 @@
package com.velocitypowered.api.proxy; package com.velocitypowered.api.proxy;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.server.ServerInfo; import com.velocitypowered.api.server.ServerInfo;
import com.velocitypowered.api.util.GameProfile.Property;
import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import net.kyori.text.Component; import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;

Datei anzeigen

@ -25,7 +25,7 @@ public class GameProfile {
public UUID idAsUuid() { public UUID idAsUuid() {
return UuidUtils.fromUndashed(id); return UuidUtils.fromUndashed(id);
} }
public String getName() { public String getName() {
return name; return name;
} }

Datei anzeigen

@ -9,12 +9,14 @@ import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.util.MessagePosition; import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
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.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.Chat; import com.velocitypowered.proxy.protocol.packet.Chat;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
@ -45,9 +47,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class); private static final Logger logger = LogManager.getLogger(ConnectedPlayer.class);
private final GameProfile profile;
private final MinecraftConnection connection; private final MinecraftConnection connection;
private final InetSocketAddress virtualHost; private final InetSocketAddress virtualHost;
private final GameProfile profile;
private PermissionFunction permissionFunction = null; private PermissionFunction permissionFunction = null;
private int tryIndex = 0; private int tryIndex = 0;
private VelocityServerConnection connectedServer; private VelocityServerConnection connectedServer;

Datei anzeigen

@ -3,11 +3,14 @@ package com.velocitypowered.proxy.connection.client;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent; import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
import com.velocitypowered.api.event.permission.PermissionsSetupEvent; import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.player.gameprofile.GameProfileRequestEvent;
import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.InboundConnection;
import com.velocitypowered.api.server.ServerInfo; import com.velocitypowered.api.server.ServerInfo;
import com.velocitypowered.proxy.connection.VelocityConstants; import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolConstants; import com.velocitypowered.proxy.protocol.ProtocolConstants;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
@ -37,7 +40,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class); private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
private static final String MOJANG_SERVER_AUTH_URL = private static final String MOJANG_SERVER_AUTH_URL =
"https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s";
private final MinecraftConnection inbound; private final MinecraftConnection inbound;
private final InboundConnection apiInbound; private final InboundConnection apiInbound;
private ServerLogin login; private ServerLogin login;
@ -97,15 +100,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
// The player disconnected after we authenticated them. // The player disconnected after we authenticated them.
return; return;
} }
try { try {
inbound.enableEncryption(decryptedSharedSecret); inbound.enableEncryption(decryptedSharedSecret);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
GameProfile profile = VelocityServer.GSON.fromJson(profileResponse, GameProfile.class); initializePlayer(VelocityServer.GSON.fromJson(profileResponse, GameProfile.class), true);
initializePlayer(profile);
}, inbound.getChannel().eventLoop()) }, inbound.getChannel().eventLoop())
.exceptionally(exception -> { .exceptionally(exception -> {
logger.error("Unable to enable encryption", exception); logger.error("Unable to enable encryption", exception);
@ -125,20 +127,24 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername()); PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername());
VelocityServer.getServer().getEventManager().fire(event) VelocityServer.getServer().getEventManager().fire(event)
.thenRunAsync(() -> { .thenRunAsync(() -> {
if (!event.getResult().isAllowed()) { if (inbound.isClosed()) {
// The player was disconnected
return;
}
PreLoginComponentResult result = event.getResult();
if (!result.isAllowed()) {
// The component is guaranteed to be provided if the connection was denied. // The component is guaranteed to be provided if the connection was denied.
inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); inbound.closeWith(Disconnect.create(event.getResult().getReason().get()));
return; return;
} }
if (VelocityServer.getServer().getConfiguration().isOnlineMode()) { if (VelocityServer.getServer().getConfiguration().isOnlineMode() || result.isOnlineModeAllowed()) {
// Request encryption. // Request encryption.
EncryptionRequest request = generateRequest(); EncryptionRequest request = generateRequest();
this.verify = Arrays.copyOf(request.getVerifyToken(), 4); this.verify = Arrays.copyOf(request.getVerifyToken(), 4);
inbound.write(request); inbound.write(request);
} else { } else {
// Offline-mode, don't try to request encryption. initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false);
initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()));
} }
}, inbound.getChannel().eventLoop()); }, inbound.getChannel().eventLoop());
} }
@ -153,28 +159,38 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
return request; return request;
} }
private void initializePlayer(GameProfile profile) { private void initializePlayer(GameProfile profile, boolean onlineMode) {
// Initiate a regular connection and move over to it.
ConnectedPlayer player = new ConnectedPlayer(profile, inbound, apiInbound.getVirtualHost().orElse(null));
// load permissions first GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(profile, onlineMode);
VelocityServer.getServer().getEventManager().fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
.thenCompose(event -> { VelocityServer.getServer().getEventManager().fire(profileRequestEvent).thenCompose(profileEvent -> {
// wait for permissions to load, then set the players permission function // Initiate a regular connection and move over to it.
player.setPermissionFunction(event.createFunction(player)); ConnectedPlayer player = new ConnectedPlayer(profileEvent.getGameProfile() == null ? profile : profileEvent.getGameProfile(),
// then call & wait for the login event inbound, apiInbound.getVirtualHost().orElse(null));
return VelocityServer.getServer().getEventManager().fire(new LoginEvent(player));
}) return VelocityServer.getServer().getEventManager().fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
// then complete the connection .thenCompose(event -> {
.thenAcceptAsync(event -> { // wait for permissions to load, then set the players permission function
if (!event.getResult().isAllowed()) { player.setPermissionFunction(event.createFunction(player));
// The component is guaranteed to be provided if the connection was denied. // then call & wait for the login event
inbound.closeWith(Disconnect.create(event.getResult().getReason().get())); return VelocityServer.getServer().getEventManager().fire(new LoginEvent(player));
return; })
} // then complete the connection
.thenAcceptAsync(event -> {
if (inbound.isClosed()) {
// The player was disconnected
return;
}
if (!event.getResult().isAllowed()) {
// The component is guaranteed to be provided if the connection was denied.
inbound.closeWith(Disconnect.create(event.getResult().getReason().get()));
return;
}
handleProxyLogin(player);
}, inbound.getChannel().eventLoop());
});
handleProxyLogin(player);
}, inbound.getChannel().eventLoop());
} }
private void handleProxyLogin(ConnectedPlayer player) { private void handleProxyLogin(ConnectedPlayer player) {