Search in sources :

Example 1 with LanternStatusResponse

use of org.lanternpowered.server.network.status.LanternStatusResponse in project LanternServer by LanternPowered.

the class HandlerStatusRequest method handle.

@Override
public void handle(NetworkContext context, MessageStatusInRequest message) {
    final NetworkSession session = context.getSession();
    final LanternServer server = session.getServer();
    final Gson gson = new Gson();
    final Text description = server.getMotd();
    final InetSocketAddress address = session.getAddress();
    final InetSocketAddress virtualAddress = session.getVirtualHost();
    final int protocol = session.getProtocolVersion();
    final MinecraftVersion clientVersion = Lantern.getGame().getMinecraftVersionCache().getVersionOrUnknown(protocol, false);
    if (clientVersion == LanternMinecraftVersion.UNKNOWN) {
        Lantern.getLogger().debug("Client with unknown protocol version {} pinged the server.", protocol);
    }
    final LanternStatusClient client = new LanternStatusClient(address, clientVersion, virtualAddress);
    final ClientPingServerEvent.Response.Players players = LanternStatusHelper.createPlayers(server);
    final LanternStatusResponse response = new LanternStatusResponse(Lantern.getGame().getPlatform().getMinecraftVersion(), server.getFavicon(), description, players);
    final Cause cause = Cause.of(EventContext.empty(), new WrappedRemoteConnection(session));
    final ClientPingServerEvent event = SpongeEventFactory.createClientPingServerEvent(cause, client, response);
    Sponge.getEventManager().post(event);
    // Cancelled, we are done here
    if (event.isCancelled()) {
        context.getChannel().close();
        return;
    }
    final JsonObject rootObject = new JsonObject();
    final JsonObject versionObject = new JsonObject();
    checkState(response.getVersion() instanceof LanternMinecraftVersion);
    final LanternMinecraftVersion serverVersion = (LanternMinecraftVersion) response.getVersion();
    versionObject.addProperty("name", serverVersion.getName());
    versionObject.addProperty("protocol", serverVersion.getProtocol());
    if (response.getPlayers().isPresent()) {
        final JsonObject playersObject = new JsonObject();
        playersObject.addProperty("max", players.getMax());
        playersObject.addProperty("online", players.getOnline());
        List<GameProfile> profiles = players.getProfiles();
        if (!profiles.isEmpty()) {
            final JsonArray array = new JsonArray();
            for (GameProfile profile : profiles) {
                Optional<String> optName = profile.getName();
                if (!optName.isPresent()) {
                    continue;
                }
                final JsonObject profileObject = new JsonObject();
                profileObject.addProperty("name", optName.get());
                profileObject.addProperty("id", profile.getUniqueId().toString());
                array.add(profileObject);
            }
            playersObject.add("sample", array);
        }
        rootObject.add("players", playersObject);
    }
    rootObject.add("version", versionObject);
    rootObject.add("description", ((LanternJsonTextSerializer) TextSerializers.JSON).getGson().toJsonTree(response.getDescription()));
    response.getFavicon().ifPresent(icon -> rootObject.addProperty("favicon", ((LanternFavicon) icon).getEncoded()));
    final JsonObject fmlObject = new JsonObject();
    // Trick the client that the server is fml, we support fml channels anyway
    fmlObject.addProperty("type", "FML");
    // The client shouldn't know the plugins (mods) list
    fmlObject.add("modList", new JsonArray());
    // Add the fml info
    rootObject.add("modinfo", fmlObject);
    session.send(new MessageStatusOutResponse(gson.toJson(rootObject)));
}
Also used : NetworkSession(org.lanternpowered.server.network.NetworkSession) MessageStatusOutResponse(org.lanternpowered.server.network.vanilla.message.type.status.MessageStatusOutResponse) LanternMinecraftVersion(org.lanternpowered.server.game.version.LanternMinecraftVersion) InetSocketAddress(java.net.InetSocketAddress) Gson(com.google.gson.Gson) JsonObject(com.google.gson.JsonObject) Text(org.spongepowered.api.text.Text) LanternJsonTextSerializer(org.lanternpowered.server.text.gson.LanternJsonTextSerializer) LanternStatusResponse(org.lanternpowered.server.network.status.LanternStatusResponse) MessageStatusOutResponse(org.lanternpowered.server.network.vanilla.message.type.status.MessageStatusOutResponse) JsonArray(com.google.gson.JsonArray) LanternStatusClient(org.lanternpowered.server.network.status.LanternStatusClient) ClientPingServerEvent(org.spongepowered.api.event.server.ClientPingServerEvent) LanternServer(org.lanternpowered.server.LanternServer) GameProfile(org.spongepowered.api.profile.GameProfile) Cause(org.spongepowered.api.event.cause.Cause) LanternFavicon(org.lanternpowered.server.network.status.LanternFavicon) LanternStatusResponse(org.lanternpowered.server.network.status.LanternStatusResponse) LanternMinecraftVersion(org.lanternpowered.server.game.version.LanternMinecraftVersion) MinecraftVersion(org.spongepowered.api.MinecraftVersion) WrappedRemoteConnection(org.lanternpowered.server.network.WrappedRemoteConnection)

Example 2 with LanternStatusResponse

use of org.lanternpowered.server.network.status.LanternStatusResponse in project LanternServer by LanternPowered.

the class LegacyProtocolHandler method channelRead.

@Override
public void channelRead(ChannelHandlerContext ctx, Object object) throws Exception {
    final LanternServer server = this.session.getServer();
    final ByteBuf buf = (ByteBuf) object;
    buf.markReaderIndex();
    // Whether it was a valid legacy message
    boolean legacy = false;
    try {
        final int messageId = buf.readUnsignedByte();
        // they don't attempt to login
        if (messageId == 0x02) {
            // Protocol version
            int protocol = buf.readByte();
            int value = buf.readShort();
            // Check the length
            if (value < 0 || value > 16) {
                return;
            }
            // Username
            buf.readBytes(value << 1);
            value = buf.readShort();
            // Check the length
            if (value < 0 || value > 255) {
                return;
            }
            // Host address
            buf.readBytes(value << 1);
            // Port
            buf.readInt();
            if (buf.readableBytes() > 0) {
                return;
            }
            legacy = true;
            sendDisconnectMessage(ctx, LanternTexts.toPlain(t("multiplayer.disconnect.outdated_client", Lantern.getGame().getPlatform().getMinecraftVersion().getName())));
            final MinecraftVersion clientVersion = Lantern.getGame().getMinecraftVersionCache().getVersionOrUnknown(protocol, true);
            if (clientVersion == LanternMinecraftVersion.UNKNOWN_LEGACY) {
                Lantern.getLogger().debug("Client with unknown legacy protocol version {} attempted to join the server.", protocol);
            } else {
                Lantern.getLogger().debug("Client with legacy protocol version {} (mc-version {}) attempted to join the server.", protocol, clientVersion.getName());
            }
            return;
        }
        // Check for the ping message id.
        if (messageId != 0xfe) {
            return;
        }
        int readable = buf.readableBytes();
        boolean full = false;
        // The version used to ping the server
        int protocol = V1_3_2_PROTOCOL;
        // Versions 1.4 - 1.5.x + 1.6 - Can request full data.
        if (readable > 0) {
            // Is always 1
            if (buf.readUnsignedByte() != 1) {
                return;
            }
            full = true;
            protocol = V1_5_2_PROTOCOL;
        }
        // The virtual address that was used to join the server
        InetSocketAddress virtualAddress = null;
        // Version 1.6 - Used extra data.
        if (readable > 1) {
            if (buf.readUnsignedByte() != 0xfa) {
                return;
            }
            byte[] bytes = new byte[buf.readShort() << 1];
            buf.readBytes(bytes);
            if (!new String(bytes, StandardCharsets.UTF_16BE).equals("MC|PingHost")) {
                return;
            }
            // Not used
            buf.readShort();
            // The protocol version is present
            protocol = buf.readUnsignedByte();
            // There is extra host and port data
            if (protocol >= 73) {
                bytes = new byte[buf.readShort() << 1];
                buf.readBytes(bytes);
                final String host = new String(bytes, StandardCharsets.UTF_16BE);
                final int port = buf.readInt();
                virtualAddress = InetSocketAddress.createUnresolved(host, port);
            }
            readable = buf.readableBytes();
            if (readable > 0) {
                Lantern.getLogger().warn("Trailing bytes on a legacy ping message: {}b", readable);
            }
        }
        // The message was successfully decoded as a legacy one
        legacy = true;
        final boolean full1 = full;
        final int protocol1 = protocol;
        final InetSocketAddress virtualAddress1 = virtualAddress;
        // Call the event in the main thread
        Lantern.getScheduler().callSync(() -> {
            final MinecraftVersion clientVersion = Lantern.getGame().getMinecraftVersionCache().getVersionOrUnknown(protocol1, true);
            if (clientVersion == LanternMinecraftVersion.UNKNOWN) {
                Lantern.getLogger().debug("Client with unknown legacy protocol version {} pinged the server.", protocol1);
            }
            final MinecraftVersion serverVersion = Lantern.getGame().getPlatform().getMinecraftVersion();
            Text description = server.getMotd();
            final InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
            final LanternStatusClient client = new LanternStatusClient(address, clientVersion, virtualAddress1);
            final ClientPingServerEvent.Response.Players players = LanternStatusHelper.createPlayers(server);
            final LanternStatusResponse response = new LanternStatusResponse(serverVersion, server.getFavicon(), description, players);
            final SimpleRemoteConnection connection = new SimpleRemoteConnection(address, virtualAddress1);
            final Cause cause = Cause.of(EventContext.empty(), connection);
            final ClientPingServerEvent event = SpongeEventFactory.createClientPingServerEvent(cause, client, response);
            Sponge.getEventManager().post(event);
            // Cancelled, we are done here
            if (event.isCancelled()) {
                ctx.channel().close();
                return;
            }
            description = response.getDescription();
            int online = players.getOnline();
            final int max = players.getMax();
            // with ???
            if (!response.getPlayers().isPresent()) {
                online = -1;
            }
            final String data;
            if (full1) {
                final String description0 = getFirstLine(TextSerializers.LEGACY_FORMATTING_CODE.serialize(description));
                // 1. This value is always 1.
                // 2. The protocol version, just use a value out of range
                // of the available ones.
                // 3. The version/name string of the server.
                // 4. The motd of the server. In legacy format.
                // 5. The online players
                // 6. The maximum amount of players
                data = String.format("\u00A7%s\u0000%s\u0000%s\u0000%s\u0000%s\u0000%s", 1, 127, response.getVersion().getName(), description0, online, max);
            } else {
                final String description0 = getFirstLine(TextSerializers.PLAIN.serialize(description));
                // 1. The motd of the server. In legacy format.
                // 2. The online players
                // 3. The maximum amount of players
                data = String.format("%s\u00A7%s\u00A7%s", description0, online, max);
            }
            sendDisconnectMessage(ctx, data);
        });
    } catch (Exception ignore) {
    } finally {
        if (legacy) {
            buf.release();
        } else {
            buf.resetReaderIndex();
            ctx.channel().pipeline().remove(this);
            ctx.fireChannelRead(buf);
        }
    }
}
Also used : SimpleRemoteConnection(org.lanternpowered.server.network.SimpleRemoteConnection) InetSocketAddress(java.net.InetSocketAddress) Text(org.spongepowered.api.text.Text) ByteBuf(io.netty.buffer.ByteBuf) LanternStatusResponse(org.lanternpowered.server.network.status.LanternStatusResponse) LanternStatusClient(org.lanternpowered.server.network.status.LanternStatusClient) ClientPingServerEvent(org.spongepowered.api.event.server.ClientPingServerEvent) LanternServer(org.lanternpowered.server.LanternServer) Cause(org.spongepowered.api.event.cause.Cause) LanternStatusResponse(org.lanternpowered.server.network.status.LanternStatusResponse) LanternMinecraftVersion(org.lanternpowered.server.game.version.LanternMinecraftVersion) MinecraftVersion(org.spongepowered.api.MinecraftVersion)

Aggregations

InetSocketAddress (java.net.InetSocketAddress)2 LanternServer (org.lanternpowered.server.LanternServer)2 LanternMinecraftVersion (org.lanternpowered.server.game.version.LanternMinecraftVersion)2 LanternStatusClient (org.lanternpowered.server.network.status.LanternStatusClient)2 LanternStatusResponse (org.lanternpowered.server.network.status.LanternStatusResponse)2 MinecraftVersion (org.spongepowered.api.MinecraftVersion)2 Cause (org.spongepowered.api.event.cause.Cause)2 ClientPingServerEvent (org.spongepowered.api.event.server.ClientPingServerEvent)2 Text (org.spongepowered.api.text.Text)2 Gson (com.google.gson.Gson)1 JsonArray (com.google.gson.JsonArray)1 JsonObject (com.google.gson.JsonObject)1 ByteBuf (io.netty.buffer.ByteBuf)1 NetworkSession (org.lanternpowered.server.network.NetworkSession)1 SimpleRemoteConnection (org.lanternpowered.server.network.SimpleRemoteConnection)1 WrappedRemoteConnection (org.lanternpowered.server.network.WrappedRemoteConnection)1 LanternFavicon (org.lanternpowered.server.network.status.LanternFavicon)1 MessageStatusOutResponse (org.lanternpowered.server.network.vanilla.message.type.status.MessageStatusOutResponse)1 LanternJsonTextSerializer (org.lanternpowered.server.text.gson.LanternJsonTextSerializer)1 GameProfile (org.spongepowered.api.profile.GameProfile)1