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

Developers can now change some Floodgate related data during handshake

This commit is contained in:
Tim203
2021-01-12 20:49:24 +01:00
parent dd4a12a5e5
commit 9b0cbd5cdd
21 changed files with 612 additions and 124 deletions

View File

@@ -27,6 +27,7 @@ package org.geysermc.floodgate.api;
import java.util.UUID; import java.util.UUID;
import lombok.Getter; import lombok.Getter;
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
import org.geysermc.floodgate.api.inject.PlatformInjector; import org.geysermc.floodgate.api.inject.PlatformInjector;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
@@ -34,10 +35,16 @@ public final class InstanceHolder {
@Getter private static FloodgateApi instance; @Getter private static FloodgateApi instance;
@Getter private static PlayerLink playerLink; @Getter private static PlayerLink playerLink;
@Getter private static PlatformInjector injector; @Getter private static PlatformInjector injector;
@Getter private static HandshakeHandlers handshakeHandlers;
private static UUID key; private static UUID key;
public static boolean setInstance(FloodgateApi floodgateApi, PlayerLink link, public static boolean setInstance(
PlatformInjector platformInjector, UUID key) { FloodgateApi floodgateApi,
PlayerLink link,
PlatformInjector platformInjector,
HandshakeHandlers handshakeHandlers,
UUID key
) {
if (instance == null) { if (instance == null) {
InstanceHolder.key = key; InstanceHolder.key = key;
} else if (!InstanceHolder.key.equals(key)) { } else if (!InstanceHolder.key.equals(key)) {
@@ -46,6 +53,7 @@ public final class InstanceHolder {
instance = floodgateApi; instance = floodgateApi;
playerLink = link; playerLink = link;
injector = platformInjector; injector = platformInjector;
InstanceHolder.handshakeHandlers = handshakeHandlers;
return true; return true;
} }

View File

@@ -0,0 +1,114 @@
/*
* 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.api.handshake;
import io.netty.channel.Channel;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin;
/**
* For advanced users only! You shouldn't play with this unless you know what you're doing.<br>
* <br>
* This class allows you change specific things of a Bedrock player before it is applied to the
* server. Note that at the time I'm writing this that the HandshakeData is created after requesting
* the player link. So the link is present here, if applicable.
*/
public interface HandshakeData {
/**
* Returns the Channel holding the connection between the client and the server.
*/
Channel getChannel();
/**
* Returns true if the given player is a Floodgate player, false otherwise.
*/
boolean isFloodgatePlayer();
/**
* Returns the decrypted BedrockData sent by Geyser or null if the player isn't a Floodgate
* player.
*/
BedrockData getBedrockData();
/**
* Returns the linked account associated with the client or null if the player isn't linked or
* not a Floodgate player.
*/
LinkedPlayer getLinkedPlayer();
/**
* Set the LinkedPlayer. This will be ignored if the player isn't a Floodgate player
*
* @param player the player to use as link
*/
void setLinkedPlayer(LinkedPlayer player);
/**
* Returns the skin of the client. Can be null even though the player is a Floodgate player.
*/
RawSkin getRawSkin();
/**
* Manually set the skin of the client.
*
* @param rawSkin the skin of the client
*/
void setRawSkin(RawSkin rawSkin);
/**
* Returns the hostname used in the handshake packet. This is the hostname after Floodgate
* removed the data.
*/
String getHostname();
/**
* Set the hostname of the handshake packet. Changing it here will also change it in the
* handshake packet.
*
* @param hostname the new hostname
*/
void setHostname(String hostname);
/**
* Returns the reason to disconnect the current player.
*/
String getDisconnectReason();
/**
* Set the reason to disconnect the current player.
*
* @param reason the reason to disconnect
*/
void setDisconnectReason(String reason);
/**
* Returns if the player should be disconnected
*/
default boolean shouldDisconnect() {
return getDisconnectReason() != null;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.api.handshake;
/**
* This class allows you to change and/or get specific data of the Bedrock client before Floodgate
* does something with this data. This means that Floodgate decrypts the data, then calls the
* handshake handlers and then applies the data to the connection.<br>
* <br>
* /!\ Note that this class will be called for both Java and Bedrock connections, but {@link
* HandshakeData#isFloodgatePlayer()} will be false and Floodgate related methods will return null
* for Java players
*/
@FunctionalInterface
public interface HandshakeHandler {
/**
* Method that will be called during the time that Floodgate handles the handshake.
*
* @param data the data usable during the handshake
*/
void handle(HandshakeData data);
}

View File

@@ -0,0 +1,51 @@
/*
* 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.api.handshake;
public interface HandshakeHandlers {
/**
* Register a custom handshake handler. This can be used to check and edit the player during the
* handshake handling.
*
* @param handshakeHandler the handshake handler to register
* @return a random (unique) int to identify this handshake handler or -1 if null
*/
int addHandshakeHandler(HandshakeHandler handshakeHandler);
/**
* Removes a custom handshake handler by id.
*
* @param handshakeHandlerId the id of the handshake handler to remove
*/
void removeHandshakeHandler(int handshakeHandlerId);
/**
* Remove a custom handshake handler by instance.
*
* @param handshakeHandler the instance to remove
*/
void removeHandshakeHandler(Class<? extends HandshakeHandler> handshakeHandler);
}

View File

@@ -73,7 +73,7 @@ public interface PlatformInjector {
* *
* @param addon the class of the addon to remove from the addon list * @param addon the class of the addon to remove from the addon list
* @param <T> the addon type * @param <T> the addon type
* @return the instance that was present when removing * @return the removed addon instance
*/ */
<T extends InjectorAddon> T removeAddon(Class<T> addon); <T extends InjectorAddon> T removeAddon(Class<T> addon);
} }

View File

@@ -89,4 +89,9 @@ public interface FloodgateLogger {
* this method, but they will be hidden from the console. * this method, but they will be hidden from the console.
*/ */
void disableDebug(); void disableDebug();
/**
* Returns if debugging is enabled
*/
boolean isDebug();
} }

View File

@@ -29,18 +29,18 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static org.geysermc.floodgate.player.HandshakeHandler.ResultType; import static org.geysermc.floodgate.player.HandshakeHandler.ResultType;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.protocol.packet.Handshake; import net.md_5.bungee.protocol.packet.Handshake;
import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.ProxyFloodgateApi;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.api.player.PropertyKey;
@@ -81,10 +81,6 @@ public final class BungeeDataHandler {
checkNotNull(CACHED_HANDSHAKE_PACKET, "Cached handshake packet field cannot be null"); checkNotNull(CACHED_HANDSHAKE_PACKET, "Cached handshake packet field cannot be null");
} }
@Inject
@Named("playerAttribute")
private AttributeKey<FloodgatePlayer> playerAttribute;
@Inject private Plugin plugin; @Inject private Plugin plugin;
@Inject private ProxyFloodgateConfig config; @Inject private ProxyFloodgateConfig config;
@Inject private ProxyFloodgateApi api; @Inject private ProxyFloodgateApi api;
@@ -94,16 +90,22 @@ public final class BungeeDataHandler {
public void handlePreLogin(PreLoginEvent event) { public void handlePreLogin(PreLoginEvent event) {
event.registerIntent(plugin); event.registerIntent(plugin);
plugin.getProxy().getScheduler().runAsync(plugin, () -> { plugin.getProxy().getScheduler().runAsync(plugin, () -> {
String extraData = ReflectionUtils.getCastedValue( PendingConnection connection = event.getConnection();
event.getConnection(), EXTRA_HANDSHAKE_DATA
);
Object channelWrapper = String extraData = ReflectionUtils.getCastedValue(connection, EXTRA_HANDSHAKE_DATA);
ReflectionUtils.getValue(event.getConnection(), PLAYER_CHANNEL_WRAPPER);
Object channelWrapper = ReflectionUtils.getValue(connection, PLAYER_CHANNEL_WRAPPER);
Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL);
HandshakeResult result = handler.handle(channel, extraData); HandshakeResult result = handler.handle(channel, extraData);
HandshakeData handshakeData = result.getHandshakeData();
if (handshakeData.getDisconnectReason() != null) {
//noinspection ConstantConditions
channel.close(); // todo disconnect with message
return;
}
switch (result.getResultType()) { switch (result.getResultType()) {
case EXCEPTION: case EXCEPTION:
event.setCancelReason(config.getDisconnect().getInvalidKey()); event.setCancelReason(config.getDisconnect().getInvalidKey());
@@ -124,12 +126,10 @@ public final class BungeeDataHandler {
FloodgatePlayer player = result.getFloodgatePlayer(); FloodgatePlayer player = result.getFloodgatePlayer();
event.getConnection().setOnlineMode(false); connection.setOnlineMode(false);
event.getConnection().setUniqueId(player.getCorrectUniqueId()); connection.setUniqueId(player.getCorrectUniqueId());
ReflectionUtils.setValue( ReflectionUtils.setValue(connection, PLAYER_NAME, player.getCorrectUsername());
event.getConnection(), PLAYER_NAME, player.getCorrectUsername()
);
SocketAddress remoteAddress = SocketAddress remoteAddress =
ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS); ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS);

View File

@@ -34,6 +34,7 @@ import java.nio.file.Path;
import java.util.UUID; import java.util.UUID;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.InstanceHolder; import org.geysermc.floodgate.api.InstanceHolder;
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
import org.geysermc.floodgate.api.inject.PlatformInjector; import org.geysermc.floodgate.api.inject.PlatformInjector;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
@@ -64,8 +65,12 @@ public class FloodgatePlatform {
} }
@Inject @Inject
public void init(@Named("dataDirectory") Path dataDirectory, ConfigLoader configLoader, public void init(
FloodgateConfigHolder configHolder) { @Named("dataDirectory") Path dataDirectory,
ConfigLoader configLoader,
FloodgateConfigHolder configHolder,
HandshakeHandlers handshakeHandlers
) {
if (!Files.isDirectory(dataDirectory)) { if (!Files.isDirectory(dataDirectory)) {
try { try {
@@ -85,7 +90,7 @@ public class FloodgatePlatform {
guice = guice.createChildInjector(new ConfigLoadedModule(config)); guice = guice.createChildInjector(new ConfigLoadedModule(config));
PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load(); PlayerLink link = guice.getInstance(PlayerLinkLoader.class).load();
InstanceHolder.setInstance(api, link, this.injector, KEY); InstanceHolder.setInstance(api, link, this.injector, handshakeHandlers, KEY);
} }
public boolean enable(Module... postInitializeModules) { public boolean enable(Module... postInitializeModules) {

View File

@@ -0,0 +1,50 @@
/*
* 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.addon.data;
import io.netty.channel.Channel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin;
@Getter
@RequiredArgsConstructor
@AllArgsConstructor
public class HandshakeDataImpl implements HandshakeData {
private final Channel channel;
private final boolean floodgatePlayer;
private final BedrockData bedrockData;
@Setter private LinkedPlayer linkedPlayer;
@Setter private RawSkin rawSkin;
@Setter private String hostname;
@Setter private String disconnectReason;
}

View File

@@ -0,0 +1,77 @@
/*
* 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.addon.data;
import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;
import java.util.Random;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.handshake.HandshakeHandler;
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
public class HandshakeHandlersImpl implements HandshakeHandlers {
private final Random random = new Random();
private final IntObjectMap<HandshakeHandler> handshakeHandlers = new IntObjectHashMap<>();
@Override
public int addHandshakeHandler(HandshakeHandler handshakeHandler) {
if (handshakeHandler == null) {
return -1;
}
int key;
do {
key = random.nextInt(Integer.MAX_VALUE);
} while (handshakeHandlers.putIfAbsent(key, handshakeHandler) != null);
return key;
}
@Override
public void removeHandshakeHandler(int handshakeHandlerId) {
// key is always positive
if (handshakeHandlerId <= 0) {
return;
}
handshakeHandlers.remove(handshakeHandlerId);
}
@Override
public void removeHandshakeHandler(Class<? extends HandshakeHandler> handshakeHandler) {
if (HandshakeHandler.class == handshakeHandler) {
return;
}
handshakeHandlers.values().removeIf(handler -> handler.getClass() == handshakeHandler);
}
public void callHandshakeHandlers(HandshakeData handshakeData) {
for (HandshakeHandler handshakeHandler : handshakeHandlers.values()) {
handshakeHandler.handle(handshakeData);
}
}
}

View File

@@ -35,6 +35,7 @@ import org.geysermc.cumulus.util.FormBuilder;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler;
import org.geysermc.floodgate.player.FloodgatePlayerImpl; import org.geysermc.floodgate.player.FloodgatePlayerImpl;
import org.geysermc.floodgate.util.Utils;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SimpleFloodgateApi implements FloodgateApi { public class SimpleFloodgateApi implements FloodgateApi {
@@ -66,7 +67,7 @@ public class SimpleFloodgateApi implements FloodgateApi {
@Override @Override
public UUID createJavaPlayerId(long xuid) { public UUID createJavaPlayerId(long xuid) {
return new UUID(0, xuid); return Utils.getJavaUuid(xuid);
} }
@Override @Override

View File

@@ -26,52 +26,50 @@
package org.geysermc.floodgate.link; package org.geysermc.floodgate.link;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.inject.Inject;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.link.LinkRequestResult; import org.geysermc.floodgate.api.link.LinkRequestResult;
import org.geysermc.floodgate.util.HttpUtils; import org.geysermc.floodgate.util.HttpUtils;
import org.geysermc.floodgate.util.HttpUtils.HttpResponse; import org.geysermc.floodgate.util.HttpUtils.HttpResponse;
import org.geysermc.floodgate.util.LinkedPlayer; import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.Utils;
public class GlobalPlayerLinking extends CommonPlayerLink { public class GlobalPlayerLinking extends CommonPlayerLink {
private static final String GET_BEDROCK_LINK = "http://localhost:4000/api/link/bedrock?xuid="; private static final String GET_BEDROCK_LINK = "http://localhost:4000/api/link/bedrock?xuid=";
@Inject private FloodgateApi api;
@Override @Override
public void load() {} public void load() {
}
@Override @Override
public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) { public CompletableFuture<LinkedPlayer> getLinkedPlayer(UUID bedrockId) {
return CompletableFuture.supplyAsync( return CompletableFuture.supplyAsync(
() -> { () -> {
HttpResponse response = HttpResponse response =
HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits()); HttpUtils.get(GET_BEDROCK_LINK + bedrockId.getLeastSignificantBits());
// both on code != 200 and fails with 200 'success' will be false // both on code != 200 and fails with 200 'success' will be false
if (!response.getResponse().get("success").getAsBoolean()) { if (!response.getResponse().get("success").getAsBoolean()) {
getLogger().error( getLogger().error(
"Failed to request link for {}: {}", "Failed to request link for {}: {}",
bedrockId.getLeastSignificantBits(), bedrockId.getLeastSignificantBits(),
response.getResponse().get("message").getAsString()); response.getResponse().get("message").getAsString());
return null; return null;
} }
JsonObject data = response.getResponse().getAsJsonObject("data"); JsonObject data = response.getResponse().getAsJsonObject("data");
// no link if data is empty // no link if data is empty
if (data.size() == 0) { if (data.size() == 0) {
return null; return null;
} }
return LinkedPlayer.of( return LinkedPlayer.of(
data.get("javaName").getAsString(), data.get("javaName").getAsString(),
UUID.fromString(data.get("javaId").getAsString()), UUID.fromString(data.get("javaId").getAsString()),
api.createJavaPlayerId(data.get("bedrockId").getAsLong())); Utils.getJavaUuid(data.get("bedrockId").getAsLong()));
}, },
getExecutorService()); getExecutorService());
} }
@Override @Override
@@ -117,8 +115,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink {
public CompletableFuture<?> createLinkRequest( public CompletableFuture<?> createLinkRequest(
UUID javaId, UUID javaId,
String javaUsername, String javaUsername,
String bedrockUsername String bedrockUsername) {
) {
return null; return null;
} }
@@ -127,8 +124,7 @@ public class GlobalPlayerLinking extends CommonPlayerLink {
UUID bedrockId, UUID bedrockId,
String javaUsername, String javaUsername,
String bedrockUsername, String bedrockUsername,
String code String code) {
) {
return null; return null;
} }
} }

View File

@@ -86,4 +86,9 @@ public final class JavaUtilFloodgateLogger implements FloodgateLogger {
logger.setLevel(originLevel); logger.setLevel(originLevel);
} }
} }
@Override
public boolean isDebug() {
return logger.getLevel() == Level.ALL;
}
} }

View File

@@ -32,8 +32,10 @@ import com.google.inject.name.Named;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import java.nio.file.Path; import java.nio.file.Path;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.addon.data.HandshakeHandlersImpl;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.SimpleFloodgateApi;
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
import org.geysermc.floodgate.api.inject.PlatformInjector; import org.geysermc.floodgate.api.inject.PlatformInjector;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
@@ -60,6 +62,7 @@ public class CommonModule extends AbstractModule {
protected void configure() { protected void configure() {
bind(FloodgateApi.class).to(SimpleFloodgateApi.class); bind(FloodgateApi.class).to(SimpleFloodgateApi.class);
bind(PlatformInjector.class).to(CommonPlatformInjector.class); bind(PlatformInjector.class).to(CommonPlatformInjector.class);
bind(HandshakeHandlers.class).to(HandshakeHandlersImpl.class);
} }
@Provides @Provides
@@ -120,15 +123,30 @@ public class CommonModule extends AbstractModule {
return new LanguageManager(configHolder, logger); return new LanguageManager(configHolder, logger);
} }
@Provides
@Singleton
public HandshakeHandlersImpl handshakeHandlers() {
return new HandshakeHandlersImpl();
}
@Provides @Provides
@Singleton @Singleton
public HandshakeHandler handshakeHandler( public HandshakeHandler handshakeHandler(
HandshakeHandlersImpl handshakeHandlers,
SimpleFloodgateApi api, SimpleFloodgateApi api,
FloodgateCipher cipher, FloodgateCipher cipher,
FloodgateConfigHolder configHolder, FloodgateConfigHolder configHolder,
@Named("playerAttribute") AttributeKey<FloodgatePlayer> playerAttribute @Named("playerAttribute") AttributeKey<FloodgatePlayer> playerAttribute,
FloodgateLogger logger
) { ) {
return new HandshakeHandler(api, cipher, configHolder, playerAttribute); return new HandshakeHandler(
handshakeHandlers,
api,
cipher,
configHolder,
playerAttribute,
logger
);
} }
@Provides @Provides

View File

@@ -37,6 +37,7 @@ import lombok.Setter;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.InstanceHolder; import org.geysermc.floodgate.api.InstanceHolder;
import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.ProxyFloodgateApi;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.link.PlayerLink;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.api.player.PropertyKey;
@@ -49,6 +50,7 @@ import org.geysermc.floodgate.util.InputMode;
import org.geysermc.floodgate.util.LinkedPlayer; import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.RawSkin;
import org.geysermc.floodgate.util.UiProfile; import org.geysermc.floodgate.util.UiProfile;
import org.geysermc.floodgate.util.Utils;
@Getter @Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@@ -79,8 +81,11 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
*/ */
@Setter private boolean login = true; @Setter private boolean login = true;
protected static FloodgatePlayerImpl from(BedrockData data, RawSkin skin, protected static FloodgatePlayerImpl from(
FloodgateConfigHolder configHolder) { BedrockData data,
HandshakeData handshakeData,
FloodgateConfigHolder configHolder) {
FloodgateApi api = FloodgateApi.getInstance(); FloodgateApi api = FloodgateApi.getInstance();
FloodgateConfig config = configHolder.get(); FloodgateConfig config = configHolder.get();
@@ -91,22 +96,14 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
javaUsername = javaUsername.replaceAll(" ", "_"); javaUsername = javaUsername.replaceAll(" ", "_");
} }
UUID javaUniqueId = api.createJavaPlayerId(Long.parseLong(data.getXuid())); UUID javaUniqueId = Utils.getJavaUuid(data.getXuid());
DeviceOs deviceOs = DeviceOs.getById(data.getDeviceOs()); DeviceOs deviceOs = DeviceOs.getById(data.getDeviceOs());
UiProfile uiProfile = UiProfile.getById(data.getUiProfile()); UiProfile uiProfile = UiProfile.getById(data.getUiProfile());
InputMode inputMode = InputMode.getById(data.getInputMode()); InputMode inputMode = InputMode.getById(data.getInputMode());
LinkedPlayer linkedPlayer; LinkedPlayer linkedPlayer = handshakeData.getLinkedPlayer();
RawSkin skin = handshakeData.getRawSkin();
// we'll use the LinkedPlayer provided by Bungee or Velocity (if they included one)
if (data.hasPlayerLink()) {
linkedPlayer = data.getLinkedPlayer();
} else {
// every implementation (Bukkit, Bungee and Velocity) run this constructor async,
// so we should be fine doing this synchronised.
linkedPlayer = fetchLinkedPlayer(api.getPlayerLink(), javaUniqueId);
}
FloodgatePlayerImpl player = new FloodgatePlayerImpl( FloodgatePlayerImpl player = new FloodgatePlayerImpl(
data.getVersion(), data.getUsername(), javaUsername, javaUniqueId, data.getXuid(), data.getVersion(), data.getUsername(), javaUsername, javaUniqueId, data.getXuid(),
@@ -149,8 +146,9 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer {
* isn't enabled * isn't enabled
* @see #fetchLinkedPlayer(PlayerLink, UUID) for the sync version * @see #fetchLinkedPlayer(PlayerLink, UUID) for the sync version
*/ */
public static CompletableFuture<LinkedPlayer> fetchLinkedPlayerAsync(PlayerLink link, public static CompletableFuture<LinkedPlayer> fetchLinkedPlayerAsync(
UUID javaUniqueId) { PlayerLink link,
UUID javaUniqueId) {
return link.isEnabled() ? return link.isEnabled() ?
link.getLinkedPlayer(javaUniqueId) : link.getLinkedPlayer(javaUniqueId) :
CompletableFuture.completedFuture(null); CompletableFuture.completedFuture(null);

View File

@@ -31,12 +31,18 @@ import com.google.common.base.Charsets;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.addon.data.HandshakeDataImpl;
import org.geysermc.floodgate.addon.data.HandshakeHandlersImpl;
import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.SimpleFloodgateApi;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.config.FloodgateConfigHolder;
@@ -45,29 +51,39 @@ import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.util.Base64Utils; import org.geysermc.floodgate.util.Base64Utils;
import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.BedrockData;
import org.geysermc.floodgate.util.InvalidFormatException; import org.geysermc.floodgate.util.InvalidFormatException;
import org.geysermc.floodgate.util.LinkedPlayer;
import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.RawSkin;
import org.geysermc.floodgate.util.Utils;
@RequiredArgsConstructor @RequiredArgsConstructor
public final class HandshakeHandler { public final class HandshakeHandler {
private final HandshakeHandlersImpl handshakeHandlers;
private final SimpleFloodgateApi api; private final SimpleFloodgateApi api;
private final FloodgateCipher cipher; private final FloodgateCipher cipher;
private final FloodgateConfigHolder configHolder; private final FloodgateConfigHolder configHolder;
private final AttributeKey<FloodgatePlayer> playerAttribute; private final AttributeKey<FloodgatePlayer> playerAttribute;
private final FloodgateLogger logger;
public HandshakeResult handle(Channel channel, @NonNull String handshakeData) { public HandshakeResult handle(Channel channel, @NonNull String hostname) {
try { try {
String[] dataArray = handshakeData.split("\0"); String[] split = hostname.split("\0");
String data = null; String data = null;
for (String value : dataArray) {
if (FloodgateCipher.hasHeader(value)) { StringBuilder hostnameBuilder = new StringBuilder();
for (String value : split) {
if (data == null && FloodgateCipher.hasHeader(value)) {
data = value; data = value;
break; continue;
} }
hostnameBuilder.append(value);
} }
// hostname now doesn't have Floodgate data anymore if it had
hostname = hostnameBuilder.toString();
if (data == null) { if (data == null) {
return ResultType.NOT_FLOODGATE_DATA.getCachedResult(); return callHandlerAndReturnResult(
ResultType.NOT_FLOODGATE_DATA,
channel, null, hostname);
} }
// header + base64 iv - 0x21 - encrypted data - 0x21 - RawSkin // header + base64 iv - 0x21 - encrypted data - 0x21 - RawSkin
@@ -91,7 +107,9 @@ public final class HandshakeHandler {
BedrockData bedrockData = BedrockData.fromString(decrypted); BedrockData bedrockData = BedrockData.fromString(decrypted);
if (bedrockData.getDataLength() != EXPECTED_LENGTH) { if (bedrockData.getDataLength() != EXPECTED_LENGTH) {
return ResultType.INVALID_DATA_LENGTH.getCachedResult(); return callHandlerAndReturnResult(
ResultType.INVALID_DATA_LENGTH,
channel, bedrockData, hostname);
} }
RawSkin rawSkin = null; RawSkin rawSkin = null;
@@ -102,7 +120,29 @@ public final class HandshakeHandler {
rawSkin = RawSkin.decode(rawSkinData); rawSkin = RawSkin.decode(rawSkinData);
} }
FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, rawSkin, configHolder); LinkedPlayer linkedPlayer;
// we'll use the LinkedPlayer provided by Bungee or Velocity (if they included one)
if (bedrockData.hasPlayerLink()) {
linkedPlayer = bedrockData.getLinkedPlayer();
} else {
// every implementation (Bukkit, Bungee and Velocity) run this constructor async,
// so we should be fine doing this synchronised.
linkedPlayer = fetchLinkedPlayer(Utils.getJavaUuid(bedrockData.getXuid()));
}
HandshakeData handshakeData = new HandshakeDataImpl(channel, true, bedrockData.clone(),
linkedPlayer != null ? linkedPlayer.clone() : null, rawSkin, hostname, null);
handshakeHandlers.callHandshakeHandlers(handshakeData);
UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid());
handshakeData.setHostname(correctHostname(
handshakeData.getHostname(), bedrockData, javaUuid
));
FloodgatePlayer player =
FloodgatePlayerImpl.from(bedrockData, handshakeData, configHolder);
api.addPlayer(player.getJavaUniqueId(), player); api.addPlayer(player.getJavaUniqueId(), player);
channel.attr(playerAttribute).set(player); channel.attr(playerAttribute).set(player);
@@ -111,19 +151,76 @@ public final class HandshakeHandler {
InetSocketAddress socketAddress = new InetSocketAddress(bedrockData.getIp(), port); InetSocketAddress socketAddress = new InetSocketAddress(bedrockData.getIp(), port);
player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress); player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress);
return new HandshakeResult(ResultType.SUCCESS, dataArray, bedrockData, player); return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player);
} catch (InvalidFormatException formatException) { } catch (InvalidFormatException formatException) {
// only header exceptions should return 'not floodgate data', // only header exceptions should return 'not floodgate data',
// all the other format exceptions are because of invalid/tempered Floodgate data // all the other format exceptions are because of invalid/tempered Floodgate data
if (formatException.isHeader()) { if (formatException.isHeader()) {
return ResultType.NOT_FLOODGATE_DATA.getCachedResult(); return callHandlerAndReturnResult(
ResultType.NOT_FLOODGATE_DATA,
channel, null, hostname);
} }
formatException.printStackTrace(); formatException.printStackTrace();
return ResultType.EXCEPTION.getCachedResult();
return callHandlerAndReturnResult(
ResultType.EXCEPTION,
channel, null, hostname);
} catch (Exception exception) { } catch (Exception exception) {
exception.printStackTrace(); exception.printStackTrace();
return ResultType.EXCEPTION.getCachedResult();
return callHandlerAndReturnResult(
ResultType.EXCEPTION,
channel, null, hostname);
}
}
private HandshakeResult callHandlerAndReturnResult(
ResultType resultType,
Channel channel,
BedrockData bedrockData,
String hostname
) {
HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null,
bedrockData, null, null, hostname, null);
handshakeHandlers.callHandshakeHandlers(handshakeData);
if (bedrockData != null) {
UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid());
handshakeData.setHostname(correctHostname(
handshakeData.getHostname(), bedrockData, javaUuid
));
}
return new HandshakeResult(resultType, handshakeData, bedrockData, null);
}
private String correctHostname(String hostname, BedrockData data, UUID correctUuid) {
// replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid
String[] split = hostname.split("\0");
if (split.length >= 3) {
if (logger.isDebug()) {
logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'",
split[1], data.getIp(), split[2], correctUuid.toString());
}
split[1] = data.getIp();
split[2] = correctUuid.toString();
}
return String.join("\0", split);
}
private LinkedPlayer fetchLinkedPlayer(UUID javaUniqueId) {
if (!api.getPlayerLink().isEnabled()) {
return null;
}
try {
return api.getPlayerLink().getLinkedPlayer(javaUniqueId).get();
} catch (InterruptedException | ExecutionException exception) {
exception.printStackTrace();
return null;
} }
} }
@@ -131,21 +228,14 @@ public final class HandshakeHandler {
EXCEPTION, EXCEPTION,
NOT_FLOODGATE_DATA, NOT_FLOODGATE_DATA,
INVALID_DATA_LENGTH, INVALID_DATA_LENGTH,
SUCCESS; SUCCESS
@Getter
private final HandshakeResult cachedResult;
ResultType() {
cachedResult = new HandshakeResult(this, null, null, null);
}
} }
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter @Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public static class HandshakeResult { public static class HandshakeResult {
private final ResultType resultType; private final ResultType resultType;
private final String[] handshakeData; private final HandshakeData handshakeData;
private final BedrockData bedrockData; private final BedrockData bedrockData;
private final FloodgatePlayer floodgatePlayer; private final FloodgatePlayer floodgatePlayer;
} }

View File

@@ -38,6 +38,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
public class Utils { public class Utils {
/** /**
@@ -62,8 +63,9 @@ public class Utils {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (;;) { for (;;) {
String line = reader.readLine(); String line = reader.readLine();
if (line == null) if (line == null) {
break; break;
}
result.add(line); result.add(line);
} }
return result; return result;
@@ -79,4 +81,12 @@ public class Utils {
public static String getLocale(Locale locale) { public static String getLocale(Locale locale) {
return locale.getLanguage() + "_" + locale.getCountry(); return locale.getLanguage() + "_" + locale.getCountry();
} }
public static UUID getJavaUuid(long xuid) {
return new UUID(0, xuid);
}
public static UUID getJavaUuid(String xuid) {
return getJavaUuid(Long.parseLong(xuid));
}
} }

View File

@@ -43,6 +43,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.api.player.PropertyKey;
@@ -159,7 +160,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
private final HandshakeHandler handshakeHandler; private final HandshakeHandler handshakeHandler;
private final FloodgateLogger logger; private final FloodgateLogger logger;
private Object networkManager; private Object networkManager;
private FloodgatePlayer fPlayer; private FloodgatePlayer player;
private boolean bungeeData; private boolean bungeeData;
private boolean done; private boolean done;
@@ -180,6 +181,13 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
String handshakeValue = getCastedValue(packet, HANDSHAKE_HOST); String handshakeValue = getCastedValue(packet, HANDSHAKE_HOST);
HandshakeResult result = handshakeHandler.handle(ctx.channel(), handshakeValue); HandshakeResult result = handshakeHandler.handle(ctx.channel(), handshakeValue);
HandshakeData handshakeData = result.getHandshakeData();
if (handshakeData.getDisconnectReason() != null) {
ctx.close(); // todo disconnect with message
return;
}
switch (result.getResultType()) { switch (result.getResultType()) {
case SUCCESS: case SUCCESS:
break; break;
@@ -195,24 +203,18 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
return; return;
} }
fPlayer = result.getFloodgatePlayer(); player = result.getFloodgatePlayer();
BedrockData bedrockData = result.getBedrockData();
String[] data = result.getHandshakeData();
bungeeData = isBungeeData(); bungeeData = isBungeeData();
InetSocketAddress correctAddress = fPlayer.getProperty(PropertyKey.SOCKET_ADDRESS); setValue(packet, HANDSHAKE_HOST, handshakeData.getHostname());
if (bungeeData) { if (!bungeeData) {
setValue(packet, HANDSHAKE_HOST, data[0] + '\0' +
bedrockData.getIp() + '\0' +
fPlayer.getCorrectUniqueId() +
(data.length == 5 ? '\0' + data[4] : ""));
} else {
// Use a spoofedUUID for initUUID (just like Bungeecord) // Use a spoofedUUID for initUUID (just like Bungeecord)
setValue(networkManager, "spoofedUUID", fPlayer.getCorrectUniqueId()); setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId());
// Use the player his IP for stuff instead of Geyser his IP // Use the player his IP for stuff instead of Geyser his IP
setValue(networkManager, SOCKET_ADDRESS, correctAddress); InetSocketAddress address = player.getProperty(PropertyKey.SOCKET_ADDRESS);
setValue(networkManager, SOCKET_ADDRESS, address);
} }
} else if (isLogin) { } else if (isLogin) {
if (!bungeeData) { if (!bungeeData) {
@@ -227,7 +229,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
// set the player his GameProfile, we can't change the username without this // set the player his GameProfile, we can't change the username without this
Object gameProfile = GAME_PROFILE_CONSTRUCTOR.newInstance( Object gameProfile = GAME_PROFILE_CONSTRUCTOR.newInstance(
fPlayer.getCorrectUniqueId(), fPlayer.getCorrectUsername() player.getCorrectUniqueId(), player.getCorrectUsername()
); );
setValue(loginListener, LOGIN_PROFILE, gameProfile); setValue(loginListener, LOGIN_PROFILE, gameProfile);
@@ -251,7 +253,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
ctx.fireChannelRead(packet); ctx.fireChannelRead(packet);
} }
if (isHandshake && bungeeData || isLogin && !bungeeData || fPlayer == null) { if (isHandshake && bungeeData || isLogin && !bungeeData || player == null) {
// we're done, we'll just wait for the loginSuccessCall // we're done, we'll just wait for the loginSuccessCall
done = true; done = true;
} }

View File

@@ -32,7 +32,6 @@ import io.netty.util.AttributeKey;
import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.ProxyFloodgateApi;
import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.inject.InjectorAddon;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.config.ProxyFloodgateConfig;
import org.geysermc.floodgate.player.HandshakeHandler; import org.geysermc.floodgate.player.HandshakeHandler;
import org.geysermc.floodgate.util.Utils; import org.geysermc.floodgate.util.Utils;
@@ -51,10 +50,6 @@ public final class VelocityDataAddon implements InjectorAddon {
@Named("packetEncoder") @Named("packetEncoder")
private String packetEncoder; private String packetEncoder;
@Inject
@Named("playerAttribute")
private AttributeKey<FloodgatePlayer> playerAttribute;
@Inject @Inject
@Named("kickMessageAttribute") @Named("kickMessageAttribute")
private AttributeKey<String> kickMessageAttribute; private AttributeKey<String> kickMessageAttribute;
@@ -71,10 +66,7 @@ public final class VelocityDataAddon implements InjectorAddon {
// The handler is already added so we should add our handler before it // The handler is already added so we should add our handler before it
channel.pipeline().addBefore( channel.pipeline().addBefore(
packetHandler, "floodgate_data_handler", packetHandler, "floodgate_data_handler",
new VelocityProxyDataHandler( new VelocityProxyDataHandler(config, handshakeHandler, kickMessageAttribute, logger)
config, api, handshakeHandler, playerAttribute,
kickMessageAttribute, logger
)
); );
} }

View File

@@ -29,6 +29,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue; import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue;
import static org.geysermc.floodgate.util.ReflectionUtils.getField; import static org.geysermc.floodgate.util.ReflectionUtils.getField;
import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass; import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass;
import static org.geysermc.floodgate.util.ReflectionUtils.setValue;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@@ -36,9 +37,10 @@ import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.config.ProxyFloodgateConfig;
import org.geysermc.floodgate.player.HandshakeHandler; import org.geysermc.floodgate.player.HandshakeHandler;
import org.geysermc.floodgate.player.HandshakeHandler.HandshakeResult; import org.geysermc.floodgate.player.HandshakeHandler.HandshakeResult;
@@ -48,6 +50,7 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
private static final Field HANDSHAKE; private static final Field HANDSHAKE;
private static final Class<?> HANDSHAKE_PACKET; private static final Class<?> HANDSHAKE_PACKET;
private static final Field HANDSHAKE_SERVER_ADDRESS; private static final Field HANDSHAKE_SERVER_ADDRESS;
private static final Field REMOTE_ADDRESS;
static { static {
Class<?> iic = getPrefixedClass("connection.client.InitialInboundConnection"); Class<?> iic = getPrefixedClass("connection.client.InitialInboundConnection");
@@ -61,12 +64,13 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
HANDSHAKE_SERVER_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress"); HANDSHAKE_SERVER_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");
checkNotNull(HANDSHAKE_SERVER_ADDRESS, "Address in the Handshake packet cannot be null"); checkNotNull(HANDSHAKE_SERVER_ADDRESS, "Address in the Handshake packet cannot be null");
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress");
} }
private final ProxyFloodgateConfig config; private final ProxyFloodgateConfig config;
private final ProxyFloodgateApi api;
private final HandshakeHandler handshakeHandler; private final HandshakeHandler handshakeHandler;
private final AttributeKey<FloodgatePlayer> playerAttribute;
private final AttributeKey<String> kickMessageAttribute; private final AttributeKey<String> kickMessageAttribute;
private final FloodgateLogger logger; private final FloodgateLogger logger;
private boolean done; private boolean done;
@@ -90,6 +94,13 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
String address = getCastedValue(packet, HANDSHAKE_SERVER_ADDRESS); String address = getCastedValue(packet, HANDSHAKE_SERVER_ADDRESS);
HandshakeResult result = handshakeHandler.handle(ctx.channel(), address); HandshakeResult result = handshakeHandler.handle(ctx.channel(), address);
HandshakeData handshakeData = result.getHandshakeData();
if (handshakeData.getDisconnectReason() != null) {
ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason());
return;
}
switch (result.getResultType()) { switch (result.getResultType()) {
case SUCCESS: case SUCCESS:
break; break;
@@ -99,12 +110,17 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
ctx.channel().attr(kickMessageAttribute) ctx.channel().attr(kickMessageAttribute)
.set(config.getDisconnect().getInvalidArgumentsLength()); .set(config.getDisconnect().getInvalidArgumentsLength());
return; return;
default: default: // only continue when SUCCESS
return; return;
} }
FloodgatePlayer player = result.getFloodgatePlayer(); FloodgatePlayer player = result.getFloodgatePlayer();
setValue(packet, HANDSHAKE_SERVER_ADDRESS, handshakeData.getHostname());
Object connection = ctx.pipeline().get("handler");
setValue(connection, REMOTE_ADDRESS, player.getProperty(PropertyKey.SOCKET_ADDRESS));
logger.info("Floodgate player who is logged in as {} {} joined", logger.info("Floodgate player who is logged in as {} {} joined",
player.getCorrectUsername(), player.getCorrectUniqueId()); player.getCorrectUsername(), player.getCorrectUniqueId());
} }

View File

@@ -87,4 +87,9 @@ public final class Slf4jFloodgateLogger implements FloodgateLogger {
Configurator.setLevel(logger.getName(), Level.INFO); Configurator.setLevel(logger.getName(), Level.INFO);
} }
} }
@Override
public boolean isDebug() {
return logger.isDebugEnabled();
}
} }