diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 38dd6aa9..4cc32fc7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -7,7 +7,7 @@ setupPlatforms() setupPlatformDependency("bukkit", "bukkitJar") setupPlatformDependency("bungee", "bungeeJar") -setupPlatformDependency("velocity", "velocityJar") +setupPlatformDependency("velocity", "velocityJar", 17) setupPlatformDependency("folia", "foliaJar", 21) val main by sourceSets diff --git a/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts b/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts index 48db4d5a..d87f820e 100644 --- a/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/apollo.base-conventions.gradle.kts @@ -1,4 +1,6 @@ import com.diffplug.gradle.spotless.FormatExtension +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaToolchainService import java.nio.charset.StandardCharsets import java.nio.file.Files import java.util.regex.Pattern @@ -83,6 +85,11 @@ checkstyle { tasks { javadoc { + val javaToolchains = project.extensions.getByType(JavaToolchainService::class.java) + javadocTool.set(javaToolchains.javadocToolFor { + languageVersion.set(JavaLanguageVersion.of(21)) + }) + val minimalOptions: MinimalJavadocOptions = options options.encoding("UTF-8") diff --git a/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java b/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java index 117e952d..013a3abd 100644 --- a/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java +++ b/bukkit/src/main/java/com/lunarclient/apollo/ApolloBukkitPlatform.java @@ -25,9 +25,11 @@ import com.lunarclient.apollo.command.impl.ApolloCommand; import com.lunarclient.apollo.command.impl.LunarClientCommand; +import com.lunarclient.apollo.listener.ApolloMetadataListener; import com.lunarclient.apollo.listener.ApolloPlayerListener; import com.lunarclient.apollo.listener.ApolloWorldListener; import com.lunarclient.apollo.loader.PlatformPlugin; +import com.lunarclient.apollo.metadata.BukkitMetadataManager; import com.lunarclient.apollo.module.ApolloModuleManagerImpl; import com.lunarclient.apollo.module.autotexthotkey.AutoTextHotkeyModule; import com.lunarclient.apollo.module.beam.BeamModule; @@ -119,7 +121,9 @@ public void onEnable() { this.stats = new BukkitApolloStats(); ApolloManager.bootstrap(this); + ApolloManager.setMetadataManager(new BukkitMetadataManager()); + new ApolloMetadataListener(this.plugin); new ApolloPlayerListener(this.plugin); new ApolloWorldListener(this.plugin); diff --git a/bukkit/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java b/bukkit/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java new file mode 100644 index 00000000..f38bc58a --- /dev/null +++ b/bukkit/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java @@ -0,0 +1,100 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.listener; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import com.lunarclient.apollo.ApolloManager; +import com.lunarclient.apollo.metadata.BukkitMetadataManager; +import com.lunarclient.apollo.util.ByteBufUtil; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.plugin.messaging.PluginMessageListener; + +/** + * Handles Apollo metadata listeners. + * + * @since 1.1.9 + */ +public final class ApolloMetadataListener implements Listener { + + private final JavaPlugin plugin; + + /** + * Constructs the {@link ApolloMetadataListener}. + * + * @param plugin the plugin + * @since 1.1.9 + */ + public ApolloMetadataListener(JavaPlugin plugin) { + this.plugin = plugin; + + this.registerBrandListener(); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + private void onResourcePackStatus(PlayerResourcePackStatusEvent event) { + String status = event.getStatus().name(); + BukkitMetadataManager manager = (BukkitMetadataManager) ApolloManager.getMetadataManager(); + Map statuses = manager.getResourcePackStatuses(); + + statuses.put(status, statuses.getOrDefault(status, 0) + 1); + } + + private void registerBrandListener() { + Messenger messenger = this.plugin.getServer().getMessenger(); + + try { + PluginMessageListener listener = (channel, player, bytes) -> { + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) { + this.collectBrand(in.readUTF()); + } catch (IOException ignored) { + } + }; + + messenger.registerIncomingPluginChannel(this.plugin, "MC|Brand", listener); + } catch (IllegalArgumentException ignored) { + } + + messenger.registerIncomingPluginChannel(this.plugin, "minecraft:brand", (channel, player, bytes) -> { + ByteArrayDataInput in = ByteStreams.newDataInput(bytes); + this.collectBrand(ByteBufUtil.readString(in)); + }); + } + + private void collectBrand(String brand) { + BukkitMetadataManager manager = (BukkitMetadataManager) ApolloManager.getMetadataManager(); + manager.getClientBrands().add(brand); + } + +} diff --git a/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadata.java b/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadata.java new file mode 100644 index 00000000..e1bf4842 --- /dev/null +++ b/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadata.java @@ -0,0 +1,60 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.Map; +import java.util.Set; +import lombok.Builder; +import lombok.ToString; + +/** + * Represents the bukkit metadata implementation. + * + * @since 1.1.9 + */ +@ToString +@Builder(toBuilder = true) +public class BukkitMetadata extends PlatformMetadata { + + /** + * Tracks client brands sent by the players. + * + *

A {@link Set} of {@link String} client brands.

+ * + * @since 1.1.9 + */ + private final Set clientBrands; + + /** + * Tracks the total number of resource pack status events received. + * + *

Represents a {@link Map} of {@link String} status enum name as a key + * and {@link Integer} count of how many times that status has been reported.

+ * + * @since 1.1.9 + */ + private final Map resourcePackStatuses; + +} diff --git a/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadataManager.java b/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadataManager.java new file mode 100644 index 00000000..10f4a042 --- /dev/null +++ b/bukkit/src/main/java/com/lunarclient/apollo/metadata/BukkitMetadataManager.java @@ -0,0 +1,59 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import lombok.Getter; + +/** + * The Bukkit implementation of {@link ApolloMetadataManager}. + * + * @since 1.1.9 + */ +@Getter +public class BukkitMetadataManager implements ApolloMetadataManager { + + private final Set clientBrands = new HashSet<>(); + private final Map resourcePackStatuses = new HashMap<>(); + + @Override + public PlatformMetadata extract() { + return BukkitMetadata.builder() + .clientBrands(new HashSet<>(this.clientBrands)) + .resourcePackStatuses(new HashMap<>(this.resourcePackStatuses)) + .build(); + } + + @Override + public void clear() { + this.clientBrands.clear(); + this.resourcePackStatuses.clear(); + } + +} diff --git a/bukkit/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java b/bukkit/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java new file mode 100644 index 00000000..67a03b80 --- /dev/null +++ b/bukkit/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java @@ -0,0 +1,83 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.util; + +import com.google.common.io.ByteArrayDataInput; +import java.nio.charset.StandardCharsets; + +/** + * Represents a util for reading byte buffs. + * + * @since 1.1.9 + */ +public final class ByteBufUtil { + + /** + * Reads an int from the given input stream. + * + * @param in in the {@link ByteArrayDataInput} to read from + * @return the read int + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static int readVarInt(ByteArrayDataInput in) { + int i = 0; + int j = 0; + + while (true) { + byte b = in.readByte(); + i |= (b & 0x7F) << j++ * 7; + + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + + if ((b & 0x80) != 128) { + break; + } + } + + return i; + } + + /** + * Reads a UTF-8 encoded string from the given input stream. + * + * @param in the {@link ByteArrayDataInput} to read from + * @return the decoded string + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static String readString(ByteArrayDataInput in) { + int length = ByteBufUtil.readVarInt(in); + byte[] bytes = new byte[length]; + in.readFully(bytes); + + return new String(bytes, StandardCharsets.UTF_8); + } + + private ByteBufUtil() { + } + +} diff --git a/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java b/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java index 44fe00b0..dc2aa3e8 100644 --- a/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java +++ b/bungee/src/main/java/com/lunarclient/apollo/ApolloBungeePlatform.java @@ -25,8 +25,10 @@ import com.lunarclient.apollo.command.impl.ApolloCommand; import com.lunarclient.apollo.command.impl.LunarClientCommand; +import com.lunarclient.apollo.listener.ApolloMetadataListener; import com.lunarclient.apollo.listener.ApolloPlayerListener; import com.lunarclient.apollo.loader.PlatformPlugin; +import com.lunarclient.apollo.metadata.BungeeMetadataManager; import com.lunarclient.apollo.module.ApolloModuleManagerImpl; import com.lunarclient.apollo.module.autotexthotkey.AutoTextHotkeyModule; import com.lunarclient.apollo.module.beam.BeamModule; @@ -107,6 +109,7 @@ public void onEnable() { this.stats = new BungeeApolloStats(); ApolloManager.bootstrap(this); + ApolloManager.setMetadataManager(new BungeeMetadataManager()); ((ApolloModuleManagerImpl) Apollo.getModuleManager()) .addModule(AutoTextHotkeyModule.class) @@ -146,6 +149,7 @@ public void onEnable() { server.registerChannel(ApolloManager.PLUGIN_MESSAGE_CHANNEL); PluginManager pluginManager = server.getPluginManager(); + pluginManager.registerListener(this.plugin, new ApolloMetadataListener(this.plugin)); pluginManager.registerListener(this.plugin, new ApolloPlayerListener()); pluginManager.registerCommand(this.plugin, ApolloCommand.create()); pluginManager.registerCommand(this.plugin, LunarClientCommand.create()); diff --git a/bungee/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java b/bungee/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java new file mode 100644 index 00000000..2cda362d --- /dev/null +++ b/bungee/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java @@ -0,0 +1,158 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.listener; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import com.lunarclient.apollo.ApolloManager; +import com.lunarclient.apollo.metadata.BungeeMetadataManager; +import com.lunarclient.apollo.util.ByteBufUtil; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.event.PreLoginEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.event.EventHandler; + +/** + * Handles Apollo metadata listeners. + * + * @since 1.1.9 + */ +public final class ApolloMetadataListener implements Listener { + + /** + * Constructs the {@link ApolloMetadataListener}. + * + * @param plugin the plugin + * @since 1.1.9 + */ + public ApolloMetadataListener(Plugin plugin) { + ProxyServer server = plugin.getProxy(); + + server.registerChannel("MC|Brand"); + server.registerChannel("minecraft:brand"); + + server.registerChannel("FML|HS"); + server.registerChannel("fml:handshake"); + } + + /** + * Handles plugin messages for client brand and FML mod information. + * + * @param event the event + * @since 1.1.9 + */ + @EventHandler + public void onPluginMessage(PluginMessageEvent event) { + if (!(event.getSender() instanceof ProxiedPlayer)) { + return; + } + + String tag = event.getTag(); + byte[] data = event.getData(); + + switch (tag) { + case "MC|Brand": { + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(data))) { + this.collectBrand(in.readUTF()); + } catch (IOException ignored) { + } + + break; + } + case "minecraft:brand": { + ByteArrayDataInput in = ByteStreams.newDataInput(data); + this.collectBrand(ByteBufUtil.readString(in)); + break; + } + case "fml:handshake": + case "FML|HS": { + this.handleFml(data); + break; + } + } + } + + /** + * Captures the server IP and port the player used to connect. + * + * @param event the event + * @since 1.1.9 + */ + @EventHandler + public void onPreLogin(PreLoginEvent event) { + PendingConnection connection = event.getConnection(); + InetSocketAddress host = connection.getVirtualHost(); + + if (host == null) { + return; + } + + String hostString; + if (host.getAddress() != null) { + hostString = host.getAddress().getHostAddress(); + } else { + hostString = host.getHostName(); + } + + BungeeMetadataManager manager = (BungeeMetadataManager) ApolloManager.getMetadataManager(); + manager.getServerAddress().add(hostString + ":" + host.getPort()); + } + + private void collectBrand(String brand) { + BungeeMetadataManager manager = (BungeeMetadataManager) ApolloManager.getMetadataManager(); + manager.getClientBrands().add(brand); + } + + private void handleFml(byte[] data) { + ByteArrayDataInput in = ByteStreams.newDataInput(data); + + try { + byte discriminator = in.readByte(); + + if (discriminator != 2) { + return; + } + + int count = ByteBufUtil.readVarInt(in); + + for (int i = 0; i < count; i++) { + String modId = ByteBufUtil.readString(in); + String version = ByteBufUtil.readString(in); + + BungeeMetadataManager manager = (BungeeMetadataManager) ApolloManager.getMetadataManager(); + manager.getMods().put(modId, version); + } + } catch (Exception ignored) { + } + } + +} diff --git a/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadata.java b/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadata.java new file mode 100644 index 00000000..2b614b4c --- /dev/null +++ b/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadata.java @@ -0,0 +1,69 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.Map; +import java.util.Set; +import lombok.Builder; +import lombok.ToString; + +/** + * Represents the bungee metadata implementation. + * + * @since 1.1.9 + */ +@ToString +@Builder(toBuilder = true) +public class BungeeMetadata extends PlatformMetadata { + + /** + * Tracks client brands sent by the players. + * + *

A {@link Set} of {@link String} client brands.

+ * + * @since 1.1.9 + */ + private final Set clientBrands; + + /** + * Tracks forge mods sent with the forge handshake. + * + *

A {@link Map} of {@link String} mod id + * as a key and {@link String} version as value.

+ * + * @since 1.1.9 + */ + private final Map mods; + + /** + * Tracks the server IP and port the player used to connect. + * + *

A {@link Set} of {@link String} server addresses in the format: host:port

+ * + * @since 1.1.9 + */ + private final Set serverAddress; + +} diff --git a/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadataManager.java b/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadataManager.java new file mode 100644 index 00000000..1ea78f1b --- /dev/null +++ b/bungee/src/main/java/com/lunarclient/apollo/metadata/BungeeMetadataManager.java @@ -0,0 +1,62 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import lombok.Getter; + +/** + * The Bungee implementation of {@link ApolloMetadataManager}. + * + * @since 1.1.9 + */ +@Getter +public class BungeeMetadataManager implements ApolloMetadataManager { + + private final Set clientBrands = new HashSet<>(); + private final Map mods = new HashMap<>(); + private final Set serverAddress = new HashSet<>(); + + @Override + public PlatformMetadata extract() { + return BungeeMetadata.builder() + .clientBrands(new HashSet<>(this.clientBrands)) + .mods(new HashMap<>(this.mods)) + .serverAddress(new HashSet<>(this.serverAddress)) + .build(); + } + + @Override + public void clear() { + this.clientBrands.clear(); + this.mods.clear(); + this.serverAddress.clear(); + } + +} diff --git a/bungee/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java b/bungee/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java new file mode 100644 index 00000000..67a03b80 --- /dev/null +++ b/bungee/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java @@ -0,0 +1,83 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.util; + +import com.google.common.io.ByteArrayDataInput; +import java.nio.charset.StandardCharsets; + +/** + * Represents a util for reading byte buffs. + * + * @since 1.1.9 + */ +public final class ByteBufUtil { + + /** + * Reads an int from the given input stream. + * + * @param in in the {@link ByteArrayDataInput} to read from + * @return the read int + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static int readVarInt(ByteArrayDataInput in) { + int i = 0; + int j = 0; + + while (true) { + byte b = in.readByte(); + i |= (b & 0x7F) << j++ * 7; + + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + + if ((b & 0x80) != 128) { + break; + } + } + + return i; + } + + /** + * Reads a UTF-8 encoded string from the given input stream. + * + * @param in the {@link ByteArrayDataInput} to read from + * @return the decoded string + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static String readString(ByteArrayDataInput in) { + int length = ByteBufUtil.readVarInt(in); + byte[] bytes = new byte[length]; + in.readFully(bytes); + + return new String(bytes, StandardCharsets.UTF_8); + } + + private ByteBufUtil() { + } + +} diff --git a/common/src/main/java/com/lunarclient/apollo/ApolloManager.java b/common/src/main/java/com/lunarclient/apollo/ApolloManager.java index 4085d740..8c967e2b 100644 --- a/common/src/main/java/com/lunarclient/apollo/ApolloManager.java +++ b/common/src/main/java/com/lunarclient/apollo/ApolloManager.java @@ -34,6 +34,7 @@ import com.lunarclient.apollo.player.ApolloPlayerManagerImpl; import com.lunarclient.apollo.roundtrip.ApolloRoundtripManager; import com.lunarclient.apollo.stats.ApolloStatsManager; +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; import com.lunarclient.apollo.util.ConfigTarget; import com.lunarclient.apollo.version.ApolloVersionManager; import com.lunarclient.apollo.world.ApolloWorldManagerImpl; @@ -42,6 +43,7 @@ import java.util.LinkedList; import java.util.List; import lombok.Getter; +import lombok.Setter; /** * Provides the instances for {@link Apollo}. @@ -74,6 +76,7 @@ public final class ApolloManager { @Getter private static ApolloNetworkManager networkManager; @Getter private static ApolloVersionManager versionManager; @Getter private static ApolloStatsManager statsManager; + @Getter @Setter private static ApolloMetadataManager metadataManager; @Getter private static Path configPath; diff --git a/common/src/main/java/com/lunarclient/apollo/api/request/ServerHeartbeatRequest.java b/common/src/main/java/com/lunarclient/apollo/api/request/heartbeat/ServerHeartbeatRequest.java similarity index 91% rename from common/src/main/java/com/lunarclient/apollo/api/request/ServerHeartbeatRequest.java rename to common/src/main/java/com/lunarclient/apollo/api/request/heartbeat/ServerHeartbeatRequest.java index 8bf8007d..2a7e5322 100644 --- a/common/src/main/java/com/lunarclient/apollo/api/request/ServerHeartbeatRequest.java +++ b/common/src/main/java/com/lunarclient/apollo/api/request/heartbeat/ServerHeartbeatRequest.java @@ -21,12 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.lunarclient.apollo.api.request; +package com.lunarclient.apollo.api.request.heartbeat; import com.lunarclient.apollo.api.ApiRequest; import com.lunarclient.apollo.api.ApiRequestType; import com.lunarclient.apollo.api.ApiServiceType; import com.lunarclient.apollo.api.response.ServerHeartbeatResponse; +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; import lombok.Builder; import lombok.ToString; @@ -81,6 +82,13 @@ public final class ServerHeartbeatRequest implements ApiRequest HEARTBEAT_USER_METADATA = Option.builder() + .comment("Set to 'true' to send players user metadata to MCStats, otherwise 'false'.") + .node(CONFIG_PREFIX, "user-metadata").type(TypeToken.get(Boolean.class)) + .defaultValue(true).build(); + /** * Constructs the {@link ApolloStatsManager}. * @@ -95,7 +100,8 @@ public ApolloStatsManager() { ApolloStatsManager.HARDWARE_INFORMATION, ApolloStatsManager.SOFTWARE_INFORMATION, ApolloStatsManager.HEARTBEAT_PERFORMANCE, - ApolloStatsManager.HEARTBEAT_COUNTS + ApolloStatsManager.HEARTBEAT_COUNTS, + ApolloStatsManager.HEARTBEAT_USER_METADATA ); } diff --git a/common/src/main/java/com/lunarclient/apollo/stats/ApolloStatsThread.java b/common/src/main/java/com/lunarclient/apollo/stats/ApolloStatsThread.java index 08924c8c..404dcc63 100644 --- a/common/src/main/java/com/lunarclient/apollo/stats/ApolloStatsThread.java +++ b/common/src/main/java/com/lunarclient/apollo/stats/ApolloStatsThread.java @@ -27,8 +27,9 @@ import com.lunarclient.apollo.ApolloManager; import com.lunarclient.apollo.ApolloPlatform; import com.lunarclient.apollo.api.ApolloHttpManager; -import com.lunarclient.apollo.api.request.ServerHeartbeatRequest; +import com.lunarclient.apollo.api.request.heartbeat.ServerHeartbeatRequest; import com.lunarclient.apollo.option.Options; +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.util.concurrent.TimeUnit; @@ -71,8 +72,9 @@ public void run() { boolean performance = options.get(ApolloStatsManager.HEARTBEAT_PERFORMANCE); boolean counts = options.get(ApolloStatsManager.HEARTBEAT_COUNTS); + boolean userMetadata = options.get(ApolloStatsManager.HEARTBEAT_USER_METADATA); - if (!performance && !counts) { + if (!performance && !counts && !userMetadata) { break; } @@ -92,6 +94,15 @@ public void run() { .totalPlayers(stats.getTotalPlayers()); } + if (userMetadata) { + ApolloMetadataManager metadataManager = ApolloManager.getMetadataManager(); + + requestBuilder + .metadata(metadataManager.extract()); + + metadataManager.clear(); + } + final ServerHeartbeatRequest finalRequest = request = requestBuilder.build(); ApolloManager.getHttpManager().request(request) diff --git a/common/src/main/java/com/lunarclient/apollo/stats/metadata/ApolloMetadataManager.java b/common/src/main/java/com/lunarclient/apollo/stats/metadata/ApolloMetadataManager.java new file mode 100644 index 00000000..e9948521 --- /dev/null +++ b/common/src/main/java/com/lunarclient/apollo/stats/metadata/ApolloMetadataManager.java @@ -0,0 +1,49 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.stats.metadata; + +/** + * Represents the Apollo metadata manager, responsible for + * managing and extracting metadata related to the platform. + * + * @since 1.1.9 + */ +public interface ApolloMetadataManager { + + /** + * Extract the current platform metadata. + * + * @return the platform metadata object + * @since 1.1.9 + */ + PlatformMetadata extract(); + + /** + * Clears the current metadata cache. + * + * @since 1.1.9 + */ + void clear(); + +} diff --git a/common/src/main/java/com/lunarclient/apollo/stats/metadata/PlatformMetadata.java b/common/src/main/java/com/lunarclient/apollo/stats/metadata/PlatformMetadata.java new file mode 100644 index 00000000..d1718112 --- /dev/null +++ b/common/src/main/java/com/lunarclient/apollo/stats/metadata/PlatformMetadata.java @@ -0,0 +1,32 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.stats.metadata; + +/** + * Represents the base class for platform specific metadata. + * + * @since 1.1.9 + */ +public abstract class PlatformMetadata { +} diff --git a/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java b/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java index f0341a7c..f72e0e61 100644 --- a/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java +++ b/folia/src/main/java/com/lunarclient/apollo/ApolloFoliaPlatform.java @@ -25,8 +25,10 @@ import com.lunarclient.apollo.command.impl.ApolloCommand; import com.lunarclient.apollo.command.impl.LunarClientCommand; +import com.lunarclient.apollo.listener.ApolloMetadataListener; import com.lunarclient.apollo.listener.ApolloPlayerListener; import com.lunarclient.apollo.listener.ApolloWorldListener; +import com.lunarclient.apollo.metadata.FoliaMetadataManager; import com.lunarclient.apollo.module.ApolloModuleManagerImpl; import com.lunarclient.apollo.module.autotexthotkey.AutoTextHotkeyModule; import com.lunarclient.apollo.module.beam.BeamModule; @@ -109,7 +111,9 @@ public void onEnable() { this.stats = new FoliaApolloStats(); ApolloManager.bootstrap(this); + ApolloManager.setMetadataManager(new FoliaMetadataManager()); + new ApolloMetadataListener(this); new ApolloPlayerListener(this); new ApolloWorldListener(this); diff --git a/folia/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java b/folia/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java new file mode 100644 index 00000000..599f8ea7 --- /dev/null +++ b/folia/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java @@ -0,0 +1,86 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.listener; + +import com.lunarclient.apollo.ApolloManager; +import com.lunarclient.apollo.metadata.FoliaMetadataManager; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent; +import org.bukkit.plugin.java.JavaPlugin; + +/** + * Handles Apollo metadata listeners. + * + * @since 1.1.9 + */ +public final class ApolloMetadataListener implements Listener { + + private final JavaPlugin plugin; + + /** + * Constructs the {@link ApolloMetadataListener}. + * + * @param plugin the plugin + * @since 1.1.9 + */ + public ApolloMetadataListener(JavaPlugin plugin) { + this.plugin = plugin; + + Bukkit.getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + private void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + Bukkit.getGlobalRegionScheduler().runDelayed(this.plugin, t -> { + if (!player.isOnline()) { + return; + } + + String brand = player.getClientBrandName(); + if (brand == null) { + return; + } + + FoliaMetadataManager manager = (FoliaMetadataManager) ApolloManager.getMetadataManager(); + manager.getClientBrands().add(brand); + }, 20L * 3); + } + + @EventHandler + private void onResourcePackStatus(PlayerResourcePackStatusEvent event) { + String status = event.getStatus().name(); + FoliaMetadataManager manager = (FoliaMetadataManager) ApolloManager.getMetadataManager(); + Map statuses = manager.getResourcePackStatuses(); + + statuses.put(status, statuses.getOrDefault(status, 0) + 1); + } + +} diff --git a/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadata.java b/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadata.java new file mode 100644 index 00000000..a8137b7c --- /dev/null +++ b/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadata.java @@ -0,0 +1,60 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.Map; +import java.util.Set; +import lombok.Builder; +import lombok.ToString; + +/** + * Represents the folia metadata implementation. + * + * @since 1.1.9 + */ +@ToString +@Builder(toBuilder = true) +public class FoliaMetadata extends PlatformMetadata { + + /** + * Tracks client brands sent by the players. + * + *

A {@link Set} of {@link String} client brands.

+ * + * @since 1.1.9 + */ + private final Set clientBrands; + + /** + * Tracks the total number of resource pack status events received. + * + *

Represents a {@link Map} of {@link String} status enum name as a key + * and {@link Integer} count of how many times that status has been reported.

+ * + * @since 1.1.9 + */ + private final Map resourcePackStatuses; + +} diff --git a/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadataManager.java b/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadataManager.java new file mode 100644 index 00000000..4018002c --- /dev/null +++ b/folia/src/main/java/com/lunarclient/apollo/metadata/FoliaMetadataManager.java @@ -0,0 +1,60 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import lombok.Getter; + +/** + * The Folia implementation of {@link ApolloMetadataManager}. + * + * @since 1.1.9 + */ +@Getter +public class FoliaMetadataManager implements ApolloMetadataManager { + + private final Set clientBrands = ConcurrentHashMap.newKeySet(); + private final Map resourcePackStatuses = new ConcurrentHashMap<>(); + + @Override + public PlatformMetadata extract() { + return FoliaMetadata.builder() + .clientBrands(new HashSet<>(this.clientBrands)) + .resourcePackStatuses(new HashMap<>(this.resourcePackStatuses)) + .build(); + } + + @Override + public void clear() { + this.clientBrands.clear(); + this.resourcePackStatuses.clear(); + } + +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60742f48..907d96b0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ protobuf = "0.0.2" gson = "2.10.1" shadow = "8.1.1" spotless = "6.13.0" -velocity = "3.0.1" +velocity = "3.3.0-SNAPSHOT" folia = "1.20.1-R0.1-SNAPSHOT" asm = "9.7.1" diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 05a96bfe..9b7ad5bf 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -2,6 +2,10 @@ plugins { id("apollo.shadow-conventions") } +java { + javaTarget(17) +} + dependencies { compileOnly(libs.velocity) diff --git a/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java b/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java index 926d96f1..48b0d255 100644 --- a/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java +++ b/velocity/src/main/java/com/lunarclient/apollo/ApolloVelocityPlatform.java @@ -26,7 +26,9 @@ import com.google.inject.Inject; import com.lunarclient.apollo.command.impl.ApolloCommand; import com.lunarclient.apollo.command.impl.LunarClientCommand; +import com.lunarclient.apollo.listener.ApolloMetadataListener; import com.lunarclient.apollo.listener.ApolloPlayerListener; +import com.lunarclient.apollo.metadata.VelocityMetadataManager; import com.lunarclient.apollo.module.ApolloModuleManagerImpl; import com.lunarclient.apollo.module.autotexthotkey.AutoTextHotkeyModule; import com.lunarclient.apollo.module.beam.BeamModule; @@ -75,6 +77,7 @@ import com.lunarclient.apollo.stats.ApolloStats; import com.lunarclient.apollo.wrapper.VelocityApolloStats; import com.velocitypowered.api.command.CommandManager; +import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; @@ -83,6 +86,7 @@ import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.messages.ChannelRegistrar; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import java.nio.file.Path; import java.util.logging.Level; @@ -163,7 +167,9 @@ public Object getPlugin() { public void onProxyInitialization(ProxyInitializeEvent event) { ApolloVelocityPlatform.instance = this; this.stats = new VelocityApolloStats(); + ApolloManager.bootstrap(this); + ApolloManager.setMetadataManager(new VelocityMetadataManager()); ((ApolloModuleManagerImpl) Apollo.getModuleManager()) .addModule(AutoTextHotkeyModule.class) @@ -199,8 +205,13 @@ public void onProxyInitialization(ProxyInitializeEvent event) { this.getPlatformLogger().log(Level.SEVERE, "Unable to load Apollo configuration and modules!", throwable); } - this.server.getEventManager().register(this, new ApolloPlayerListener()); - this.server.getChannelRegistrar().register(ApolloVelocityPlatform.PLUGIN_CHANNEL); + EventManager eventManager = this.server.getEventManager(); + eventManager.register(this, new ApolloMetadataListener()); + eventManager.register(this, new ApolloPlayerListener()); + + ChannelRegistrar channelRegistrar = this.server.getChannelRegistrar(); + channelRegistrar.register(ApolloVelocityPlatform.PLUGIN_CHANNEL); + channelRegistrar.register(ApolloMetadataListener.FML_HANDSHAKE_CHANNEL); CommandManager commandManager = this.server.getCommandManager(); commandManager.register(ApolloCommand.create()); @@ -222,12 +233,7 @@ public void onProxyShutdown(ProxyShutdownEvent event) { } static { - try { - PLUGIN_CHANNEL = MinecraftChannelIdentifier.from(ApolloManager.PLUGIN_MESSAGE_CHANNEL); - } catch (NoSuchMethodError e) { - String[] messageChannel = ApolloManager.PLUGIN_MESSAGE_CHANNEL.split(":"); - PLUGIN_CHANNEL = MinecraftChannelIdentifier.create(messageChannel[0], messageChannel[1]); - } + PLUGIN_CHANNEL = MinecraftChannelIdentifier.create("lunar", "apollo"); } } diff --git a/velocity/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java b/velocity/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java new file mode 100644 index 00000000..09a56802 --- /dev/null +++ b/velocity/src/main/java/com/lunarclient/apollo/listener/ApolloMetadataListener.java @@ -0,0 +1,141 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.listener; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import com.lunarclient.apollo.ApolloManager; +import com.lunarclient.apollo.metadata.VelocityMetadataManager; +import com.lunarclient.apollo.util.ByteBufUtil; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.connection.PluginMessageEvent; +import com.velocitypowered.api.event.connection.PreLoginEvent; +import com.velocitypowered.api.event.player.PlayerClientBrandEvent; +import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import java.net.InetSocketAddress; +import java.util.Map; + +/** + * Handles Apollo metadata listeners. + * + * @since 1.1.9 + */ +public final class ApolloMetadataListener { + + public static final MinecraftChannelIdentifier FML_HANDSHAKE_CHANNEL; + + /** + * Handles plugin messages for FML mod information. + * + * @param event the event + * @since 1.1.9 + */ + @Subscribe + public void onPluginMessage(PluginMessageEvent event) { + if (!(event.getSource() instanceof Player)) { + return; + } + + if (!event.getIdentifier().equals(ApolloMetadataListener.FML_HANDSHAKE_CHANNEL)) { + return; + } + + this.handleFml(event.getData()); + } + + /** + * Handles player client brands. + * + * @param event the event + * @since 1.1.9 + */ + @Subscribe + public void onPlayerClientBrand(PlayerClientBrandEvent event) { + VelocityMetadataManager manager = (VelocityMetadataManager) ApolloManager.getMetadataManager(); + manager.getClientBrands().add(event.getBrand()); + } + + /** + * Captures the server IP and port the player used to connect. + * + * @param event the event + * @since 1.1.9 + */ + @Subscribe + public void onPreLogin(PreLoginEvent event) { + InetSocketAddress host = event.getConnection().getVirtualHost().orElse(null); + + if (host == null) { + return; + } + + VelocityMetadataManager manager = (VelocityMetadataManager) ApolloManager.getMetadataManager(); + manager.getServerAddress().add(host.getHostString() + ":" + host.getPort()); + } + + /** + * Tracks the total number of resource pack status events received. + * + * @param event the event + * @since 1.1.9 + */ + @Subscribe + public void onResourcePackStatus(PlayerResourcePackStatusEvent event) { + String status = event.getStatus().name(); + VelocityMetadataManager manager = (VelocityMetadataManager) ApolloManager.getMetadataManager(); + Map statuses = manager.getResourcePackStatuses(); + + statuses.put(status, statuses.getOrDefault(status, 0) + 1); + } + + private void handleFml(byte[] data) { + ByteArrayDataInput in = ByteStreams.newDataInput(data); + + try { + byte discriminator = in.readByte(); + + if (discriminator != 2) { + return; + } + + int count = ByteBufUtil.readVarInt(in); + + for (int i = 0; i < count; i++) { + String modId = ByteBufUtil.readString(in); + String version = ByteBufUtil.readString(in); + + VelocityMetadataManager manager = (VelocityMetadataManager) ApolloManager.getMetadataManager(); + manager.getMods().put(modId, version); + } + } catch (Exception ignored) { + } + } + + static { + FML_HANDSHAKE_CHANNEL = MinecraftChannelIdentifier.create("fml", "handshake"); + } + +} diff --git a/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadata.java b/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadata.java new file mode 100644 index 00000000..91577bbc --- /dev/null +++ b/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadata.java @@ -0,0 +1,79 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.Map; +import java.util.Set; +import lombok.Builder; +import lombok.ToString; + +/** + * Represents the velocity metadata implementation. + * + * @since 1.1.9 + */ +@ToString +@Builder(toBuilder = true) +public class VelocityMetadata extends PlatformMetadata { + + /** + * Tracks client brands sent by the players. + * + *

A {@link Set} of {@link String} client brands.

+ * + * @since 1.1.9 + */ + private final Set clientBrands; + + /** + * Tracks forge mods sent with the forge handshake. + * + *

A {@link Map} of {@link String} mod id + * as a key and {@link String} version as value.

+ * + * @since 1.1.9 + */ + private final Map mods; + + /** + * Tracks the server IP and port the player used to connect. + * + *

A {@link Set} of {@link String} server addresses in the format: host:port

+ * + * @since 1.1.9 + */ + private final Set serverAddress; + + /** + * Tracks the total number of resource pack status events received. + * + *

Represents a {@link Map} of {@link String} status enum name as a key + * and {@link Integer} count of how many times that status has been reported.

+ * + * @since 1.1.9 + */ + private final Map resourcePackStatuses; + +} diff --git a/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadataManager.java b/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadataManager.java new file mode 100644 index 00000000..a386448e --- /dev/null +++ b/velocity/src/main/java/com/lunarclient/apollo/metadata/VelocityMetadataManager.java @@ -0,0 +1,65 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.metadata; + +import com.lunarclient.apollo.stats.metadata.ApolloMetadataManager; +import com.lunarclient.apollo.stats.metadata.PlatformMetadata; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import lombok.Getter; + +/** + * The Velocity implementation of {@link ApolloMetadataManager}. + * + * @since 1.1.9 + */ +@Getter +public class VelocityMetadataManager implements ApolloMetadataManager { + + private final Set clientBrands = new HashSet<>(); + private final Map mods = new HashMap<>(); + private final Set serverAddress = new HashSet<>(); + private final Map resourcePackStatuses = new HashMap<>(); + + @Override + public PlatformMetadata extract() { + return VelocityMetadata.builder() + .clientBrands(new HashSet<>(this.clientBrands)) + .mods(new HashMap<>(this.mods)) + .serverAddress(new HashSet<>(this.serverAddress)) + .resourcePackStatuses(new HashMap<>(this.resourcePackStatuses)) + .build(); + } + + @Override + public void clear() { + this.clientBrands.clear(); + this.mods.clear(); + this.serverAddress.clear(); + this.resourcePackStatuses.clear(); + } + +} diff --git a/velocity/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java b/velocity/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java new file mode 100644 index 00000000..67a03b80 --- /dev/null +++ b/velocity/src/main/java/com/lunarclient/apollo/util/ByteBufUtil.java @@ -0,0 +1,83 @@ +/* + * This file is part of Apollo, licensed under the MIT License. + * + * Copyright (c) 2023 Moonsworth + * + * 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 com.lunarclient.apollo.util; + +import com.google.common.io.ByteArrayDataInput; +import java.nio.charset.StandardCharsets; + +/** + * Represents a util for reading byte buffs. + * + * @since 1.1.9 + */ +public final class ByteBufUtil { + + /** + * Reads an int from the given input stream. + * + * @param in in the {@link ByteArrayDataInput} to read from + * @return the read int + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static int readVarInt(ByteArrayDataInput in) { + int i = 0; + int j = 0; + + while (true) { + byte b = in.readByte(); + i |= (b & 0x7F) << j++ * 7; + + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + + if ((b & 0x80) != 128) { + break; + } + } + + return i; + } + + /** + * Reads a UTF-8 encoded string from the given input stream. + * + * @param in the {@link ByteArrayDataInput} to read from + * @return the decoded string + * @throws IllegalArgumentException if the value length is invalid or too large + * @since 1.1.9 + */ + public static String readString(ByteArrayDataInput in) { + int length = ByteBufUtil.readVarInt(in); + byte[] bytes = new byte[length]; + in.readFully(bytes); + + return new String(bytes, StandardCharsets.UTF_8); + } + + private ByteBufUtil() { + } + +}