mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2026-01-04 15:31:48 +00:00
Stopped using disconnect and (Bungee's) handshake event
Instead we're listening for the actual channel to close.
This commit is contained in:
@@ -47,6 +47,16 @@ public interface InjectorAddon {
|
||||
*/
|
||||
void onLoginDone(Channel channel);
|
||||
|
||||
/**
|
||||
* Called when the channel has been closed. Note that this method will be called for every
|
||||
* closed connection (if it is injected), so it'll also run this method for closed connections
|
||||
* between a server and the proxy (when Floodgate is running on a proxy).
|
||||
*
|
||||
* @param channel the channel that the injecor injected
|
||||
*/
|
||||
default void onChannelClosed(Channel channel) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when Floodgate is removing the injection from the server. The addon should remove his
|
||||
* traces otherwise it is likely that an error will popup after the server is injected again.
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
public class BungeeDataAddon implements InjectorAddon {
|
||||
@Inject private FloodgateHandshakeHandler handshakeHandler;
|
||||
@Inject private ProxyFloodgateConfig config;
|
||||
@Inject private ProxyFloodgateApi api;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("packetHandler")
|
||||
private String packetHandler;
|
||||
|
||||
@Inject
|
||||
@Named("packetEncoder")
|
||||
private String packetEncoder;
|
||||
|
||||
@Inject
|
||||
@Named("kickMessageAttribute")
|
||||
private AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
private AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean toServer) {
|
||||
if (toServer) {
|
||||
channel.pipeline().addAfter(
|
||||
packetEncoder, "floodgate_data_handler",
|
||||
new BungeeServerDataHandler(config, api, playerAttribute)
|
||||
);
|
||||
return;
|
||||
}
|
||||
channel.pipeline().addBefore(
|
||||
packetHandler, "floodgate_data_handler",
|
||||
new BungeeProxyDataHandler(config, handshakeHandler, kickMessageAttribute)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginDone(Channel channel) {
|
||||
onRemoveInject(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
FloodgatePlayer player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.removePlayer(player)) {
|
||||
api.removeEncryptedData(player.getCorrectUniqueId());
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getCorrectUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
Utils.removeHandler(channel.pipeline(), "floodgate_data_handler");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInject() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.ResultType;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter {
|
||||
private static final Field HANDLER;
|
||||
private static final Field CHANNEL_WRAPPER;
|
||||
|
||||
static {
|
||||
HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler");
|
||||
checkNotNull(HANDLER, "handler field cannot be null");
|
||||
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
}
|
||||
|
||||
private final ProxyFloodgateConfig config;
|
||||
private final FloodgateHandshakeHandler handler;
|
||||
private final AttributeKey<String> kickMessageAttribute;
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
ReferenceCountUtil.retain(msg);
|
||||
if (done || !(msg instanceof PacketWrapper)) {
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
DefinedPacket packet = ((PacketWrapper) msg).packet;
|
||||
|
||||
// we're only interested in the Handshake packet
|
||||
if (packet instanceof Handshake) {
|
||||
handleHandshake(ctx, (Handshake) packet);
|
||||
done = true;
|
||||
}
|
||||
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
|
||||
private void handleHandshake(ChannelHandlerContext ctx, Handshake packet) {
|
||||
String data = packet.getHost();
|
||||
|
||||
HandshakeResult result = handler.handle(ctx.channel(), data);
|
||||
HandshakeData handshakeData = result.getHandshakeData();
|
||||
|
||||
// we'll change the IP address from the proxy to the IP of the Bedrock client very early on
|
||||
// so that almost every plugin will use the IP of the Bedrock client
|
||||
if (result.getFloodgatePlayer() != null) {
|
||||
|
||||
HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class);
|
||||
// InitialHandler extends PacketHandler and implements PendingConnection
|
||||
InitialHandler connection = ReflectionUtils.getCastedValue(handlerBoss, HANDLER);
|
||||
|
||||
ChannelWrapper channelWrapper =
|
||||
ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
|
||||
|
||||
InetSocketAddress address =
|
||||
result.getFloodgatePlayer().getProperty(PropertyKey.SOCKET_ADDRESS);
|
||||
|
||||
channelWrapper.setRemoteAddress(address);
|
||||
}
|
||||
|
||||
if (handshakeData.getDisconnectReason() != null) {
|
||||
ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getResultType() == ResultType.EXCEPTION) {
|
||||
ctx.channel().attr(kickMessageAttribute).set(
|
||||
config.getDisconnect().getInvalidKey());
|
||||
}
|
||||
|
||||
if (result.getResultType() == ResultType.INVALID_DATA_LENGTH) {
|
||||
ctx.channel().attr(kickMessageAttribute)
|
||||
.set(config.getDisconnect().getInvalidArgumentsLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.ServerConnector;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeServerDataHandler extends MessageToMessageEncoder<Object> {
|
||||
private static final Field HANDLER;
|
||||
private static final Field USER_CONNECTION;
|
||||
private static final Field CHANNEL_WRAPPER;
|
||||
|
||||
static {
|
||||
HANDLER = ReflectionUtils.getField(HandlerBoss.class, "handler");
|
||||
checkNotNull(HANDLER, "handler field cannot be null");
|
||||
|
||||
USER_CONNECTION = ReflectionUtils.getField(ServerConnector.class, "user");
|
||||
checkNotNull(USER_CONNECTION, "user field cannot be null");
|
||||
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(UserConnection.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
}
|
||||
|
||||
private final ProxyFloodgateConfig config;
|
||||
private final ProxyFloodgateApi api;
|
||||
private final AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, List<Object> out) {
|
||||
ReferenceCountUtil.retain(packet);
|
||||
if (done) {
|
||||
out.add(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// passes the information through to the connecting server if enabled
|
||||
if (!(packet instanceof Handshake) || !config.isSendFloodgateData()) {
|
||||
done = true;
|
||||
out.add(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the Proxy <-> Player channel from the Proxy <-> Server channel
|
||||
HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class);
|
||||
ServerConnector connector = ReflectionUtils.getCastedValue(handlerBoss, HANDLER);
|
||||
UserConnection connection = ReflectionUtils.getCastedValue(connector, USER_CONNECTION);
|
||||
ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
|
||||
|
||||
FloodgatePlayer player = wrapper.getHandle().attr(playerAttribute).get();
|
||||
|
||||
if (player != null) {
|
||||
String encryptedData = api.getEncryptedData(player.getCorrectUniqueId());
|
||||
|
||||
Handshake handshake = (Handshake) packet;
|
||||
String address = handshake.getHost();
|
||||
|
||||
// our data goes before all the other data
|
||||
int addressFinished = address.indexOf('\0');
|
||||
String originalAddress = address.substring(0, addressFinished);
|
||||
String remaining = address.substring(addressFinished);
|
||||
|
||||
handshake.setHost(originalAddress + '\0' + encryptedData + remaining);
|
||||
// Bungeecord will add his data after our data
|
||||
}
|
||||
|
||||
done = true;
|
||||
out.add(packet);
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* 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.handler;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.geysermc.floodgate.player.FloodgateHandshakeHandler.ResultType;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.netty.channel.Channel;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
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.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
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.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
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;
|
||||
|
||||
public final class BungeeDataHandler {
|
||||
private static final Field EXTRA_HANDSHAKE_DATA;
|
||||
private static final Field PLAYER_NAME;
|
||||
private static final Field PLAYER_CHANNEL_WRAPPER;
|
||||
private static final Field PLAYER_CHANNEL;
|
||||
private static final Field PLAYER_REMOTE_ADDRESS;
|
||||
private static final Field CACHED_HANDSHAKE_PACKET;
|
||||
|
||||
static {
|
||||
Class<?> initialHandler = ReflectionUtils.getPrefixedClass("connection.InitialHandler");
|
||||
EXTRA_HANDSHAKE_DATA = ReflectionUtils.getField(initialHandler, "extraDataInHandshake");
|
||||
checkNotNull(EXTRA_HANDSHAKE_DATA, "extraDataInHandshake field cannot be null");
|
||||
|
||||
PLAYER_NAME = ReflectionUtils.getField(initialHandler, "name");
|
||||
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||
|
||||
Class<?> channelWrapper = ReflectionUtils.getPrefixedClass("netty.ChannelWrapper");
|
||||
PLAYER_CHANNEL_WRAPPER = ReflectionUtils.getFieldOfType(initialHandler, channelWrapper);
|
||||
checkNotNull(PLAYER_CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
|
||||
PLAYER_CHANNEL = ReflectionUtils.getFieldOfType(channelWrapper, Channel.class);
|
||||
checkNotNull(PLAYER_CHANNEL, "Channel field cannot be null");
|
||||
|
||||
PLAYER_REMOTE_ADDRESS = ReflectionUtils.getFieldOfType(channelWrapper, SocketAddress.class);
|
||||
checkNotNull(PLAYER_REMOTE_ADDRESS, "Remote address field cannot be null");
|
||||
|
||||
Class<?> handshakePacket = ReflectionUtils.getPrefixedClass("protocol.packet.Handshake");
|
||||
CACHED_HANDSHAKE_PACKET = ReflectionUtils.getFieldOfType(initialHandler, handshakePacket);
|
||||
checkNotNull(CACHED_HANDSHAKE_PACKET, "Cached handshake packet field cannot be null");
|
||||
}
|
||||
|
||||
@Inject private Plugin plugin;
|
||||
@Inject private ProxyFloodgateConfig config;
|
||||
@Inject private ProxyFloodgateApi api;
|
||||
@Inject private FloodgateHandshakeHandler handler;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
public void handlePreLogin(PreLoginEvent event) {
|
||||
event.registerIntent(plugin);
|
||||
plugin.getProxy().getScheduler().runAsync(plugin, () -> {
|
||||
PendingConnection connection = event.getConnection();
|
||||
|
||||
String extraData = ReflectionUtils.getCastedValue(connection, EXTRA_HANDSHAKE_DATA);
|
||||
|
||||
Object channelWrapper = ReflectionUtils.getValue(connection, PLAYER_CHANNEL_WRAPPER);
|
||||
Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL);
|
||||
|
||||
HandshakeResult result = handler.handle(channel, extraData);
|
||||
HandshakeData handshakeData = result.getHandshakeData();
|
||||
|
||||
if (handshakeData.getDisconnectReason() != null) {
|
||||
//noinspection ConstantConditions
|
||||
channel.close(); // todo disconnect with message
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getResultType() == ResultType.EXCEPTION) {
|
||||
event.setCancelReason(config.getDisconnect().getInvalidKey());
|
||||
event.completeIntent(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.getResultType() == ResultType.INVALID_DATA_LENGTH) {
|
||||
event.setCancelReason(TextComponent.fromLegacyText(String.format(
|
||||
config.getDisconnect().getInvalidArgumentsLength(),
|
||||
BedrockData.EXPECTED_LENGTH, result.getBedrockData().getDataLength()
|
||||
)));
|
||||
event.completeIntent(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
// only continue when SUCCESS
|
||||
if (result.getResultType() != ResultType.SUCCESS) {
|
||||
event.completeIntent(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
FloodgatePlayer player = result.getFloodgatePlayer();
|
||||
|
||||
connection.setOnlineMode(false);
|
||||
connection.setUniqueId(player.getCorrectUniqueId());
|
||||
|
||||
ReflectionUtils.setValue(connection, PLAYER_NAME, player.getCorrectUsername());
|
||||
|
||||
SocketAddress remoteAddress =
|
||||
ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS);
|
||||
|
||||
if (!(remoteAddress instanceof InetSocketAddress)) {
|
||||
logger.info("Player {} doesn't use an InetSocketAddress, it uses {}. " +
|
||||
"Ignoring the player, I guess.",
|
||||
player.getUsername(), remoteAddress.getClass().getSimpleName()
|
||||
);
|
||||
event.setCancelled(true);
|
||||
event.setCancelReason(
|
||||
new TextComponent("remoteAddress is not an InetSocketAddress!"));
|
||||
event.completeIntent(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
InetSocketAddress correctAddress = player.getProperty(PropertyKey.SOCKET_ADDRESS);
|
||||
ReflectionUtils.setValue(channelWrapper, PLAYER_REMOTE_ADDRESS, correctAddress);
|
||||
|
||||
event.completeIntent(plugin);
|
||||
});
|
||||
}
|
||||
|
||||
public void handleServerConnect(ProxiedPlayer player) {
|
||||
// Passes the information through to the connecting server if enabled
|
||||
if (config.isSendFloodgateData() && api.isFloodgatePlayer(player.getUniqueId())) {
|
||||
Handshake handshake = ReflectionUtils.getCastedValue(
|
||||
player.getPendingConnection(), CACHED_HANDSHAKE_PACKET
|
||||
);
|
||||
|
||||
// Ensures that only the hostname remains,
|
||||
// this way it can't mess up the Floodgate data format
|
||||
String initialHostname = handshake.getHost().split("\0")[0];
|
||||
handshake.setHost(initialHostname + '\0' + api.getEncryptedData(player.getUniqueId()));
|
||||
|
||||
// Bungeecord will add his data after our data
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,5 +96,9 @@ public final class BungeeInjector extends CommonPlatformInjector {
|
||||
public void injectClient(Channel channel, boolean clientToProxy) {
|
||||
injectAddonsCall(channel, !clientToProxy);
|
||||
addInjectedClient(channel);
|
||||
channel.closeFuture().addListener(listener -> {
|
||||
channelClosedCall(channel);
|
||||
removeInjectedClient(channel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,32 +25,50 @@
|
||||
|
||||
package org.geysermc.floodgate.listener;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.name.Named;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.api.player.PropertyKey;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.handler.BungeeDataHandler;
|
||||
import org.geysermc.floodgate.player.FloodgatePlayerImpl;
|
||||
import org.geysermc.floodgate.pluginmessage.BungeePluginMessageHandler;
|
||||
import org.geysermc.floodgate.skin.SkinHandler;
|
||||
import org.geysermc.floodgate.util.BungeeCommandUtil;
|
||||
import org.geysermc.floodgate.util.LanguageManager;
|
||||
import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public final class BungeeListener implements Listener {
|
||||
private BungeeDataHandler dataHandler;
|
||||
private static final Field CHANNEL_WRAPPER;
|
||||
private static final Field PLAYER_NAME;
|
||||
|
||||
static {
|
||||
CHANNEL_WRAPPER =
|
||||
ReflectionUtils.getFieldOfType(InitialHandler.class, ChannelWrapper.class);
|
||||
checkNotNull(CHANNEL_WRAPPER, "ChannelWrapper field cannot be null");
|
||||
|
||||
PLAYER_NAME = ReflectionUtils.getField(InitialHandler.class, "name");
|
||||
checkNotNull(PLAYER_NAME, "Initial name field cannot be null");
|
||||
}
|
||||
|
||||
@Inject private ProxyFloodgateApi api;
|
||||
@Inject private LanguageManager languageManager;
|
||||
@Inject private FloodgateLogger logger;
|
||||
@@ -60,14 +78,12 @@ public final class BungeeListener implements Listener {
|
||||
@Inject private SkinHandler skinHandler;
|
||||
|
||||
@Inject
|
||||
public void init(Injector injector) {
|
||||
dataHandler = injector.getInstance(BungeeDataHandler.class);
|
||||
}
|
||||
@Named("playerAttribute")
|
||||
private AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onServerConnect(ServerConnectEvent event) {
|
||||
dataHandler.handleServerConnect(event.getPlayer());
|
||||
}
|
||||
@Inject
|
||||
@Named("kickMessageAttribute")
|
||||
private AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@EventHandler
|
||||
public void onServerConnected(ServerConnectedEvent event) {
|
||||
@@ -89,15 +105,31 @@ public final class BungeeListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPreLogin(PreLoginEvent event) {
|
||||
dataHandler.handlePreLogin(event);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPreLoginMonitor(PreLoginEvent event) {
|
||||
// well, no reason to check if the player will be kicked anyway
|
||||
if (event.isCancelled()) {
|
||||
api.removePlayer(event.getConnection().getUniqueId(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
PendingConnection connection = event.getConnection();
|
||||
|
||||
ChannelWrapper wrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
|
||||
Channel channel = wrapper.getHandle();
|
||||
|
||||
// check if the player has to be kicked
|
||||
String kickReason = channel.attr(kickMessageAttribute).get();
|
||||
if (kickReason != null) {
|
||||
event.setCancelled(true);
|
||||
event.setCancelReason(kickReason);
|
||||
return;
|
||||
}
|
||||
|
||||
FloodgatePlayer player = channel.attr(playerAttribute).get();
|
||||
if (player != null) {
|
||||
connection.setOnlineMode(false);
|
||||
connection.setUniqueId(player.getCorrectUniqueId());
|
||||
ReflectionUtils.setValue(connection, PLAYER_NAME, player.getCorrectUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,22 +149,8 @@ public final class BungeeListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onLoginMonitor(LoginEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
api.removePlayer(event.getConnection().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
|
||||
BungeeCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo
|
||||
|
||||
if (api.removePlayer(player.getUniqueId()) != null) {
|
||||
api.removeEncryptedData(player.getUniqueId());
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getName());
|
||||
}
|
||||
BungeeCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.inject.Singleton;
|
||||
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import org.geysermc.floodgate.addon.AddonManagerAddon;
|
||||
import org.geysermc.floodgate.addon.DebugAddon;
|
||||
import org.geysermc.floodgate.addon.data.BungeeDataAddon;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.register.AddonRegister;
|
||||
|
||||
@@ -45,6 +46,12 @@ public final class BungeeAddonModule extends AbstractModule {
|
||||
return new AddonManagerAddon();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public InjectorAddon dataAddon() {
|
||||
return new BungeeDataAddon();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public InjectorAddon debugAddon() {
|
||||
|
||||
@@ -75,10 +75,6 @@ public class BungeeUserAudience implements UserAudience, ForwardingAudience.Sing
|
||||
@Override
|
||||
public void sendMessage(@NonNull Identity source, @NonNull Component message,
|
||||
@NonNull MessageType type) {
|
||||
if (type != MessageType.CHAT) {
|
||||
throw new IllegalStateException("Cannot send any other messages then chat messages");
|
||||
}
|
||||
|
||||
this.source.sendMessage(GsonComponentSerializer.gson().serialize(message));
|
||||
}
|
||||
|
||||
|
||||
@@ -65,15 +65,23 @@ public class HandshakeDataImpl implements HandshakeData {
|
||||
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;
|
||||
String javaUsername = null;
|
||||
UUID javaUniqueId = null;
|
||||
|
||||
this.javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
|
||||
if (bedrockData != null) {
|
||||
|
||||
String prefix = config.getUsernamePrefix();
|
||||
int usernameLength = Math.min(bedrockData.getUsername().length(), 16 - prefix.length());
|
||||
javaUsername = prefix + bedrockData.getUsername().substring(0, usernameLength);
|
||||
if (config.isReplaceSpaces()) {
|
||||
javaUsername = javaUsername.replaceAll(" ", "_");
|
||||
}
|
||||
|
||||
javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
|
||||
}
|
||||
|
||||
this.javaUsername = javaUsername;
|
||||
this.javaUniqueId = javaUniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.geysermc.floodgate.util.Utils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleFloodgateApi implements FloodgateApi {
|
||||
private final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
public final Map<UUID, FloodgatePlayer> players = new HashMap<>();
|
||||
private final PluginMessageHandler pluginMessageHandler;
|
||||
|
||||
@Override
|
||||
@@ -146,7 +146,6 @@ public class SimpleFloodgateApi implements FloodgateApi {
|
||||
* instance directly.
|
||||
*/
|
||||
public boolean removePlayer(FloodgatePlayer player) {
|
||||
boolean removed = players.remove(player.getJavaUniqueId(), player);
|
||||
return removed && player.getLinkedPlayer() != null;
|
||||
return players.remove(player.getJavaUniqueId(), player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.command;
|
||||
|
||||
import cloud.commandframework.Command;
|
||||
import cloud.commandframework.CommandManager;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||
import org.geysermc.floodgate.player.UserAudience;
|
||||
import org.geysermc.floodgate.util.Constants;
|
||||
|
||||
public class TestCommand implements FloodgateCommand {
|
||||
@Override
|
||||
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
|
||||
return commandManager.commandBuilder("floodgate-test")
|
||||
.senderType(UserAudience.class)
|
||||
.handler(this::execute)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext<UserAudience> context) {
|
||||
int players = ((SimpleFloodgateApi) FloodgateApi.getInstance()).players.size();
|
||||
context.getSender().sendMessage(Component.text(players));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRegister(FloodgateConfig config) {
|
||||
return Constants.DEBUG_MODE;
|
||||
}
|
||||
}
|
||||
@@ -30,11 +30,15 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.inject.PlatformInjector;
|
||||
|
||||
public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
@Getter(AccessLevel.PROTECTED)
|
||||
private final Set<Channel> injectedClients = new HashSet<>();
|
||||
|
||||
private final Map<Class<?>, InjectorAddon> addons = new HashMap<>();
|
||||
|
||||
protected boolean addInjectedClient(Channel channel) {
|
||||
@@ -86,6 +90,20 @@ public abstract class CommonPlatformInjector implements PlatformInjector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to loop throguh all the addons and call {@link InjectorAddon#onChannelClosed(Channel)}
|
||||
* if {@link InjectorAddon#shouldInject()}
|
||||
*
|
||||
* @param channel the channel that was injected
|
||||
*/
|
||||
public void channelClosedCall(Channel channel) {
|
||||
for (InjectorAddon addon : addons.values()) {
|
||||
if (addon.shouldInject()) {
|
||||
addon.onChannelClosed(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to loop through all the addons and call {@link InjectorAddon#onRemoveInject(Channel)}
|
||||
* if {@link InjectorAddon#shouldInject()}.
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.multibindings.ProvidesIntoSet;
|
||||
import org.geysermc.floodgate.command.LinkAccountCommand;
|
||||
import org.geysermc.floodgate.command.TestCommand;
|
||||
import org.geysermc.floodgate.command.UnlinkAccountCommand;
|
||||
import org.geysermc.floodgate.platform.command.FloodgateCommand;
|
||||
import org.geysermc.floodgate.register.CommandRegister;
|
||||
@@ -50,4 +51,10 @@ public class CommandModule extends AbstractModule {
|
||||
public FloodgateCommand unlinkAccountCommand() {
|
||||
return new UnlinkAccountCommand();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@ProvidesIntoSet
|
||||
public FloodgateCommand testCommand() {
|
||||
return new TestCommand();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,13 @@ public class CommonModule extends AbstractModule {
|
||||
);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("kickMessageAttribute")
|
||||
public AttributeKey<String> kickMessageAttribute() {
|
||||
return AttributeKey.valueOf("floodgate-kick-message");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("playerAttribute")
|
||||
|
||||
@@ -28,4 +28,6 @@ package org.geysermc.floodgate.util;
|
||||
public final class Constants {
|
||||
public static final String DATABASE_NAME_FORMAT = "^floodgate-[a-zA-Z0-9_]{0,16}-database.jar$";
|
||||
public static final int LOGIN_SUCCESS_PACKET_ID = 2;
|
||||
|
||||
public static final boolean DEBUG_MODE = true;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@ package org.geysermc.floodgate.addon.data;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.name.Named;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.FloodgateConfig;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
@@ -37,12 +40,17 @@ import org.geysermc.floodgate.util.Utils;
|
||||
public final class SpigotDataAddon implements InjectorAddon {
|
||||
@Inject private FloodgateHandshakeHandler handshakeHandler;
|
||||
@Inject private FloodgateConfig config;
|
||||
@Inject private SimpleFloodgateApi api;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@Inject
|
||||
@Named("packetHandler")
|
||||
private String packetHandlerName;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
private AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean toServer) {
|
||||
channel.pipeline().addBefore(
|
||||
@@ -56,6 +64,14 @@ public final class SpigotDataAddon implements InjectorAddon {
|
||||
onRemoveInject(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
FloodgatePlayer player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.removePlayer(player)) {
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getCorrectUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
Utils.removeHandler(channel.pipeline(), "floodgate_data_handler");
|
||||
|
||||
@@ -34,7 +34,8 @@ import static org.geysermc.floodgate.util.ReflectionUtils.makeAccessible;
|
||||
import static org.geysermc.floodgate.util.ReflectionUtils.setValue;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -54,7 +55,7 @@ import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
import org.geysermc.floodgate.util.SpigotUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object> {
|
||||
public final class SpigotDataHandler extends ChannelInboundHandlerAdapter {
|
||||
private static final Field SOCKET_ADDRESS;
|
||||
private static final Class<?> HANDSHAKE_PACKET;
|
||||
private static final Field HANDSHAKE_HOST;
|
||||
@@ -160,7 +161,8 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object packet) throws Exception {
|
||||
public void channelRead(ChannelHandlerContext ctx, Object packet) throws Exception {
|
||||
ReferenceCountUtil.retain(packet);
|
||||
// we're done but we're not yet removed from the connection
|
||||
if (done) {
|
||||
ctx.fireChannelRead(packet);
|
||||
@@ -244,7 +246,9 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler<Object>
|
||||
} finally {
|
||||
// don't let the packet through if the packet is the login packet
|
||||
// because we want to skip the login cycle
|
||||
if (!isLogin) {
|
||||
if (isLogin) {
|
||||
ReferenceCountUtil.release(packet, 2);
|
||||
} else {
|
||||
ctx.fireChannelRead(packet);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.inject.CommonPlatformInjector;
|
||||
@@ -45,8 +43,6 @@ import org.geysermc.floodgate.util.ReflectionUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class SpigotInjector extends CommonPlatformInjector {
|
||||
private final Set<Channel> injectedClients = new HashSet<>();
|
||||
|
||||
private Object serverConnection;
|
||||
private String injectedFieldName;
|
||||
|
||||
@@ -115,7 +111,11 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
injectAddonsCall(channel, false);
|
||||
injectedClients.add(channel);
|
||||
addInjectedClient(channel);
|
||||
channel.closeFuture().addListener(listener -> {
|
||||
channelClosedCall(channel);
|
||||
removeInjectedClient(channel);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -129,10 +129,10 @@ public final class SpigotInjector extends CommonPlatformInjector {
|
||||
}
|
||||
|
||||
// remove injection from clients
|
||||
for (Channel channel : injectedClients) {
|
||||
for (Channel channel : getInjectedClients()) {
|
||||
removeAddonsCall(channel);
|
||||
}
|
||||
injectedClients.clear();
|
||||
getInjectedClients().clear();
|
||||
|
||||
// and change the list back to the original
|
||||
Object serverConnection = getServerConnection();
|
||||
|
||||
@@ -27,11 +27,9 @@ package org.geysermc.floodgate.listener;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.geysermc.floodgate.api.SimpleFloodgateApi;
|
||||
@@ -48,18 +46,10 @@ public final class SpigotListener implements Listener {
|
||||
@Inject private LanguageManager languageManager;
|
||||
@Inject private FloodgateLogger logger;
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||
api.removePlayer(event.getUniqueId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerLogin(PlayerLoginEvent event) {
|
||||
UUID uniqueId = event.getPlayer().getUniqueId();
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
api.removePlayer(uniqueId);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,12 +69,6 @@ public final class SpigotListener implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
SpigotCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo
|
||||
|
||||
if (api.removePlayer(player.getUniqueId()) != null) {
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getName());
|
||||
}
|
||||
SpigotCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +75,6 @@ public class SpigotUserAudience implements UserAudience, ForwardingAudience.Sing
|
||||
@Override
|
||||
public void sendMessage(@NonNull Identity source, @NonNull Component message,
|
||||
@NonNull MessageType type) {
|
||||
if (type != MessageType.CHAT) {
|
||||
throw new IllegalStateException("Cannot send any other messages then chat messages");
|
||||
}
|
||||
|
||||
this.source.sendMessage(GsonComponentSerializer.gson().serialize(message));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import io.netty.util.AttributeKey;
|
||||
import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.inject.InjectorAddon;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.util.Utils;
|
||||
@@ -54,6 +55,10 @@ public final class VelocityDataAddon implements InjectorAddon {
|
||||
@Named("kickMessageAttribute")
|
||||
private AttributeKey<String> kickMessageAttribute;
|
||||
|
||||
@Inject
|
||||
@Named("playerAttribute")
|
||||
private AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
|
||||
@Override
|
||||
public void onInject(Channel channel, boolean toServer) {
|
||||
if (toServer) {
|
||||
@@ -75,6 +80,15 @@ public final class VelocityDataAddon implements InjectorAddon {
|
||||
onRemoveInject(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel) {
|
||||
FloodgatePlayer player = channel.attr(playerAttribute).get();
|
||||
if (player != null && api.removePlayer(player)) {
|
||||
api.removeEncryptedData(player.getCorrectUniqueId());
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveInject(Channel channel) {
|
||||
Utils.removeHandler(channel.pipeline(), "floodgate_data_handler");
|
||||
|
||||
@@ -32,7 +32,7 @@ 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.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -46,7 +46,7 @@ import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<Object> {
|
||||
public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter {
|
||||
private static final Field HANDSHAKE;
|
||||
private static final Class<?> HANDSHAKE_PACKET;
|
||||
private static final Field HANDSHAKE_SERVER_ADDRESS;
|
||||
@@ -76,12 +76,12 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
ReferenceCountUtil.retain(msg);
|
||||
// we're only interested in the Handshake packet
|
||||
// we're only interested in the Handshake packet.
|
||||
// it should be the first packet but you never know
|
||||
if (done || !HANDSHAKE_PACKET.isInstance(msg)) {
|
||||
ctx.fireChannelRead(msg);
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler<
|
||||
case SUCCESS:
|
||||
break;
|
||||
case EXCEPTION:
|
||||
ctx.channel().attr(kickMessageAttribute).set(config.getDisconnect().getInvalidKey());
|
||||
ctx.channel().attr(kickMessageAttribute)
|
||||
.set(config.getDisconnect().getInvalidKey());
|
||||
return;
|
||||
case INVALID_DATA_LENGTH:
|
||||
ctx.channel().attr(kickMessageAttribute)
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.geysermc.floodgate.api.ProxyFloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.geysermc.floodgate.config.ProxyFloodgateConfig;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@RequiredArgsConstructor
|
||||
public final class VelocityServerDataHandler extends MessageToMessageEncoder<Object> {
|
||||
private static final Class<?> HANDSHAKE_PACKET;
|
||||
@@ -74,6 +75,7 @@ public final class VelocityServerDataHandler extends MessageToMessageEncoder<Obj
|
||||
|
||||
private final ProxyFloodgateConfig config;
|
||||
private final ProxyFloodgateApi api;
|
||||
//private final AttributeKey<FloodgatePlayer> playerAttribute;
|
||||
private boolean done;
|
||||
|
||||
@Override
|
||||
@@ -100,6 +102,14 @@ public final class VelocityServerDataHandler extends MessageToMessageEncoder<Obj
|
||||
//noinspection ConstantConditions
|
||||
FloodgatePlayer player = api.getPlayer(velocityPlayer.getUniqueId());
|
||||
|
||||
//todo use something similar to what's written below for a more direct approach
|
||||
|
||||
// get the Proxy <-> Player channel from the Proxy <-> Server channel
|
||||
//MinecraftConnection minecraftConnection = ctx.pipeline().get("handler");
|
||||
//((VelocityServerConnection) minecraftConnection.association).proxyPlayer.connection.channel
|
||||
|
||||
//FloodgatePlayer player = playerChannel.attr(playerAttribute).get();
|
||||
|
||||
// player is not a Floodgate player
|
||||
if (player == null) {
|
||||
out.add(packet);
|
||||
|
||||
@@ -100,6 +100,10 @@ public final class VelocityInjector extends CommonPlatformInjector {
|
||||
|
||||
injector.injectAddonsCall(channel, proxyToServer);
|
||||
injector.addInjectedClient(channel);
|
||||
channel.closeFuture().addListener(listener -> {
|
||||
injector.channelClosedCall(channel);
|
||||
injector.removeInjectedClient(channel);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
@@ -62,7 +61,6 @@ import org.geysermc.floodgate.util.VelocityCommandUtil;
|
||||
|
||||
public final class VelocityListener {
|
||||
private static final Field INITIAL_MINECRAFT_CONNECTION;
|
||||
private static final Field MINECRAFT_CONNECTION;
|
||||
private static final Field CHANNEL;
|
||||
|
||||
static {
|
||||
@@ -70,8 +68,6 @@ public final class VelocityListener {
|
||||
|
||||
Class<?> minecraftConnection = getPrefixedClass("connection.MinecraftConnection");
|
||||
INITIAL_MINECRAFT_CONNECTION = getFieldOfType(initialConnection, minecraftConnection);
|
||||
Class<?> connectedPlayer = getPrefixedClass("connection.client.ConnectedPlayer");
|
||||
MINECRAFT_CONNECTION = getFieldOfType(connectedPlayer, minecraftConnection);
|
||||
CHANNEL = getFieldOfType(minecraftConnection, Channel.class);
|
||||
}
|
||||
|
||||
@@ -167,21 +163,6 @@ public final class VelocityListener {
|
||||
|
||||
@Subscribe(order = PostOrder.LAST)
|
||||
public void onDisconnect(DisconnectEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
VelocityCommandUtil.AUDIENCE_CACHE.remove(player.getUniqueId()); //todo
|
||||
|
||||
try {
|
||||
Object minecraftConnection = getValue(player, MINECRAFT_CONNECTION);
|
||||
Channel channel = getCastedValue(minecraftConnection, CHANNEL);
|
||||
FloodgatePlayer fPlayer = channel.attr(playerAttribute).get();
|
||||
|
||||
if (fPlayer != null && api.removePlayer(fPlayer)) {
|
||||
api.removeEncryptedData(event.getPlayer().getUniqueId());
|
||||
logger.translatedInfo("floodgate.ingame.disconnect_name", player.getUsername());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
logger.error("Failed to remove the player", exception);
|
||||
}
|
||||
VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import com.google.inject.name.Named;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.event.EventManager;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.floodgate.VelocityPlugin;
|
||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||
@@ -148,11 +147,4 @@ public final class VelocityPlatformModule extends AbstractModule {
|
||||
public String implementationName() {
|
||||
return "Velocity";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("kickMessageAttribute")
|
||||
public AttributeKey<String> kickMessageAttribute() {
|
||||
return AttributeKey.valueOf("floodgate-kick-message");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user