1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Add ProtocolSupport support

Based of the work done by Camotoy on the 1.0

co-authored-by: Camotoy <20743703+camotoy@users.noreply.github.com>
This commit is contained in:
Tim203
2021-01-16 23:58:13 +01:00
parent 6b47ef1a7a
commit f27ad277ed
9 changed files with 292 additions and 35 deletions

View File

@@ -26,6 +26,7 @@
package org.geysermc.floodgate.api.handshake;
import io.netty.channel.Channel;
import java.util.UUID;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin;
@@ -54,6 +55,14 @@ public interface HandshakeData {
*/
BedrockData getBedrockData();
String getJavaUsername();
String getCorrectUsername();
UUID getJavaUniqueId();
UUID getCorrectUniqueId();
/**
* Returns the linked account associated with the client or null if the player isn't linked or
* not a Floodgate player.

View File

@@ -26,25 +26,63 @@
package org.geysermc.floodgate.addon.data;
import io.netty.channel.Channel;
import lombok.AllArgsConstructor;
import java.util.UUID;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin;
import org.geysermc.floodgate.util.Utils;
@Getter
@RequiredArgsConstructor
@AllArgsConstructor
public class HandshakeDataImpl implements HandshakeData {
private final Channel channel;
private final boolean floodgatePlayer;
private final BedrockData bedrockData;
private final String javaUsername;
private final UUID javaUniqueId;
@Setter private LinkedPlayer linkedPlayer;
@Setter private RawSkin rawSkin;
@Setter private String hostname;
@Setter private String disconnectReason;
public HandshakeDataImpl(
Channel channel,
boolean floodgatePlayer,
BedrockData bedrockData,
FloodgateConfig config,
LinkedPlayer linkedPlayer,
RawSkin rawSkin,
String hostname) {
this.channel = channel;
this.floodgatePlayer = floodgatePlayer;
this.bedrockData = bedrockData;
this.linkedPlayer = linkedPlayer;
this.rawSkin = rawSkin;
this.hostname = hostname;
String prefix = config.getUsernamePrefix();
int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length());
String javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength);
if (config.isReplaceSpaces()) {
javaUsername = javaUsername.replaceAll(" ", "_");
}
this.javaUsername = javaUsername;
this.javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
}
@Override
public String getCorrectUsername() {
return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername;
}
@Override
public UUID getCorrectUniqueId() {
return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId;
}
}

View File

@@ -131,8 +131,9 @@ public final class FloodgateHandshakeHandler {
linkedPlayer = fetchLinkedPlayer(Utils.getJavaUuid(bedrockData.getXuid()));
}
HandshakeData handshakeData = new HandshakeDataImpl(channel, true, bedrockData.clone(),
linkedPlayer != null ? linkedPlayer.clone() : null, rawSkin, hostname, null);
HandshakeData handshakeData = new HandshakeDataImpl(
channel, true, bedrockData.clone(), configHolder.get(),
linkedPlayer != null ? linkedPlayer.clone() : null, rawSkin, hostname);
handshakeHandlers.callHandshakeHandlers(handshakeData);
UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid());
@@ -141,7 +142,7 @@ public final class FloodgateHandshakeHandler {
));
FloodgatePlayer player =
FloodgatePlayerImpl.from(bedrockData, handshakeData, configHolder);
FloodgatePlayerImpl.from(bedrockData, handshakeData);
api.addPlayer(player.getJavaUniqueId(), player);
@@ -184,7 +185,7 @@ public final class FloodgateHandshakeHandler {
String hostname) {
HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null,
bedrockData, null, null, hostname, null);
bedrockData, configHolder.get(), null, null, hostname);
handshakeHandlers.callHandshakeHandlers(handshakeData);
if (bedrockData != null) {

View File

@@ -42,8 +42,6 @@ import org.geysermc.floodgate.api.link.PlayerLink;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.api.player.PropertyKey.Result;
import org.geysermc.floodgate.config.FloodgateConfig;
import org.geysermc.floodgate.config.FloodgateConfigHolder;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.InputMode;
@@ -83,18 +81,9 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
protected static FloodgatePlayerImpl from(
BedrockData data,
HandshakeData handshakeData,
FloodgateConfigHolder configHolder) {
HandshakeData handshakeData) {
FloodgateApi api = FloodgateApi.getInstance();
FloodgateConfig config = configHolder.get();
String prefix = config.getUsernamePrefix();
int usernameLength = Math.min(data.getUsername().length(), 16 - prefix.length());
String javaUsername = prefix + data.getUsername().substring(0, usernameLength);
if (config.isReplaceSpaces()) {
javaUsername = javaUsername.replaceAll(" ", "_");
}
UUID javaUniqueId = Utils.getJavaUuid(data.getXuid());
@@ -106,9 +95,10 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
RawSkin skin = handshakeData.getRawSkin();
FloodgatePlayerImpl player = new FloodgatePlayerImpl(
data.getVersion(), data.getUsername(), javaUsername, javaUniqueId, data.getXuid(),
deviceOs, data.getLanguageCode(), uiProfile, inputMode, data.getIp(),
data.isFromProxy(), api instanceof ProxyFloodgateApi, linkedPlayer, skin);
data.getVersion(), data.getUsername(), handshakeData.getJavaUsername(),
javaUniqueId, data.getXuid(), deviceOs, data.getLanguageCode(), uiProfile,
inputMode, data.getIp(), data.isFromProxy(), api instanceof ProxyFloodgateApi,
linkedPlayer, skin);
// RawSkin should be removed, fromProxy should be changed
// and encrypted data can be changed after fetching the linkedPlayer

View File

@@ -28,6 +28,7 @@ package org.geysermc.floodgate;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.InstanceHolder;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.module.ServerCommonModule;
import org.geysermc.floodgate.module.SpigotAddonModule;
@@ -35,6 +36,8 @@ import org.geysermc.floodgate.module.SpigotCommandModule;
import org.geysermc.floodgate.module.SpigotListenerModule;
import org.geysermc.floodgate.module.SpigotPlatformModule;
import org.geysermc.floodgate.util.ReflectionUtils;
import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
public final class SpigotPlugin extends JavaPlugin {
private SpigotPlatform platform;
@@ -64,6 +67,13 @@ public final class SpigotPlugin extends JavaPlugin {
new SpigotListenerModule(),
new SpigotAddonModule()
);
// add ProtocolSupport support (hack)
if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) {
InstanceHolder.getHandshakeHandlers()
.addHandshakeHandler(new SpigotProtocolSupportHandler());
SpigotProtocolSupportListener.registerHack(this);
}
}
@Override

View File

@@ -27,7 +27,6 @@ package org.geysermc.floodgate.addon.data;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass;
@@ -52,10 +51,10 @@ import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.ReflectionUtils;
import org.geysermc.floodgate.util.SpigotUtils;
@RequiredArgsConstructor
public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object> {
private static final Field IS_BUNGEE_DATA;
private static final Field SOCKET_ADDRESS;
private static final Class<?> HANDSHAKE_PACKET;
private static final Field HANDSHAKE_HOST;
@@ -77,10 +76,6 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
private static final Object READY_TO_ACCEPT_PROTOCOL_STATE;
static {
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
IS_BUNGEE_DATA = getField(spigotConfig, "bungee");
checkNotNull(IS_BUNGEE_DATA, "bungee field cannot be null. Are you using CraftBukkit?");
Class<?> networkManager = getPrefixedClass("NetworkManager");
checkNotNull(networkManager, "NetworkManager class cannot be null");
@@ -204,7 +199,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
}
player = result.getFloodgatePlayer();
bungeeData = isBungeeData();
bungeeData = SpigotUtils.isBungeeData();
setValue(packet, HANDSHAKE_HOST, handshakeData.getHostname());
@@ -265,9 +260,4 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
super.exceptionCaught(ctx, cause);
cause.printStackTrace();
}
@SuppressWarnings("ConstantConditions")
private boolean isBungeeData() {
return ReflectionUtils.getCastedValue(null, IS_BUNGEE_DATA);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.util;
import io.netty.channel.Channel;
import java.lang.reflect.Method;
import java.util.UUID;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.handshake.HandshakeHandler;
public class SpigotProtocolSupportHandler implements HandshakeHandler {
private static final Method getFromChannel;
private static final Method getLoginProfile;
private static final Method setName;
private static final Method setOriginalName;
private static final Method setUuid;
private static final Method setOriginalUuid;
private static final Method getNetworkManagerWrapper;
private static final Method getPacketListener;
private static final Method handleLoginStart;
static {
Class<?> connectionImpl =
ReflectionUtils.getClass("protocolsupport.protocol.ConnectionImpl");
getFromChannel = ReflectionUtils.getMethod(connectionImpl, "getFromChannel", Channel.class);
getLoginProfile = ReflectionUtils.getMethod(connectionImpl, "getLoginProfile");
Class<?> profile = ReflectionUtils.getClass("protocolsupport.api.utils.Profile");
setName = ReflectionUtils.getMethod(profile, "setName", String.class);
setOriginalName = ReflectionUtils.getMethod(profile, "setOriginalName", String.class);
setUuid = ReflectionUtils.getMethod(profile, "setUUID", UUID.class);
setOriginalUuid = ReflectionUtils.getMethod(profile, "setOriginalUUID", UUID.class);
getNetworkManagerWrapper =
ReflectionUtils.getMethod(connectionImpl, "getNetworkManagerWrapper");
Class<?> networkManagerWrapper =
ReflectionUtils.getClass("protocolsupport.zplatform.network.NetworkManagerWrapper");
getPacketListener = ReflectionUtils.getMethod(networkManagerWrapper, "getPacketListener");
Class<?> loginListener = ReflectionUtils.getClass(
"protocolsupport.protocol.packet.handler.AbstractLoginListener");
handleLoginStart = ReflectionUtils.getMethod(loginListener, "handleLoginStart", UUID.class);
}
@Override
public void handle(HandshakeData data) {
if (data.isFloodgatePlayer() && SpigotUtils.isBungeeData()) {
Object connection = ReflectionUtils.invoke(null, getFromChannel, data.getChannel());
Object profile = ReflectionUtils.invoke(connection, getLoginProfile);
// set correct uuid and name on ProtocolSupport's end, since we skip the LoginStart
ReflectionUtils.invoke(profile, setName, data.getCorrectUsername());
ReflectionUtils.invoke(profile, setOriginalName, data.getCorrectUsername());
ReflectionUtils.invoke(profile, setUuid, data.getCorrectUniqueId());
ReflectionUtils.invoke(profile, setOriginalUuid, data.getCorrectUniqueId());
Object temp = ReflectionUtils.invoke(connection, getNetworkManagerWrapper);
temp = ReflectionUtils.invoke(temp, getPacketListener);
ReflectionUtils.invoke(temp, handleLoginStart, data.getCorrectUsername());
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.util;
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass;
import java.lang.reflect.Method;
import java.util.UUID;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.geysermc.floodgate.api.FloodgateApi;
@SuppressWarnings("unchecked")
public final class SpigotProtocolSupportListener {
public static void registerHack(Plugin plugin) {
String prefix = ReflectionUtils.getPrefix();
ReflectionUtils.setPrefix("protocolsupport.api");
Class<? extends Event> playerLoginStartEvent =
(Class<? extends Event>) getPrefixedClass("events.PlayerLoginStartEvent");
Method setOnlineMode = getMethod(playerLoginStartEvent, "setOnlineMode", boolean.class);
Class<?> connectionEvent = getPrefixedClass("events.ConnectionEvent");
Method getConnection = getMethod(connectionEvent, "getConnection");
Class<?> connection = getPrefixedClass("Connection");
Method getProfile = getMethod(connection, "getProfile");
Class<?> profile = getPrefixedClass("utils.Profile");
Method getUuid = getMethod(profile, "getUUID");
plugin.getServer().getPluginManager().registerEvent(
playerLoginStartEvent, new Listener() {}, EventPriority.MONITOR,
(listener, event) -> {
Object temp = ReflectionUtils.invoke(event, getConnection);
temp = ReflectionUtils.invoke(temp, getProfile);
UUID uuid = ReflectionUtils.castedInvoke(temp, getUuid);
// a normal player that doesn't have a UUID set
if (uuid == null) {
return;
}
if (FloodgateApi.getInstance().isFloodgatePlayer(uuid)) {
// otherwise ProtocolSupport attempts to connect with online mode
ReflectionUtils.invoke(event, setOnlineMode, false);
}
},
plugin);
ReflectionUtils.setPrefix(prefix);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Floodgate
*/
package org.geysermc.floodgate.util;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
import java.lang.reflect.Field;
@SuppressWarnings("ConstantConditions")
public final class SpigotUtils {
private static final Field IS_BUNGEE_DATA;
static {
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
IS_BUNGEE_DATA = getField(spigotConfig, "bungee");
checkNotNull(IS_BUNGEE_DATA, "bungee field cannot be null. Are you using CraftBukkit?");
}
public static boolean isBungeeData() {
return ReflectionUtils.getCastedValue(null, IS_BUNGEE_DATA);
}
}