use of org.lanternpowered.server.LanternServer in project LanternServer by LanternPowered.
the class QueryHandler method handleFullStats.
private void handleFullStats(ChannelHandlerContext ctx, DatagramPacket packet, int sessionId) {
final LanternGame game = this.queryServer.getGame();
final LanternServer server = game.getServer();
final Platform platform = game.getPlatform();
final PluginContainer api = platform.getContainer(Platform.Component.API);
final PluginContainer impl = platform.getContainer(Platform.Component.IMPLEMENTATION);
final PluginContainer mc = platform.getContainer(Platform.Component.GAME);
final StringBuilder plugins = new StringBuilder().append(impl.getName()).append(" ").append(impl.getVersion()).append(" on ").append(api.getName()).append(" ").append(api.getVersion());
if (this.showPlugins) {
final List<PluginContainer> containers = new ArrayList<>(game.getPluginManager().getPlugins());
containers.remove(api);
containers.remove(impl);
containers.remove(mc);
char delim = ':';
for (PluginContainer plugin : containers) {
plugins.append(delim).append(' ').append(plugin.getName());
delim = ';';
}
}
final List<String> playerNames = server.getOnlinePlayers().stream().map(CommandSource::getName).collect(Collectors.toList());
final Cause cause = Cause.of(EventContext.empty(), new SimpleRemoteConnection((InetSocketAddress) ctx.channel().remoteAddress(), null));
final QueryServerEvent.Full event = SpongeEventFactory.createQueryServerEventFull(cause, (InetSocketAddress) ctx.channel().localAddress(), new HashMap<>(), "MINECRAFT", "SMP", getWorldName(), server.getMotd().toPlain(), playerNames, plugins.toString(), mc.getVersion().orElse("unknown"), server.getMaxPlayers(), Integer.MAX_VALUE, playerNames.size(), 0);
final InetSocketAddress address = event.getAddress();
final Map<String, Object> data = new LinkedHashMap<>();
data.put("hostname", event.getMotd());
data.put("gametype", event.getGameType());
data.put("game_id", event.getGameId());
data.put("version", event.getVersion());
data.put("plugins", event.getPlugins());
data.put("map", event.getMap());
data.put("numplayers", event.getPlayerCount());
data.put("maxplayers", event.getMaxPlayerCount());
data.put("hostport", address.getPort());
data.put("hostip", address.getHostString());
event.getCustomValuesMap().entrySet().stream().filter(entry -> !data.containsKey(entry.getKey())).forEach(entry -> data.put(entry.getKey(), entry.getValue()));
final ByteBuf buf = ctx.alloc().buffer();
buf.writeByte(ACTION_STATS);
buf.writeInt(sessionId);
// constant: splitnum\x00\x80\x00
buf.writeBytes(new byte[] { 0x73, 0x70, 0x6C, 0x69, 0x74, 0x6E, 0x75, 0x6D, 0x00, (byte) 0x80, 0x00 });
for (Entry<String, Object> e : data.entrySet()) {
writeString(buf, e.getKey());
writeString(buf, String.valueOf(e.getValue()));
}
buf.writeByte(0);
// constant: \x01player_\x00\x00
buf.writeBytes(new byte[] { 0x01, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x5F, 0x00, 0x00 });
for (Player player : game.getServer().getOnlinePlayers()) {
writeString(buf, player.getName());
}
buf.writeByte(0);
ctx.write(new DatagramPacket(buf, packet.sender()));
}
use of org.lanternpowered.server.LanternServer 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)));
}
use of org.lanternpowered.server.LanternServer in project LanternServer by LanternPowered.
the class CommandListPlayers method completeSpec.
@Override
public void completeSpec(PluginContainer pluginContainer, CommandSpec.Builder specBuilder) {
specBuilder.arguments(GenericArguments.optional(GenericArguments.string(Text.of("uuids")))).executor((src, args) -> {
final boolean includeUUIDs = "uuids".equalsIgnoreCase(args.<String>getOne("uuids").orElse(null));
final LanternServer server = Lantern.getServer();
src.sendMessage(t("commands.players.list", server.getRawOnlinePlayers().size(), server.getMaxPlayers()));
src.sendMessage(Text.of(Joiner.on(", ").join(server.getRawOnlinePlayers().stream().map(player -> {
String name = player.getName();
if (includeUUIDs) {
name = String.format("%s (%s)", name, player.getUniqueId().toString());
}
return name;
}).collect(Collectors.toList()))));
return CommandResult.builder().queryResult(server.getRawOnlinePlayers().size()).build();
});
}
use of org.lanternpowered.server.LanternServer 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);
}
}
}
use of org.lanternpowered.server.LanternServer in project LanternServer by LanternPowered.
the class QueryHandler method handleBasicStats.
private void handleBasicStats(ChannelHandlerContext ctx, DatagramPacket packet, int sessionId) {
LanternServer server = this.queryServer.getGame().getServer();
// TODO: Find out how to support the size and max size properties
final Cause cause = Cause.of(EventContext.empty(), new SimpleRemoteConnection((InetSocketAddress) ctx.channel().remoteAddress(), null));
final QueryServerEvent.Basic event = SpongeEventFactory.createQueryServerEventBasic(cause, (InetSocketAddress) ctx.channel().localAddress(), "SMP", this.getWorldName(), server.getMotd().toPlain(), server.getMaxPlayers(), Integer.MAX_VALUE, server.getOnlinePlayers().size(), 0);
Sponge.getEventManager().post(event);
final InetSocketAddress address = event.getAddress();
final ByteBuf buf = ctx.alloc().buffer();
buf.writeByte(ACTION_STATS);
buf.writeInt(sessionId);
writeString(buf, event.getMotd());
writeString(buf, event.getGameType());
writeString(buf, event.getMap());
writeString(buf, String.valueOf(event.getPlayerCount()));
writeString(buf, String.valueOf(event.getMaxPlayerCount()));
buf.writeShortLE(address.getPort());
writeString(buf, address.getHostString());
ctx.write(new DatagramPacket(buf, packet.sender()));
}
Aggregations