/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* 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.
*/
package org.lanternpowered.server.network.vanilla.message.handler.status;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.lanternpowered.server.LanternServer;
import org.lanternpowered.server.game.Lantern;
import org.lanternpowered.server.game.version.LanternMinecraftVersion;
import org.lanternpowered.server.network.NetworkContext;
import org.lanternpowered.server.network.NetworkSession;
import org.lanternpowered.server.network.message.handler.Handler;
import org.lanternpowered.server.network.vanilla.message.type.status.MessageStatusInRequest;
import org.lanternpowered.server.network.vanilla.message.type.status.MessageStatusOutResponse;
import org.lanternpowered.server.network.status.LanternFavicon;
import org.lanternpowered.server.network.status.LanternStatusClient;
import org.lanternpowered.server.network.status.LanternStatusHelper;
import org.lanternpowered.server.network.status.LanternStatusResponse;
import org.lanternpowered.server.text.gson.LanternJsonTextSerializer;
import org.spongepowered.api.MinecraftVersion;
import org.spongepowered.api.event.SpongeEventFactory;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.event.server.ClientPingServerEvent;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.serializer.TextSerializers;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Optional;
public final class HandlerStatusRequest implements Handler<MessageStatusInRequest> {
@Override
public void handle(NetworkContext context, MessageStatusInRequest message) {
final NetworkSession session = context.getSession();
final LanternServer server = session.getServer();
final Gson gson = new Gson();
final LanternMinecraftVersion serverVersion = Lantern.getGame().getPlatform().getMinecraftVersion();
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(serverVersion, server.getFavicon(), description, players);
final ClientPingServerEvent event = SpongeEventFactory.createClientPingServerEvent(Cause.source(client).build(), client, response);
// Cancelled, we are done here
if (event.isCancelled()) {
context.getChannel().close();
return;
}
final JsonObject rootObject = new JsonObject();
final JsonObject versionObject = new JsonObject();
versionObject.addProperty("name", Lantern.getGame().getPlatform().getMinecraftVersion().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)));
}
}