diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..929018a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Auto detect text files and perform LF normalization +* text=auto + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc5ef80 --- /dev/null +++ b/.gitignore @@ -0,0 +1,154 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.iml + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar + +# Logs and databases # +###################### +*.log +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +.idea +ehthumbs.db +Thumbs.db + +#Eclipse + +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode + +## Directory-based project format +.idea/ +# if you remove the above rule, at least ignore user-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# and these sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml + +## File-based project format +*.ipr +*.iws +*.iml + +## Additional for IntelliJ +out/ + +# generated by mpeltonen/sbt-idea plugin +.idea_modules/ + +# generated by JIRA plugin +atlassian-ide-plugin.xml + +# generated by Crashlytics plugin (for Android Studio and Intellij) +com_crashlytics_export_strings.xml + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Notepad++ backups # +*.bak + +# Project stuff +target/ + +#IntelliJ +.idea/** +*.iml + +# eclipse specific +*.pydevproject +.project +.metadata +bin/** +tmp/** +tmp/**/* +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch +Karhu.iml +*.iml +Karhu.iml +*.iml +Karhu.iml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8805d0d --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md index 729c77c..105ea9a 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ -# Karhu-old \ No newline at end of file +# Karhu -old +MC-Market anticheat + +Contact me on discord before u start working on something xd, so i can commit shit, because i forget to commit so often. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..975c145 --- /dev/null +++ b/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + me.liwk + Karhu + 1.9.20- + jar + + Karhu + + KarhuAC + + 1.8 + UTF-8 + + + + clean package + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + + + + + + + org.projectlombok + lombok + 1.18.12 + provided + + + + commons-io + commons-io + 2.7 + + + + com.google.guava + guava + 29.0-jre + + + + io.janusproject.guava + guava + 19.0.0 + + + + + + com.google.collections + google-collections + 1.0 + + + + diff --git a/src/main/java/io/github/retrooper/packetevents/PacketEvents.java b/src/main/java/io/github/retrooper/packetevents/PacketEvents.java new file mode 100644 index 0000000..7787d0c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/PacketEvents.java @@ -0,0 +1,285 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.impl.PostPlayerInjectEvent; +import io.github.retrooper.packetevents.exceptions.PacketEventsLoadFailureException; +import io.github.retrooper.packetevents.packetmanager.PacketManager; +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.settings.PacketEventsSettings; +import io.github.retrooper.packetevents.updatechecker.UpdateChecker; +import io.github.retrooper.packetevents.utils.entityfinder.EntityFinderUtils; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import io.github.retrooper.packetevents.utils.server.PEVersion; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import io.github.retrooper.packetevents.utils.versionlookup.VersionLookupUtils; +import io.github.retrooper.packetevents.utils.versionlookup.v_1_7_10.ProtocolVersionAccessor_v_1_7; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public final class PacketEvents implements Listener { + private static final PacketEventsAPI packetEventsAPI = new PacketEventsAPI(); + private static final PacketEvents instance = new PacketEvents(); + private static final ArrayList plugins = new ArrayList<>(1); + private static boolean loading, loaded, initialized, initializing, uninitializing; + private static final PEVersion version = new PEVersion(1, 7, 9); + private static PacketEventsSettings settings = new PacketEventsSettings(); + /** + * General executor service, basically for anything that the packet executor service doesn't do. + */ + public static ExecutorService generalExecutorService = Executors.newSingleThreadExecutor(); + //Executor used for player injecting/ejecting and for packet processing/event calling + public static ExecutorService packetHandlingExecutorService = Executors.newSingleThreadExecutor(); + + /** + * This loads the PacketEvents API. + *

+ * ServerVersion: + * In this method we detect and cache the server version. + *

+ * NMSUtils: + * We setup some NMS utilities. + *

+ * Packet ID System: + * All the packet classes we will be needing are cached in a Map with an integer ID. + *

+ * Version Lookup Utils: + * We setup the client protocol version system. + * We check if ViaVersion, ProtocolSupport or ProtocolLib is present. + *

+ * Wrappers: + * All PacketEvents' wrappers are setup and do all loading they need to do. + */ + public static void load() { + if (!loaded && !loading) { + loading = true; + ServerVersion version = ServerVersion.getVersion(); + WrappedPacket.version = version; + PacketEvent.version = version; + NMSUtils.version = version; + EntityFinderUtils.version = version; + + try { + NMSUtils.load(); + + PacketTypeClasses.Client.load(); + PacketTypeClasses.Server.load(); + PacketTypeClasses.Login.load(); + PacketTypeClasses.Status.load(); + + EntityFinderUtils.load(); + + WrappedPacket.loadAllWrappers(); + } catch (Exception ex) { + loading = false; + throw new PacketEventsLoadFailureException(ex); + } + loaded = true; + loading = false; + } + + } + + public static void loadSettings(PacketEventsSettings settings) { + PacketEvents.settings = settings; + } + + public static void init(final Plugin plugin) { + init(plugin, settings); + } + + /** + * Initiates PacketEvents + *

+ * Loading: + * Loads PacketEvents if you haven't already. + *

+ * Registering: + * Registers this class as a Bukkit listener to inject/eject players. + * + * @param pl JavaPlugin instance + */ + public static void init(final Plugin pl, PacketEventsSettings packetEventsSettings) { + load(); + if (!initialized && !initializing) { + initializing = true; + settings = packetEventsSettings; + int packetHandlingThreadCount = settings.getPacketHandlingThreadCount(); + //if the count is 1 or is invalid + if (packetHandlingThreadCount == 1 || packetHandlingThreadCount < 0) { + packetHandlingExecutorService = Executors.newSingleThreadExecutor(); + } else { + packetHandlingExecutorService = Executors.newFixedThreadPool(packetHandlingThreadCount); + } + plugins.add(pl); + + //Register Bukkit listener + Bukkit.getPluginManager().registerEvents(instance, plugins.get(0)); + PacketEvents.getAPI().packetManager = new PacketManager(plugins.get(0), settings.shouldInjectEarly()); + + for (final Player p : Bukkit.getOnlinePlayers()) { + try { + getAPI().getPlayerUtils().injectPlayer(p); + } catch (Exception ex) { + p.kickPlayer("Failed to inject you, please rejoin the server!"); + } + } + + if (settings.shouldCheckForUpdates()) { + PacketEvents.generalExecutorService.execute(new Runnable() { + @Override + public void run() { + new UpdateChecker().handleUpdate(); + } + }); + + } + initialized = true; + initializing = false; + } + } + + /** + * + */ + public static void stop() { + if (initialized && !uninitializing) { + uninitializing = true; + for (Player player : Bukkit.getOnlinePlayers()) { + PacketEvents.getAPI().packetManager.ejectPlayer(player); + } + + if (PacketEvents.getAPI().packetManager.tinyProtocol != null) { + PacketEvents.getAPI().packetManager.tinyProtocol.unregisterChannelHandler(); + } + + getAPI().getEventManager().unregisterAllListeners(); + PacketEvents.generalExecutorService.shutdownNow(); + PacketEvents.packetHandlingExecutorService.shutdownNow(); + initialized = false; + uninitializing = true; + } + } + + public static boolean hasLoaded() { + return loaded; + } + + public static boolean isLoading() { + return loading; + } + + public static boolean isInitializing() { + return initializing; + } + + public static boolean isUninitializing() { + return uninitializing; + } + + public static boolean isInitialized() { + return initialized; + } + + public static PacketEventsAPI getAPI() { + return packetEventsAPI; + } + + public static ArrayList getPlugins() { + return plugins; + } + + /** + * Get the PacketEvents settings. + * + * @return Configure some settings for the API + */ + public static PacketEventsSettings getSettings() { + return settings; + } + + public static PEVersion getVersion() { + return version; + } + + @EventHandler(priority = EventPriority.LOW) + public void onLogin(PlayerLoginEvent e) { + if (PacketEvents.getSettings().shouldInjectEarly()) { + assert getAPI().packetManager.tinyProtocol != null; + try { + getAPI().packetManager.injectPlayer(e.getPlayer()); + } catch (Exception ex) { + e.disallow(PlayerLoginEvent.Result.KICK_OTHER, "We are unable to inject you. Please try again!"); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onJoin(final PlayerJoinEvent e) { + Object channel = NMSUtils.getChannel(e.getPlayer()); + if (PacketEvents.getAPI().getServerUtils().getVersion() == ServerVersion.v_1_7_10) { + ClientVersion version = ClientVersion.getClientVersion(ProtocolVersionAccessor_v_1_7.getProtocolVersion(e.getPlayer())); + PacketEvents.getAPI().getPlayerUtils().clientVersionsMap.put(channel, version); + } + else if(VersionLookupUtils.isDependencyAvailable()) { + int protocolVersion = VersionLookupUtils.getProtocolVersion(e.getPlayer()); + ClientVersion version = ClientVersion.getClientVersion(protocolVersion); + PacketEvents.getAPI().getPlayerUtils().clientVersionsMap.put(channel, version); + } + + if (!PacketEvents.getSettings().shouldInjectEarly()) { + try { + PacketEvents.getAPI().packetManager.injectPlayer(e.getPlayer()); + PacketEvents.getAPI().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer())); + } catch (Exception ex) { + e.getPlayer().kickPlayer("There was an issue injecting you. Please try again!"); + } + } + else { + PacketEvents.getAPI().getEventManager().callEvent(new PostPlayerInjectEvent(e.getPlayer())); + } + } + + + @EventHandler(priority = EventPriority.HIGHEST) + public void onQuit(final PlayerQuitEvent e) { + Object channel = NMSUtils.getChannelNoCache(e.getPlayer()); + PacketEvents.getAPI().getPlayerUtils().clientVersionsMap.remove(channel); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/PacketEventsAPI.java b/src/main/java/io/github/retrooper/packetevents/PacketEventsAPI.java new file mode 100644 index 0000000..185464a --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/PacketEventsAPI.java @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents; + +import io.github.retrooper.packetevents.event.manager.EventManager; +import io.github.retrooper.packetevents.packetmanager.PacketManager; +import io.github.retrooper.packetevents.settings.PacketEventsSettings; +import io.github.retrooper.packetevents.utils.player.PlayerUtils; +import io.github.retrooper.packetevents.utils.server.ServerUtils; + +public final class PacketEventsAPI { + private final EventManager eventManager = new EventManager(); + private final PlayerUtils playerUtils = new PlayerUtils(); + private final ServerUtils serverUtils = new ServerUtils(); + public PacketManager packetManager = null; + + /** + * Get all utilities to do with the player + * + * @return Player Utilities + */ + public PlayerUtils getPlayerUtils() { + return playerUtils; + } + + /** + * Get all utilities to do with the server + * + * @return Server Utilities + */ + public ServerUtils getServerUtils() { + return serverUtils; + } + + + /** + * Get the event manager + * Used to call events, register and unregister listeners + * + * @return PacketEvents' event manager + */ + public EventManager getEventManager() { + return eventManager; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/annotations/Beta.java b/src/main/java/io/github/retrooper/packetevents/annotations/Beta.java new file mode 100644 index 0000000..2e712a6 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/annotations/Beta.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This annotation indicates that something might be unstable. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface Beta { +} diff --git a/src/main/java/io/github/retrooper/packetevents/annotations/NotNull.java b/src/main/java/io/github/retrooper/packetevents/annotations/NotNull.java new file mode 100644 index 0000000..1189bec --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/annotations/NotNull.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.annotations; + + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Guaranteed to not be null. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface NotNull { + + +} diff --git a/src/main/java/io/github/retrooper/packetevents/annotations/Nullable.java b/src/main/java/io/github/retrooper/packetevents/annotations/Nullable.java new file mode 100644 index 0000000..a551f6d --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/annotations/Nullable.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.annotations; + + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Possible that it is null. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface Nullable { + + +} diff --git a/src/main/java/io/github/retrooper/packetevents/enums/Direction.java b/src/main/java/io/github/retrooper/packetevents/enums/Direction.java new file mode 100644 index 0000000..c0dcbf5 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/enums/Direction.java @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.enums; + +public enum Direction { + /** + * -Y offset + */ + DOWN, + + /** + * +Y offset + */ + UP, + + /** + * -Z offset + */ + NORTH, + + /** + * +Z offset + */ + SOUTH, + + /** + * -X offset + */ + WEST, + + /** + * +X offset + */ + EAST; +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/PacketEvent.java b/src/main/java/io/github/retrooper/packetevents/event/PacketEvent.java new file mode 100644 index 0000000..fd134ba --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/PacketEvent.java @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event; + + +import io.github.retrooper.packetevents.utils.server.ServerVersion; + +/** + * An event in the PacketEvents EventManager system. + */ +public abstract class PacketEvent { + public static ServerVersion version; + private long timestamp = System.currentTimeMillis(); + + /** + * Timestamp of the packet + * + * @return timestamp in milliseconds + */ + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/event/PacketListener.java b/src/main/java/io/github/retrooper/packetevents/event/PacketListener.java new file mode 100644 index 0000000..cea782d --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/PacketListener.java @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event; + +/** + * A Packet listener. + * Implement this interface in your listeners. + */ +@Deprecated +public interface PacketListener { +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/PacketListenerDynamic.java b/src/main/java/io/github/retrooper/packetevents/event/PacketListenerDynamic.java new file mode 100644 index 0000000..9b469d6 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/PacketListenerDynamic.java @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event; + +import io.github.retrooper.packetevents.event.impl.*; + +public abstract class PacketListenerDynamic { + private final byte priority; + public PacketListenerDynamic(final byte priority) { + this.priority = priority; + } + + public final byte getPriority() { + return priority; + } + + public void onPacketStatus(PacketStatusEvent event) { + } + + public void onPacketLogin(PacketLoginEvent event) { + } + + public void onPacketReceive(PacketReceiveEvent event) { + } + + public void onPacketSend(PacketSendEvent event) { + } + + public void onPostPacketReceive(PostPacketReceiveEvent event) { + } + + public void onPostPacketSend(PostPacketSendEvent event) { + } + + public void onPlayerInject(PlayerInjectEvent event) { + } + + public void onPlayerEject(PlayerEjectEvent event) { + } + + public void onPacketEvent(PacketEvent event) { + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/annotation/PacketHandler.java b/src/main/java/io/github/retrooper/packetevents/event/annotation/PacketHandler.java new file mode 100644 index 0000000..13cf7cf --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/annotation/PacketHandler.java @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.annotation; + +import io.github.retrooper.packetevents.event.priority.PacketEventPriority; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PacketHandler { + byte priority() default PacketEventPriority.NORMAL; +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/eventtypes/CancellableEvent.java b/src/main/java/io/github/retrooper/packetevents/event/eventtypes/CancellableEvent.java new file mode 100644 index 0000000..003c20c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/eventtypes/CancellableEvent.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.eventtypes; + +/** + * This indicates that it is possible to cancel an event, + * which should result in cancelling the planned action. + */ +public interface CancellableEvent { + + /** + * Has the event been cancelled? + * @return is event cancelled + */ + boolean isCancelled(); + + /** + * Cancel or proceed with the event. + * @param val + */ + void setCancelled(boolean val); + + /** + * Cancel the event. + * You can achieve the same result by just using {@link #setCancelled(boolean)} + */ + void cancel(); + + /** + * Uncancel the event. + * You can achieve the same result by just using {@link #setCancelled(boolean)} + */ + void uncancel(); +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/eventtypes/PlayerEvent.java b/src/main/java/io/github/retrooper/packetevents/event/eventtypes/PlayerEvent.java new file mode 100644 index 0000000..1ae0219 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/eventtypes/PlayerEvent.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.eventtypes; + +import org.bukkit.entity.Player; + +/** + * Event having to do with a player. + */ +public interface PlayerEvent { + /** + * Associated player. + */ + Player getPlayer(); +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PacketLoginEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketLoginEvent.java new file mode 100644 index 0000000..54bbe2d --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketLoginEvent.java @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +/** + * This event is called when we receive a login packet. + */ +public class PacketLoginEvent extends PacketEvent implements CancellableEvent { + private final Object channel; + private final Object packet; + private boolean cancelled; + + public PacketLoginEvent(final Object channel, final Object packet) { + this.channel = channel; + this.packet = packet; + } + + public Object getChannel() { + return channel; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the raw packet object + * + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + /** + * Get the ID of the packet + * + * @return packet id + */ + public byte getPacketId() { + return PacketType.Login.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PacketReceiveEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketReceiveEvent.java new file mode 100644 index 0000000..78e0258 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketReceiveEvent.java @@ -0,0 +1,109 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import org.bukkit.entity.Player; + +/** + * This event is called each time a packet is received from a client. + */ +public final class PacketReceiveEvent extends PacketEvent implements CancellableEvent, PlayerEvent { + private final Player player; + private final Object packet; + private boolean cancelled; + private byte packetID = -1; + + public PacketReceiveEvent(final Player player, final Object packet) { + this.player = player; + this.packet = packet; + } + + /** + * Get the packet sender + * + * @return player + */ + @Override + public Player getPlayer() { + return player; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the raw packet object + * + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + /** + * Get the ID of the packet + * + * @return packet id + */ + public byte getPacketId() { + if (packetID == -1) { + packetID = PacketType.Client.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + return packetID; + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PacketSendEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketSendEvent.java new file mode 100644 index 0000000..9dcabb2 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketSendEvent.java @@ -0,0 +1,104 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import org.bukkit.entity.Player; + +/** + * This event is called each time the server sends a packet to the client. + */ +public final class PacketSendEvent extends PacketEvent implements CancellableEvent, PlayerEvent { + private final Player player; + private final Object packet; + private boolean cancelled; + private byte packetID = -1; + + public PacketSendEvent(final Player player, final Object packet) { + this.player = player; + this.packet = packet; + this.cancelled = false; + } + + @Override + public Player getPlayer() { + return player; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the ID of the packet + * + * @return packet id + */ + public byte getPacketId() { + if(packetID == -1) { + packetID = PacketType.Server.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + return packetID; + } + + /** + * Get the NMS packet object + * + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } +} + diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PacketStatusEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketStatusEvent.java new file mode 100644 index 0000000..5fe6c84 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PacketStatusEvent.java @@ -0,0 +1,97 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +public class PacketStatusEvent extends PacketEvent implements CancellableEvent { + private final Object channel; + private final Object packet; + private boolean cancelled; + private byte packetID = -1; + + public PacketStatusEvent(final Object channel, final Object packet) { + this.channel = channel; + this.packet = packet; + } + + public Object getChannel() { + return channel; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the raw packet object + * + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + /** + * Get the ID of the packet + * + * @return packet id + */ + public byte getPacketId() { + if (packetID == -1) { + packetID = PacketType.Status.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + return packetID; + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerEjectEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerEjectEvent.java new file mode 100644 index 0000000..18a7870 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerEjectEvent.java @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import org.bukkit.entity.Player; + +/** + * This event is called each time you eject a player. + */ +public final class PlayerEjectEvent extends PacketEvent implements CancellableEvent, PlayerEvent { + private final Player player; + private final boolean async; + private boolean cancelled; + + public PlayerEjectEvent(final Player player, final boolean isAsync) { + this.player = player; + this.async = isAsync; + } + + public PlayerEjectEvent(final Player player) { + this.player = player; + this.async = true; //default value + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } + + @Override + public Player getPlayer() { + return player; + } + + public boolean isAsync() { + return async; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerInjectEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerInjectEvent.java new file mode 100644 index 0000000..a339c2a --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PlayerInjectEvent.java @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import org.bukkit.entity.Player; + +/** + * This event is called each time you inject a player. + */ +public final class PlayerInjectEvent extends PacketEvent implements CancellableEvent, PlayerEvent { + private final Player player; + private final boolean async; + private boolean cancelled; + + public PlayerInjectEvent(final Player player) { + this(player, false); + } + + public PlayerInjectEvent(final Player player, boolean isAsync) { + this.player= player; + this.async = isAsync; + } + + @Override + public void cancel() { + cancelled = true; + } + + @Override + public void uncancel() { + cancelled = false; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean value) { + cancelled = value; + } + + @Override + public Player getPlayer() { + return player; + } + + public boolean isAsync() { + return async; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketReceiveEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketReceiveEvent.java new file mode 100644 index 0000000..a95f182 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketReceiveEvent.java @@ -0,0 +1,78 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import org.bukkit.entity.Player; + +public class PostPacketReceiveEvent extends PacketEvent implements PlayerEvent { + private final Player player; + private final Object packet; + private byte packetID = -1; + public PostPacketReceiveEvent(final Player player, final Object packet) { + this.player = player; + this.packet = packet; + } + + /** + * Get the packet sender + * @return player + */ + @Override + public Player getPlayer() { + return player; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the raw packet object + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + /** + * Get the ID of the packet + * @return packet id + */ + public byte getPacketId() { + if(packetID == -1) { + packetID = PacketType.Client.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + return packetID; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketSendEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketSendEvent.java new file mode 100644 index 0000000..53da22b --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPacketSendEvent.java @@ -0,0 +1,83 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import org.bukkit.entity.Player; + +public class PostPacketSendEvent extends PacketEvent implements PlayerEvent { + private final Player player; + private final Object packet; + private byte packetID = -1; + + public PostPacketSendEvent(final Player player, final Object packet) { + this.player = player; + this.packet = packet; + } + + /** + * Get the packet sender + * + * @return player + */ + @Override + public Player getPlayer() { + return player; + } + + /** + * Get the packet's name (NMS packet class simple name). + * The class simple name is cached. + * + * @return Name of the packet + */ + public String getPacketName() { + return ClassUtil.getClassSimpleName(packet.getClass()); + } + + /** + * Get the raw packet object + * + * @return packet object + */ + public Object getNMSPacket() { + return packet; + } + + /** + * Get the ID of the packet + * + * @return packet id + */ + public byte getPacketId() { + if (packetID == -1) { + packetID = PacketType.Server.packetIds.getOrDefault(packet.getClass(), (byte) -1); + } + return packetID; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/impl/PostPlayerInjectEvent.java b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPlayerInjectEvent.java new file mode 100644 index 0000000..eaa1c73 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/impl/PostPlayerInjectEvent.java @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.impl; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.eventtypes.PlayerEvent; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import org.bukkit.entity.Player; + +public class PostPlayerInjectEvent extends PacketEvent implements PlayerEvent { + private final Player player; + public PostPlayerInjectEvent(Player player) { + this.player = player; + } + + @Override + public Player getPlayer() { + return player; + } + + public Object getChannel() { + return NMSUtils.getChannel(player); + } + + public ClientVersion getClientVersion() { + return PacketEvents.getAPI().getPlayerUtils().clientVersionsMap.get(getChannel()); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/manager/EventManager.java b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManager.java new file mode 100644 index 0000000..7b6eb09 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManager.java @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.manager; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.PacketListener; +import io.github.retrooper.packetevents.event.PacketListenerDynamic; + +public final class EventManager { + private final EventManagerLegacy legacyEventManager = new EventManagerLegacy(); + private final EventManagerDynamic dynamicEventManager = new EventManagerDynamic(); + + public void callEvent(PacketEvent event) { + dynamicEventManager.callEvent(event); + legacyEventManager.callEvent(event); + } + + @Deprecated + public void registerListener(PacketListener listener) { + legacyEventManager.registerListener(listener); + } + + @Deprecated + public void registerListeners(PacketListener... listeners) { + legacyEventManager.registerListeners(listeners); + } + + @Deprecated + public void unregisterListener(PacketListener listener) { + legacyEventManager.unregisterListener(listener); + } + + @Deprecated + public void unregisterListeners(PacketListener... listeners) { + legacyEventManager.unregisterListeners(listeners); + } + + public void registerListener(PacketListenerDynamic listener) { + dynamicEventManager.registerListener(listener); + } + + public void registerListeners(PacketListenerDynamic... listeners) { + dynamicEventManager.registerListeners(listeners); + } + + public void unregisterListener(PacketListenerDynamic listener) { + dynamicEventManager.unregisterListener(listener); + } + + public void unregisterListeners(PacketListenerDynamic... listeners) { + dynamicEventManager.unregisterListeners(listeners); + } + + public void unregisterAllListeners() { + dynamicEventManager.unregisterAllListeners(); + legacyEventManager.unregisterAllListeners(); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerDynamic.java b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerDynamic.java new file mode 100644 index 0000000..e93603b --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerDynamic.java @@ -0,0 +1,117 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.manager; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.PacketListenerDynamic; +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.impl.*; +import io.github.retrooper.packetevents.event.priority.PacketEventPriority; +import io.github.retrooper.packetevents.exceptions.PacketEventsMethodInvokeException; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class EventManagerDynamic { + private final Map> map = new HashMap<>(); + + public void callEvent(PacketEvent event) { + boolean isCancelled = false; + for (byte i = PacketEventPriority.LOWEST; i <= PacketEventPriority.MONITOR; i++) { + if (map.get(i) != null) { + for (PacketListenerDynamic listener : map.get(i)) { + try { + listener.onPacketEvent(event); + if (event instanceof PacketStatusEvent) { + listener.onPacketStatus((PacketStatusEvent) event); + } else if (event instanceof PacketLoginEvent) { + listener.onPacketLogin((PacketLoginEvent) event); + } else if (event instanceof PacketReceiveEvent) { + listener.onPacketReceive((PacketReceiveEvent) event); + } else if (event instanceof PacketSendEvent) { + listener.onPacketSend((PacketSendEvent) event); + } else if (event instanceof PostPacketReceiveEvent) { + listener.onPostPacketReceive((PostPacketReceiveEvent) event); + } else if (event instanceof PostPacketSendEvent) { + listener.onPostPacketSend((PostPacketSendEvent) event); + } else if (event instanceof PlayerInjectEvent) { + listener.onPlayerInject((PlayerInjectEvent) event); + } else if (event instanceof PlayerEjectEvent) { + listener.onPlayerEject((PlayerEjectEvent) event); + } + + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (event instanceof CancellableEvent) { + CancellableEvent ce = (CancellableEvent) event; + isCancelled = ce.isCancelled(); + } + } + } + } + if (event instanceof CancellableEvent) { + CancellableEvent ce = (CancellableEvent) event; + ce.setCancelled(isCancelled); + } + } + + public void registerListener(PacketListenerDynamic listener) { + List listeners = map.get(listener.getPriority()); + if (listeners == null) { + map.put(listener.getPriority(), new ArrayList<>()); + listeners = map.get(listener.getPriority()); + } + listeners.add(listener); + } + + public void registerListeners(PacketListenerDynamic... listeners) { + for (PacketListenerDynamic listener : listeners) { + registerListener(listener); + } + } + + public void unregisterListener(PacketListenerDynamic listener) { + List listeners = map.get(listener.getPriority()); + if (listeners == null) { + return; + } + listeners.remove(listener); + } + + public void unregisterListeners(PacketListenerDynamic... listeners) { + for (PacketListenerDynamic listener : listeners) { + unregisterListener(listener); + } + } + + public void unregisterAllListeners() { + map.clear(); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerLegacy.java b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerLegacy.java new file mode 100644 index 0000000..5720904 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/manager/EventManagerLegacy.java @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.manager; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.event.PacketListener; +import io.github.retrooper.packetevents.event.annotation.PacketHandler; +import io.github.retrooper.packetevents.event.eventtypes.CancellableEvent; +import io.github.retrooper.packetevents.event.priority.PacketEventPriority; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class EventManagerLegacy { + private final Map> staticRegisteredMethods = new HashMap<>(); + public void callEvent(final PacketEvent e) { + boolean isCancelled = false; + byte eventPriority = PacketEventPriority.LOWEST; + //STATIC LISTENERS + for (final PacketListener listener : staticRegisteredMethods.keySet()) { + List methods = staticRegisteredMethods.get(listener); + + for (Method method : methods) { + Class parameterType = method.getParameterTypes()[0]; + if (parameterType.equals(PacketEvent.class) + || parameterType.isInstance(e)) { + + PacketHandler annotation = method.getAnnotation(PacketHandler.class); + try { + method.invoke(listener, e); + } catch (IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + } + if (e instanceof CancellableEvent) { + CancellableEvent ce = (CancellableEvent) e; + if (annotation.priority() >= eventPriority) { + eventPriority = annotation.priority(); + isCancelled = ce.isCancelled(); + } + } + } + } + } + if(e instanceof CancellableEvent) { + CancellableEvent ce = (CancellableEvent)e; + ce.setCancelled(isCancelled); + } + } + + public void registerListener(final PacketListener listener) { + final List methods = new ArrayList<>(); + for (final Method m : listener.getClass().getDeclaredMethods()) { + if (!m.isAccessible()) { + m.setAccessible(true); + } + if (m.isAnnotationPresent(PacketHandler.class) + && m.getParameterTypes().length == 1) { + methods.add(m); + } + } + + if (!methods.isEmpty()) { + staticRegisteredMethods.put(listener, methods); + } + } + + public void registerListeners(final PacketListener... listeners) { + for (final PacketListener listener : listeners) { + registerListener(listener); + } + } + + public void unregisterListener(final PacketListener e) { + staticRegisteredMethods.remove(e); + } + + public void unregisterListeners(final PacketListener... listeners) { + for (final PacketListener listener : listeners) { + unregisterListener(listener); + } + } + + public void unregisterAllListeners() { + staticRegisteredMethods.clear(); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/event/priority/PacketEventPriority.java b/src/main/java/io/github/retrooper/packetevents/event/priority/PacketEventPriority.java new file mode 100644 index 0000000..f1e1bbb --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/event/priority/PacketEventPriority.java @@ -0,0 +1,34 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.event.priority; + +public interface PacketEventPriority { + byte LOWEST = 0; + byte LOW = 1; + byte NORMAL = 2; + byte HIGH = 3; + byte HIGHEST = 4; + byte MONITOR = 5; +} diff --git a/src/main/java/io/github/retrooper/packetevents/example/MainExample.java b/src/main/java/io/github/retrooper/packetevents/example/MainExample.java new file mode 100644 index 0000000..1fa042e --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/example/MainExample.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.example; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketListenerDynamic; +import io.github.retrooper.packetevents.event.impl.PacketReceiveEvent; +import io.github.retrooper.packetevents.event.priority.PacketEventPriority; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.packetwrappers.in.useentity.WrappedPacketInUseEntity; +import io.github.retrooper.packetevents.utils.player.ClientHand; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import org.bukkit.plugin.java.JavaPlugin; + +public class MainExample extends JavaPlugin { + @Override + public void onLoad() { + PacketEvents.load(); + } + + @Override + public void onEnable() { + PacketEvents.getSettings().injectAsync(true) + .ejectAsync(true).backupServerVersion(ServerVersion.v_1_7_10) + .checkForUpdates(true).injectEarly(true) + .packetHandlingThreadCount(1); + PacketEvents.init(this); + } + + @Override + public void onDisable() { + PacketEvents.stop(); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsLoadFailureException.java b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsLoadFailureException.java new file mode 100644 index 0000000..fea803c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsLoadFailureException.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.exceptions; + +public class PacketEventsLoadFailureException extends RuntimeException { + public PacketEventsLoadFailureException(String message) { + super(message); + } + + public PacketEventsLoadFailureException(String message, Throwable cause) { + super(message, cause); + } + + public PacketEventsLoadFailureException() { + this("PacketEvents failed to successfully load..."); + } + + public PacketEventsLoadFailureException(Throwable cause) { + this("PacketEvents failed to successfully load...", cause); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodAccessException.java b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodAccessException.java new file mode 100644 index 0000000..82a4c9c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodAccessException.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.exceptions; + +import io.github.retrooper.packetevents.event.PacketListener; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +import java.lang.reflect.Method; + +public class PacketEventsMethodAccessException extends RuntimeException { + public PacketEventsMethodAccessException(String message) { + super(message); + } + + public PacketEventsMethodAccessException(Method method, PacketListener listener) { + this("PacketEvents failed to access the " + + method.getName() + " method in " + ClassUtil.getClassSimpleName(listener.getClass())); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodInvokeException.java b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodInvokeException.java new file mode 100644 index 0000000..cc83835 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/exceptions/PacketEventsMethodInvokeException.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.exceptions; + +import io.github.retrooper.packetevents.event.PacketListener; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +import java.lang.reflect.Method; + +public class PacketEventsMethodInvokeException extends RuntimeException { + public PacketEventsMethodInvokeException(String message) { + super(message); + } + + public PacketEventsMethodInvokeException(String message, Throwable cause) { + super(message, cause); + } + + public PacketEventsMethodInvokeException(Method method, PacketListener listener) { + this("PacketEvents failed to call the " + method.getName() + " method in " + + ClassUtil.getClassSimpleName(listener.getClass())); + } + + public PacketEventsMethodInvokeException(Method method, PacketListener listener, Throwable cause) { + this("PacketEvents failed to call the " + method.getName() + " method in " + + ClassUtil.getClassSimpleName(listener.getClass()), cause); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/exceptions/WrapperFieldNotFoundException.java b/src/main/java/io/github/retrooper/packetevents/exceptions/WrapperFieldNotFoundException.java new file mode 100644 index 0000000..29621e9 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/exceptions/WrapperFieldNotFoundException.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.exceptions; + +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; + +public class WrapperFieldNotFoundException extends RuntimeException{ + public WrapperFieldNotFoundException(String message) { + super(message); + } + + public WrapperFieldNotFoundException(Class packetClass, Class type, int index) { + this("PacketEvents failed to find a " + ClassUtil.getClassSimpleName(type) + " indexed " + index + " by its type in the " + packetClass.getName() + " class!"); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/PacketManager.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/PacketManager.java new file mode 100644 index 0000000..b84e426 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/PacketManager.java @@ -0,0 +1,292 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetmanager; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.impl.*; +import io.github.retrooper.packetevents.packetmanager.netty.NettyPacketManager; +import io.github.retrooper.packetevents.packetmanager.tinyprotocol.TinyProtocol; +import io.github.retrooper.packetevents.packettype.PacketType; +import io.github.retrooper.packetevents.packetwrappers.login.in.WrappedPacketLoginHandshake; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +public class PacketManager { + private final Plugin plugin; + private final boolean tinyProtocolMode; + public final TinyProtocol tinyProtocol; + public final NettyPacketManager nettyProtocol; + private final HashMap keepAliveMap = new HashMap<>(); + + public PacketManager(Plugin plugin, boolean tinyProtocolMode) { + this.plugin = plugin; + this.tinyProtocolMode = tinyProtocolMode; + if (tinyProtocolMode) { + tinyProtocol = new TinyProtocol(plugin); + nettyProtocol = null; + } else { + nettyProtocol = new NettyPacketManager(plugin); + tinyProtocol = null; + } + } + + public void injectPlayer(Player player) { + if (PacketEvents.getSettings().shouldInjectAsync()) { + injectPlayerAsync(player); + } else { + injectPlayerSync(player); + } + } + + public void ejectPlayer(Player player) { + if (PacketEvents.getSettings().shouldEjectAsync()) { + ejectPlayerAsync(player); + } else { + ejectPlayerSync(player); + } + } + + public void injectPlayerSync(Player player) { + PlayerInjectEvent injectEvent = new PlayerInjectEvent(player, false); + PacketEvents.getAPI().getEventManager().callEvent(injectEvent); + if (!injectEvent.isCancelled()) { + if (tinyProtocolMode) { + tinyProtocol.injectPlayer(player); + } else { + nettyProtocol.injectPlayer(player); + } + } + } + + public void injectPlayerAsync(Player player) { + PlayerInjectEvent injectEvent = new PlayerInjectEvent(player, true); + PacketEvents.getAPI().getEventManager().callEvent(injectEvent); + if (!injectEvent.isCancelled()) { + if (tinyProtocolMode) { + assert tinyProtocol != null; + tinyProtocol.injectPlayerAsync(player); + } else { + assert nettyProtocol != null; + nettyProtocol.injectPlayerAsync(player); + } + } + } + + public void ejectPlayerSync(Player player) { + PlayerEjectEvent ejectEvent = new PlayerEjectEvent(player, false); + PacketEvents.getAPI().getEventManager().callEvent(ejectEvent); + if (!ejectEvent.isCancelled()) { + keepAliveMap.remove(player.getUniqueId()); + if (tinyProtocolMode) { + tinyProtocol.ejectPlayer(player); + } else { + nettyProtocol.ejectPlayer(player); + } + } + } + + public void ejectPlayerAsync(Player player) { + PlayerEjectEvent ejectEvent = new PlayerEjectEvent(player, true); + PacketEvents.getAPI().getEventManager().callEvent(ejectEvent); + if (!ejectEvent.isCancelled()) { + keepAliveMap.remove(player.getUniqueId()); + if (tinyProtocolMode) { + tinyProtocol.ejectPlayerAsync(player); + } else { + nettyProtocol.ejectPlayerAsync(player); + } + } + } + + public void ejectChannel(Object channel) { + if (!PacketEvents.getSettings().shouldEjectAsync()) { + ejectChannelSync(channel); + } else { + ejectChannelAsync(channel); + } + } + + public void ejectChannelSync(Object channel) { + if (tinyProtocolMode) { + tinyProtocol.ejectChannelSync(channel); + } else { + nettyProtocol.ejectChannelSync(channel); + } + } + + public void ejectChannelAsync(Object channel) { + if (tinyProtocolMode) { + tinyProtocol.ejectChannelAsync(channel); + } else { + nettyProtocol.ejectChannelAsync(channel); + } + } + + public void sendPacket(Object channel, Object packet) { + if (tinyProtocolMode) { + tinyProtocol.sendPacket(channel, packet); + } else { + nettyProtocol.sendPacket(channel, packet); + } + } + + public String getNettyHandlerName() { + return "pe-" + plugin.getName(); + } + + public Object read(Player player, Object channel, Object packet) { + if (player == null) { + String simpleClassName = ClassUtil.getClassSimpleName(packet.getClass()); + //Status packet + if (simpleClassName.startsWith("PacketS")) { + final PacketStatusEvent packetStatusEvent = new PacketStatusEvent(channel, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetStatusEvent); + interceptStatus(packetStatusEvent); + if (packetStatusEvent.isCancelled()) { + packet = null; + } + } else { + //Login packet + final PacketLoginEvent packetLoginEvent = new PacketLoginEvent(channel, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetLoginEvent); + interceptLogin(packetLoginEvent); + if (packetLoginEvent.isCancelled()) { + packet = null; + } + } + } else { + final PacketReceiveEvent packetReceiveEvent = new PacketReceiveEvent(player, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetReceiveEvent); + interceptRead(packetReceiveEvent); + if (packetReceiveEvent.isCancelled()) { + packet = null; + } + } + return packet; + } + + public Object write(Player player, Object channel, Object packet) { + if (player == null) { + String simpleClassName = ClassUtil.getClassSimpleName(packet.getClass()); + //Status packet + if (simpleClassName.startsWith("PacketS")) { + final PacketStatusEvent packetStatusEvent = new PacketStatusEvent(channel, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetStatusEvent); + interceptStatus(packetStatusEvent); + if (packetStatusEvent.isCancelled()) { + packet = null; + } + } + //Login packet + else { + final PacketLoginEvent packetLoginEvent = new PacketLoginEvent(channel, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetLoginEvent); + interceptLogin(packetLoginEvent); + if (packetLoginEvent.isCancelled()) { + packet = null; + } + } + } else { + final PacketSendEvent packetSendEvent = new PacketSendEvent(player, packet); + PacketEvents.getAPI().getEventManager().callEvent(packetSendEvent); + interceptWrite(packetSendEvent); + if (packetSendEvent.isCancelled()) { + packet = null; + } + } + return packet; + } + + public void postRead(Player player, Object packet) { + switch (ClassUtil.getClassSimpleName(packet.getClass())) { + case "PacketHandshakingInSetProtocol": + case "PacketLoginInCustomPayload": + case "PacketLoginInStart": + case "PacketLoginInEncryptionBegin": + case "PacketStatusInPing": + break; + default: + PacketEvents.getAPI().getEventManager().callEvent(new PostPacketReceiveEvent(player, packet)); + break; + } + } + + public void postWrite(Player player, Object packet) { + switch (ClassUtil.getClassSimpleName(packet.getClass())) { + case "PacketLoginOutDisconnect": + case "PacketLoginOutEncryptionBegin": + case "PacketLoginOutSetCompression": + case "PacketLoginOutSuccess": + case "PacketStatusOutPong": + case "PacketStatusOutServerInfo": + break; + default: + PacketEvents.getAPI().getEventManager().callEvent(new PostPacketSendEvent(player, packet)); + break; + } + } + + + private void interceptRead(PacketReceiveEvent event) { + if (event.getPacketId() == PacketType.Client.KEEP_ALIVE) { + UUID uuid = event.getPlayer().getUniqueId(); + long timestamp = keepAliveMap.getOrDefault(uuid, event.getTimestamp()); + long currentTime = event.getTimestamp(); + long ping = currentTime - timestamp; + long smoothedPing = (PacketEvents.getAPI().getPlayerUtils().getSmoothedPing(event.getPlayer()) * 3 + ping) / 4; + PacketEvents.getAPI().getPlayerUtils().playerPingMap.put(uuid, (short) ping); + PacketEvents.getAPI().getPlayerUtils().playerSmoothedPingMap.put(uuid, (short) smoothedPing); + } + } + + private void interceptWrite(PacketSendEvent event) { + if (event.getPacketId() == PacketType.Server.KEEP_ALIVE) { + keepAliveMap.put(event.getPlayer().getUniqueId(), event.getTimestamp()); + } + } + + private void interceptLogin(PacketLoginEvent event) { + if (event.getPacketId() == PacketType.Login.HANDSHAKE + && PacketEvents.getAPI().getServerUtils().getVersion() != ServerVersion.v_1_7_10 + && !PacketEvents.getAPI().getServerUtils().isBungeeCordEnabled()) { + WrappedPacketLoginHandshake handshake = new WrappedPacketLoginHandshake(event.getNMSPacket()); + int protocolVersion = handshake.getProtocolVersion(); + ClientVersion version = ClientVersion.getClientVersion(protocolVersion); + PacketEvents.getAPI().getPlayerUtils().clientVersionsMap.put(event.getChannel(), version); + } + } + + private void interceptStatus(PacketStatusEvent event) { + + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager.java new file mode 100644 index 0000000..a726264 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager.java @@ -0,0 +1,156 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetmanager.netty; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NettyPacketManager { + public static final boolean v1_7_nettyMode; + + static { + boolean v1_7_nettyMode1; + try { + Class.forName("net.minecraft.util.io.netty.channel.Channel"); + v1_7_nettyMode1 = true; + } catch (ClassNotFoundException e) { + v1_7_nettyMode1 = false; + } + v1_7_nettyMode = v1_7_nettyMode1; + } + + private final Object npm; + + public NettyPacketManager(Plugin plugin) { + if (v1_7_nettyMode) { + npm = new NettyPacketManager_7(); + } else { + npm = new NettyPacketManager_8(); + } + } + + /** + * Synchronously inject a player + * + * @param player Target player to inject + */ + public void injectPlayer(final Player player) { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.injectPlayer(player); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.injectPlayer(player); + } + } + + /** + * Asynchronously inject a player + * + * @param player + */ + public void injectPlayerAsync(final Player player) { + //Redundant channel variable created just so we can cache it synchronously. + Object channel = NMSUtils.getChannel(player); + PacketEvents.packetHandlingExecutorService.execute(() -> { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.injectPlayer(player); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.injectPlayer(player); + } + }); + } + + /** + * Synchronously eject a player. + * + * @param player + */ + public void ejectPlayer(final Player player) { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.ejectPlayer(player); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.ejectPlayer(player); + } + } + + /** + * Asynchronously eject a player + * + * @param player + */ + public void ejectPlayerAsync(final Player player) { + PacketEvents.packetHandlingExecutorService.execute(() -> { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.ejectPlayer(player); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.ejectPlayer(player); + } + }); + } + + public void ejectChannelSync(Object channel) { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.ejectChannel(channel); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.ejectChannel(channel); + } + } + + public void ejectChannelAsync(Object channel) { + PacketEvents.packetHandlingExecutorService.execute(() -> { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.ejectChannel(channel); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.ejectChannel(channel); + } + }); + } + + public void sendPacket(Object channel, Object packet) { + if (v1_7_nettyMode) { + NettyPacketManager_7 npm_7 = (NettyPacketManager_7) npm; + npm_7.sendPacket(channel, packet); + } else { + NettyPacketManager_8 npm_8 = (NettyPacketManager_8) npm; + npm_8.sendPacket(channel, packet); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_7.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_7.java new file mode 100644 index 0000000..2f154af --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_7.java @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetmanager.netty; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import net.minecraft.util.io.netty.channel.Channel; +import net.minecraft.util.io.netty.channel.ChannelDuplexHandler; +import net.minecraft.util.io.netty.channel.ChannelHandlerContext; +import net.minecraft.util.io.netty.channel.ChannelPipeline; +import net.minecraft.util.io.netty.channel.ChannelPromise; +import org.bukkit.entity.Player; + +final class NettyPacketManager_7 { + NettyPacketManager_7() { + + } + /** + * Inject a player using 1.7.10's netty import location + * @param player + */ + public void injectPlayer(final Player player) { + final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Object packet = PacketEvents.getAPI().packetManager.read(player, ctx.channel(), msg); + if (packet == null) { + return; + } + super.channelRead(ctx, msg); + PacketEvents.getAPI().packetManager.postRead(player, packet); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Object packet = PacketEvents.getAPI().packetManager.write(player, ctx.channel(),msg); + if (packet == null) { + return; + } + super.write(ctx, msg, promise); + PacketEvents.getAPI().packetManager.postWrite(player, packet); + } + }; + final ChannelPipeline pipeline = ((Channel) NMSUtils.getChannel(player)).pipeline(); + pipeline.addBefore("packet_handler", PacketEvents.getAPI().packetManager.getNettyHandlerName(), channelDuplexHandler); + } + + public void ejectPlayer(Player player) { + Channel channel = (Channel) NMSUtils.getChannel(player); + assert channel != null; + ejectChannel(channel); + } + + public void ejectChannel(Object ch) { + Channel channel = (Channel) ch; + channel.pipeline().remove(PacketEvents.getAPI().packetManager.getNettyHandlerName()); + } + + public void sendPacket(Object rawChannel, Object packet) { + Channel channel = (Channel) rawChannel; + channel.pipeline().writeAndFlush(packet); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_8.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_8.java new file mode 100644 index 0000000..7212aa1 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/netty/NettyPacketManager_8.java @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetmanager.netty; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelPromise; +import org.bukkit.entity.Player; + +final class NettyPacketManager_8 { + NettyPacketManager_8() { + + } + /** + * Inject a player with 1.8+ netty import location. + * @param player + */ + public void injectPlayer(final Player player) { + final ChannelDuplexHandler channelDuplexHandler = new ChannelDuplexHandler() { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Object packet = PacketEvents.getAPI().packetManager.read(player, ctx.channel(),msg); + if (packet == null) { + return; + } + super.channelRead(ctx, msg); + PacketEvents.getAPI().packetManager.postRead(player, packet); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + Object packet = PacketEvents.getAPI().packetManager.write(player, ctx.channel(),msg); + if (packet == null) { + return; + } + super.write(ctx, msg, promise); + PacketEvents.getAPI().packetManager.postWrite(player, packet); + } + }; + final ChannelPipeline pipeline = ((Channel) NMSUtils.getChannel(player)).pipeline(); + pipeline.addBefore("packet_handler", PacketEvents.getAPI().packetManager.getNettyHandlerName(), channelDuplexHandler); + } + + /** + * Eject a player with 1.8+ netty import location. + * @param player + */ + public void ejectPlayer(final Player player) { + final Channel channel = (Channel) NMSUtils.getChannel(player); + assert channel != null; + ejectChannel(channel); + } + + public void ejectChannel(Object channel) { + Channel ch = (Channel)channel; + ch.pipeline().remove(PacketEvents.getAPI().packetManager.getNettyHandlerName()); + } + + public void sendPacket(Object rawChannel, Object packet) { + Channel channel = (Channel)rawChannel; + channel.pipeline().writeAndFlush(packet); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/Reflection.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/Reflection.java new file mode 100644 index 0000000..b65a95a --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/Reflection.java @@ -0,0 +1,376 @@ +package io.github.retrooper.packetevents.packetmanager.tinyprotocol; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; + +/** + * An utility class that simplifies reflection in Bukkit plugins. + * + * @author Kristian + */ +public final class Reflection { + /** + * An interface for invoking a specific constructor. + */ + public interface ConstructorInvoker { + /** + * Invoke a constructor for a specific class. + * + * @param arguments - the arguments to pass to the constructor. + * @return The constructed object. + */ + Object invoke(Object... arguments); + } + + /** + * An interface for invoking a specific method. + */ + public interface MethodInvoker { + /** + * Invoke a method on a specific target object. + * + * @param target - the target object, or NULL for a static method. + * @param arguments - the arguments to pass to the method. + * @return The return value, or NULL if is void. + */ + Object invoke(Object target, Object... arguments); + } + + /** + * An interface for retrieving the field content. + * + * @param - field type. + */ + public interface FieldAccessor { + /** + * Retrieve the content of a field. + * + * @param target - the target object, or NULL for a static field. + * @return The value of the field. + */ + T get(Object target); + + /** + * Set the content of a field. + * + * @param target - the target object, or NULL for a static field. + * @param value - the new value of the field. + */ + void set(Object target, Object value); + + /** + * Determine if the given object has this field. + * + * @param target - the object to test. + * @return TRUE if it does, FALSE otherwise. + */ + boolean hasField(Object target); + } + + // Deduce the net.minecraft.server.v* package + private static String OBC_PREFIX = Bukkit.getServer().getClass().getPackage().getName(); + private static String NMS_PREFIX = OBC_PREFIX.replace("org.bukkit.craftbukkit", "net.minecraft.server"); + private static String VERSION = OBC_PREFIX.replace("org.bukkit.craftbukkit", "").replace(".", ""); + + // Variable replacement + private static Pattern MATCH_VARIABLE = Pattern.compile("\\{([^\\}]+)\\}"); + + private Reflection() { + // Seal class + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param target - the target type. + * @param name - the name of the field, or NULL to ignore. + * @param fieldType - a compatible field type. + * @return The field accessor. + */ + public static FieldAccessor getField(Class target, String name, Class fieldType) { + return getField(target, name, fieldType, 0); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param name - the name of the field, or NULL to ignore. + * @param fieldType - a compatible field type. + * @return The field accessor. + */ + public static FieldAccessor getField(String className, String name, Class fieldType) { + return getField(getClass(className), name, fieldType, 0); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param target - the target type. + * @param fieldType - a compatible field type. + * @param index - the number of compatible fields to skip. + * @return The field accessor. + */ + public static FieldAccessor getField(Class target, Class fieldType, int index) { + return getField(target, null, fieldType, index); + } + + /** + * Retrieve a field accessor for a specific field type and name. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param fieldType - a compatible field type. + * @param index - the number of compatible fields to skip. + * @return The field accessor. + */ + public static FieldAccessor getField(String className, Class fieldType, int index) { + return getField(getClass(className), fieldType, index); + } + + // Common method + private static FieldAccessor getField(Class target, String name, Class fieldType, int index) { + for (final Field field : target.getDeclaredFields()) { + if ((name == null || field.getName().equals(name)) && fieldType.isAssignableFrom(field.getType()) && index-- <= 0) { + field.setAccessible(true); + + // A function for retrieving a specific field value + return new FieldAccessor() { + + @Override + @SuppressWarnings("unchecked") + public T get(Object target) { + try { + return (T) field.get(target); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } + } + + @Override + public void set(Object target, Object value) { + try { + field.set(target, value); + } catch (IllegalAccessException e) { + throw new RuntimeException("Cannot access reflection.", e); + } + } + + @Override + public boolean hasField(Object target) { + // target instanceof DeclaringClass + return field.getDeclaringClass().isAssignableFrom(target.getClass()); + } + }; + } + } + + // Search in parent classes + if (target.getSuperclass() != null) + return getField(target.getSuperclass(), name, fieldType, index); + + throw new IllegalArgumentException("Cannot find field with type " + fieldType); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param methodName - the method name, or NULL to skip. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getMethod(String className, String methodName, Class... params) { + return getTypedMethod(getClass(className), methodName, null, params); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param methodName - the method name, or NULL to skip. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getMethod(Class clazz, String methodName, Class... params) { + return getTypedMethod(clazz, methodName, null, params); + } + + /** + * Search for the first publicly and privately defined method of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param methodName - the method name, or NULL to skip. + * @param returnType - the expected return type, or NULL to ignore. + * @param params - the expected parameters. + * @return An object that invokes this specific method. + * @throws IllegalStateException If we cannot find this method. + */ + public static MethodInvoker getTypedMethod(Class clazz, String methodName, Class returnType, Class... params) { + for (final Method method : clazz.getDeclaredMethods()) { + if ((methodName == null || method.getName().equals(methodName)) + && (returnType == null || method.getReturnType().equals(returnType)) + && Arrays.equals(method.getParameterTypes(), params)) { + method.setAccessible(true); + + return new MethodInvoker() { + + @Override + public Object invoke(Object target, Object... arguments) { + try { + return method.invoke(target, arguments); + } catch (Exception e) { + throw new RuntimeException("Cannot invoke method " + method, e); + } + } + + }; + } + } + + // Search in every superclass + if (clazz.getSuperclass() != null) + return getMethod(clazz.getSuperclass(), methodName, params); + + throw new IllegalStateException(String.format("Unable to find method %s (%s).", methodName, Arrays.asList(params))); + } + + /** + * Search for the first publically and privately defined constructor of the given name and parameter count. + * + * @param className - lookup name of the class, see {@link #getClass(String)}. + * @param params - the expected parameters. + * @return An object that invokes this constructor. + * @throws IllegalStateException If we cannot find this method. + */ + public static ConstructorInvoker getConstructor(String className, Class... params) { + return getConstructor(getClass(className), params); + } + + /** + * Search for the first publically and privately defined constructor of the given name and parameter count. + * + * @param clazz - a class to start with. + * @param params - the expected parameters. + * @return An object that invokes this constructor. + * @throws IllegalStateException If we cannot find this method. + */ + public static ConstructorInvoker getConstructor(Class clazz, Class... params) { + for (final Constructor constructor : clazz.getDeclaredConstructors()) { + if (Arrays.equals(constructor.getParameterTypes(), params)) { + constructor.setAccessible(true); + + return new ConstructorInvoker() { + + @Override + public Object invoke(Object... arguments) { + try { + return constructor.newInstance(arguments); + } catch (Exception e) { + throw new RuntimeException("Cannot invoke constructor " + constructor, e); + } + } + + }; + } + } + + throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params))); + } + + /** + * Retrieve a class from its full name, without knowing its type on compile time. + *

+ * This is useful when looking up fields by a NMS or OBC type. + *

+ * + * See {@link #getClass()} for more information. + * @param lookupName - the class name with variables. + * @return The class. + */ + public static Class getUntypedClass(String lookupName) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + Class clazz = (Class) getClass(lookupName); + return clazz; + } + + + public static Class getClass(String lookupName) { + return getCanonicalClass(expandVariables(lookupName)); + } + + /** + * Retrieve a class in the net.minecraft.server.VERSION.* package. + * + * @param name - the name of the class, excluding the package. + * @throws IllegalArgumentException If the class doesn't exist. + */ + public static Class getMinecraftClass(String name) { + return getCanonicalClass(NMS_PREFIX + "." + name); + } + + /** + * Retrieve a class in the org.bukkit.craftbukkit.VERSION.* package. + * + * @param name - the name of the class, excluding the package. + * @throws IllegalArgumentException If the class doesn't exist. + */ + public static Class getCraftBukkitClass(String name) { + return getCanonicalClass(OBC_PREFIX + "." + name); + } + + /** + * Retrieve a class by its canonical name. + * + * @param canonicalName - the canonical name. + * @return The class. + */ + private static Class getCanonicalClass(String canonicalName) { + try { + return Class.forName(canonicalName); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Cannot find " + canonicalName, e); + } + } + + /** + * Expand variables such as "{nms}" and "{obc}" to their corresponding packages. + * + * @param name - the full name of the class. + * @return The expanded string. + */ + private static String expandVariables(String name) { + StringBuffer output = new StringBuffer(); + Matcher matcher = MATCH_VARIABLE.matcher(name); + + while (matcher.find()) { + String variable = matcher.group(1); + String replacement = ""; + + // Expand all detected variables + if ("nms".equalsIgnoreCase(variable)) + replacement = NMS_PREFIX; + else if ("obc".equalsIgnoreCase(variable)) + replacement = OBC_PREFIX; + else if ("version".equalsIgnoreCase(variable)) + replacement = VERSION; + else + throw new IllegalArgumentException("Unknown variable: " + variable); + + // Assume the expanded variables are all packages, and append a dot + if (replacement.length() > 0 && matcher.end() < name.length() && name.charAt(matcher.end()) != '.') + replacement += "."; + matcher.appendReplacement(output, Matcher.quoteReplacement(replacement)); + } + + matcher.appendTail(output); + return output.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol.java new file mode 100644 index 0000000..ac21e0f --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol.java @@ -0,0 +1,135 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetmanager.tinyprotocol; + +import io.github.retrooper.packetevents.packetmanager.netty.NettyPacketManager; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class TinyProtocol { + private final Object tinyProt; + + public TinyProtocol(Plugin plugin) { + if (NettyPacketManager.v1_7_nettyMode) { + tinyProt = new TinyProtocol7(plugin); + } else { + tinyProt = new TinyProtocol8(plugin); + } + } + + public void injectPlayer(Player player) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.injectPlayer(player); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.injectPlayer(player); + } + } + + public void injectPlayerAsync(Player player) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.injectPlayerAsync(player); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.injectPlayerAsync(player); + } + } + + public void ejectPlayer(Player player) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.uninjectPlayer(player); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.uninjectPlayer(player); + } + } + + public void ejectPlayerAsync(Player player) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.uninjectPlayerAsync(player); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.uninjectPlayerAsync(player); + } + } + + public void ejectChannelSync(Object channel) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.ejectChannelSync(channel); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.ejectChannelSync(channel); + } + } + + public void ejectChannelAsync(Object channel) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.ejectChannelAsync(channel); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.ejectChannelAsync(channel); + } + } + + public void sendPacket(Object channel, Object packet) { + if (!NettyPacketManager.v1_7_nettyMode) { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.sendPacket(channel, packet); + } else { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.sendPacket(channel, packet); + } + } + + public Object getChannel(Player player) { + if (NettyPacketManager.v1_7_nettyMode) { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + return tp7.getChannel(player); + } else { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + return tp8.getChannel(player); + } + } + + public void unregisterChannelHandler() { + + } + + public void handleQueuedKicks() { + if (NettyPacketManager.v1_7_nettyMode) { + TinyProtocol7 tp7 = (TinyProtocol7) tinyProt; + tp7.handleQueuedKicks(); + } else { + TinyProtocol8 tp8 = (TinyProtocol8) tinyProt; + tp8.handleQueuedKicks(); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol7.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol7.java new file mode 100644 index 0000000..01b55a5 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol7.java @@ -0,0 +1,514 @@ +package io.github.retrooper.packetevents.packetmanager.tinyprotocol; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.packetmanager.tinyprotocol.Reflection.FieldAccessor; +import io.github.retrooper.packetevents.packetmanager.tinyprotocol.Reflection.MethodInvoker; +import io.github.retrooper.packetevents.packetwrappers.out.kickdisconnect.WrappedPacketOutKickDisconnect; +import net.minecraft.util.com.google.common.collect.Lists; +import net.minecraft.util.com.google.common.collect.MapMaker; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.io.netty.channel.*; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + +/** + * Represents a very tiny alternative to ProtocolLib. + *

+ * It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)! + * + * @author Kristian + */ + +//BROKEN: NPEs when player joins while server is starting, and using PlayerJoinEvent instead will cause NPE for protocol version lookup. +//Protocol version number lookup method from DeprecatedLuke's version of TinyProtocol +public class TinyProtocol7 { + private static final AtomicInteger ID = new AtomicInteger(); + + // Used in order to lookup a channel + private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle"); + private static final FieldAccessor getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class); + private static final FieldAccessor getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class); + private static final FieldAccessor getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0); + + // Looking up ServerConnection + private static final Class minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer"); + private static final Class serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection"); + private static final FieldAccessor getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0); + private static final FieldAccessor getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0); + private static final MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass); + + // Packets we have to intercept + private static final Class PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart"); + private static final Class PACKET_HANDSHAKING_IN_SET_PROTOCOL = Reflection.getMinecraftClass("PacketHandshakingInSetProtocol"); + private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0); + + // Speedup channel/protocol lookup + private final Map channelLookup = new MapMaker().weakValues().makeMap(); + + // List of network markers + private List networkManagers; + + // Injected channel handlers + private final List serverChannels = Lists.newArrayList(); + private ChannelInboundHandlerAdapter serverChannelHandler; + private ChannelInitializer beginInitProtocol; + private ChannelInitializer endInitProtocol; + + public final ConcurrentLinkedQueue queueingChannelKicks = new ConcurrentLinkedQueue<>(); + + // Current handler name + private final String handlerName; + + protected volatile boolean closed; + protected Plugin plugin; + + /** + * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients. + *

+ * You can construct multiple instances per plugin. + * + * @param plugin - the plugin. + */ + public TinyProtocol7(final Plugin plugin) { + this.plugin = plugin; + + // Compute handler name + this.handlerName = getHandlerName(); + + try { + registerChannelHandler(); + registerPlayers(plugin); + } catch (IllegalArgumentException ex) { + // Damn you, late bind + plugin.getLogger().info("Delaying server channel injection due to late bind."); + + new BukkitRunnable() { + @Override + public void run() { + registerChannelHandler(); + registerPlayers(plugin); + plugin.getLogger().info("Late bind injection successful."); + } + }.runTask(plugin); + } + } + + private void createServerChannelHandler() { + // Handle connected channels + endInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + try { + // This can take a while, so we need to stop the main thread from interfering + synchronized (networkManagers) { + // Stop injecting channels + if (!closed) { + channel.eventLoop().submit(() -> injectChannelInternal(channel)); + } + } + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e); + } + } + + }; + + // This is executed before Minecraft's channel handler + beginInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(endInitProtocol); + } + + }; + + serverChannelHandler = new ChannelInboundHandlerAdapter() { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel channel = (Channel) msg; + + // Prepare to initialize ths channel + channel.pipeline().addFirst(beginInitProtocol); + ctx.fireChannelRead(msg); + } + + }; + } + + + @SuppressWarnings("unchecked") + private void registerChannelHandler() { + Object mcServer = getMinecraftServer.get(Bukkit.getServer()); + Object serverConnection = getServerConnection.get(mcServer); + boolean looking = true; + + // We need to synchronize against this list + networkManagers = (List) getNetworkMarkers.invoke(null, serverConnection); + createServerChannelHandler(); + + // Find the correct list, or implicitly throw an exception + for (int i = 0; looking; i++) { + List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); + + for (Object item : list) { + if (!(item instanceof ChannelFuture)) + break; + + // Channel future that contains the server connection + Channel serverChannel = ((ChannelFuture) item).channel(); + + serverChannels.add(serverChannel); + serverChannel.pipeline().addFirst(serverChannelHandler); + looking = false; + } + } + } + + private void unregisterChannelHandler() { + if (serverChannelHandler == null) + return; + + for (Channel serverChannel : serverChannels) { + final ChannelPipeline pipeline = serverChannel.pipeline(); + + // Remove channel handler + serverChannel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + try { + pipeline.remove(serverChannelHandler); + } catch (NoSuchElementException e) { + // That's fine + } + } + + }); + } + } + + public void handleQueuedKicks() { + for (Channel channel : queueingChannelKicks) { + Object packet = new WrappedPacketOutKickDisconnect("We failed to inject you. Please try rejoining!").asNMSPacket(); + sendPacket(channel, packet); + } + } + + private void registerPlayers(Plugin plugin) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + injectPlayer(player); + } + } + + /** + * Invoked when the server is starting to send a packet to a player. + *

+ * Note that this is not executed on the main thread. + * + * @param receiver - the receiving player, NULL for early login/status packets. + * @param channel - the channel that received the packet. Never NULL. + * @param packet - the packet being sent. + * @return The packet to send instead, or NULL to cancel the transmission. + */ + public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { + return PacketEvents.getAPI().packetManager.write(receiver, channel, packet); + } + + /** + * Invoked when the server has received a packet from a given player. + *

+ * Use {@link Channel#remoteAddress()} to get the remote address of the client. + * + * @param sender - the player that sent the packet, NULL for early login/status packets. + * @param channel - channel that received the packet. Never NULL. + * @param packet - the packet being received. + * @return The packet to recieve instead, or NULL to cancel. + */ + public Object onPacketInAsync(Player sender, Channel channel, Object packet) { + return PacketEvents.getAPI().packetManager.read(sender, channel, packet); + } + + /** + * Send a packet to a particular player. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the destination player. + * @param packet - the packet to send. + */ + public void sendPacket(Player player, Object packet) { + sendPacket(getChannel(player), packet); + } + + /** + * Send a packet to a particular client. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet to send. + */ + public void sendPacket(Channel channel, Object packet) { + channel.pipeline().writeAndFlush(packet); + } + + public void sendPacket(Object channel, Object packet) { + sendPacket((Channel) channel, packet); + } + + /** + * Pretend that a given packet has been received from a player. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the player that sent the packet. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Player player, Object packet) { + receivePacket(getChannel(player), packet); + } + + /** + * Pretend that a given packet has been received from a given client. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Channel channel, Object packet) { + channel.pipeline().context("encoder").fireChannelRead(packet); + } + + /** + * Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID. + *

+ * Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances. + * + * @return A unique channel handler name. + */ + protected String getHandlerName() { + return "PacketEvents-" + ID.incrementAndGet(); + } + + /** + * Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets. + *

+ * This will automatically be called when a player has logged in. + * + * @param player - the player to inject. + */ + public void injectPlayer(Player player) { + injectChannelInternal(getChannel(player)).player = player; + } + + public void injectPlayerAsync(Player player) { + Channel channel = getChannel(player); + injectChannelInternalAsync(channel).player = player; + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + */ + public void injectChannel(Channel channel) { + injectChannelInternal(channel); + } + + public void injectChannelAsync(Channel channel) { + injectChannelInternalAsync(channel); + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The packet interceptor. + */ + private PacketInterceptor injectChannelInternal(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + channel.pipeline().addBefore("packet_handler", handlerName, interceptor); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + private PacketInterceptor injectChannelInternalAsync(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + final PacketInterceptor pi = interceptor; + PacketEvents.packetHandlingExecutorService.execute(new Runnable() { + @Override + public void run() { + try { + channel.pipeline().addBefore("packet_handler", handlerName, pi); + } catch (Exception ex) { + //kick them + Object packet = new WrappedPacketOutKickDisconnect("We unfortunately failed to inject you. Please try rejoining!").asNMSPacket(); + sendPacket(channel, packet); + } + } + }); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + /** + * Retrieve the Netty channel associated with a player. This is cached. + * + * @param player - the player. + * @return The Netty channel. + */ + public Channel getChannel(Player player) { + Channel channel = channelLookup.get(player.getName()); + + // Lookup channel again + if (channel == null) { + Object connection = getConnection.get(getPlayerHandle.invoke(player)); + Object manager = getManager.get(connection); + + channelLookup.put(player.getName(), channel = getChannel.get(manager)); + } + + return channel; + } + + /** + * Uninject a specific player. + * + * @param player - the injected player. + */ + public void uninjectPlayer(Player player) { + uninjectChannel(getChannel(player)); + } + + public void uninjectPlayerAsync(Player player) { + Channel channel = getChannel(player); + uninjectChannelAsync(channel); + } + + public void ejectChannelSync(Object ch) { + uninjectChannel((Channel) ch); + } + + public void ejectChannelAsync(Object ch) { + uninjectChannelAsync((Channel) ch); + } + + /** + * Uninject a specific channel. + *

+ * This will also disable the automatic channel injection that occurs when a player has properly logged in. + * + * @param channel - the injected channel. + */ + public void uninjectChannel(final Channel channel) { + // See ChannelInjector in ProtocolLib, line 590 + channel.pipeline().remove(handlerName); + } + + public void uninjectChannelAsync(Channel channel) { + // See ChannelInjector in ProtocolLib, line 590 + PacketEvents.packetHandlingExecutorService.execute(new Runnable() { + @Override + public void run() { + channel.pipeline().remove(handlerName); + } + }); + } + + /** + * Determine if the given player has been injected by TinyProtocol. + * + * @param player - the player. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Player player) { + return hasInjected(getChannel(player)); + } + + /** + * Determine if the given channel has been injected by TinyProtocol. + * + * @param channel - the channel. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Channel channel) { + return channel.pipeline().get(handlerName) != null; + } + + /** + * Cease listening for packets. This is called automatically when your plugin is disabled. + */ + + /** + * Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets. + * + * @author Kristian + */ + private final class PacketInterceptor extends ChannelDuplexHandler { + // Updated by the login event + public volatile Player player; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // Intercept channel + final Channel channel = ctx.channel(); + handleLoginStart(channel, msg); + + msg = onPacketInAsync(player, channel, msg); + + if (msg != null) { + super.channelRead(ctx, msg); + PacketEvents.getAPI().packetManager.postRead(player, msg); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + msg = onPacketOutAsync(player, ctx.channel(), msg); + if (msg != null) { + super.write(ctx, msg, promise); + PacketEvents.getAPI().packetManager.postWrite(player, msg); + } + } + + private void handleLoginStart(Channel channel, Object packet) { + if (PACKET_LOGIN_IN_START.isInstance(packet)) { + GameProfile profile = getGameProfile.get(packet); + channelLookup.put(profile.getName(), channel); + } + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol8.java b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol8.java new file mode 100644 index 0000000..c5badf2 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetmanager/tinyprotocol/TinyProtocol8.java @@ -0,0 +1,547 @@ +package io.github.retrooper.packetevents.packetmanager.tinyprotocol; + +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import com.mojang.authlib.GameProfile; +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.packetmanager.tinyprotocol.Reflection.FieldAccessor; +import io.github.retrooper.packetevents.packetmanager.tinyprotocol.Reflection.MethodInvoker; +import io.github.retrooper.packetevents.packetwrappers.out.kickdisconnect.WrappedPacketOutKickDisconnect; +import io.netty.channel.*; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + +/** + * Represents a very tiny alternative to ProtocolLib. + *

+ * It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)! + * + * @author Kristian + */ + +//BROKEN: NPEs when player joins while server is starting, and using PlayerJoinEvent instead will cause NPE for protocol version lookup. +//Protocol version number lookup method from DeprecatedLuke's version of TinyProtocol +public class TinyProtocol8 { + private static final AtomicInteger ID = new AtomicInteger(); + + // Used in order to lookup a channel + private static final MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle"); + private static final FieldAccessor getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class); + private static final FieldAccessor getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class); + private static final FieldAccessor getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0); + + // Looking up ServerConnection + private static final Class minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer"); + private static final Class serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection"); + private static final FieldAccessor getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0); + private static final FieldAccessor getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0); + private static final MethodInvoker getNetworkMarkers; + + // Packets we have to intercept + private static final Class PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart"); + private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0); + + static { + MethodInvoker tempNetworkMarkers; + try { + tempNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass); + } catch (Exception ex) { + tempNetworkMarkers = null; + } + getNetworkMarkers = tempNetworkMarkers; + } + + // Speedup channel/protocol lookup + private final Map channelLookup = new MapMaker().weakValues().makeMap(); + + // List of network markers + private List networkManagers; + + // Injected channel handlers + private final List serverChannels = Lists.newArrayList(); + private ChannelInboundHandlerAdapter serverChannelHandler; + private ChannelInitializer beginInitProtocol; + private ChannelInitializer endInitProtocol; + + public final ConcurrentLinkedQueue queueingChannelKicks = new ConcurrentLinkedQueue<>(); + + // Current handler name + private final String handlerName; + + protected volatile boolean closed; + protected Plugin plugin; + + /** + * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients. + *

+ * You can construct multiple instances per plugin. + * + * @param plugin - the plugin. + */ + public TinyProtocol8(final Plugin plugin) { + this.plugin = plugin; + + // Compute handler name + this.handlerName = getHandlerName(); + + try { + registerChannelHandler(); + registerPlayers(plugin); + } catch (IllegalArgumentException ex) { + // Damn you, late bind + plugin.getLogger().info("Delaying server channel injection due to late bind."); + + new BukkitRunnable() { + @Override + public void run() { + registerChannelHandler(); + registerPlayers(plugin); + plugin.getLogger().info("Late bind injection successful."); + } + }.runTask(plugin); + } + } + + private void createServerChannelHandler() { + // Handle connected channels + endInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + try { + // This can take a while, so we need to stop the main thread from interfering + synchronized (networkManagers) { + // Stop injecting channels + if (!closed) { + channel.eventLoop().submit(() -> injectChannelInternal(channel)); + } + } + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Cannot inject incoming channel " + channel, e); + } + } + + }; + + // This is executed before Minecraft's channel handler + beginInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(endInitProtocol); + } + + }; + + serverChannelHandler = new ChannelInboundHandlerAdapter() { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel channel = (Channel) msg; + + // Prepare to initialize ths channel + channel.pipeline().addFirst(beginInitProtocol); + ctx.fireChannelRead(msg); + } + + }; + } + + /** + * Register bukkit events. + */ + + @SuppressWarnings("unchecked") + private void registerChannelHandler() { + Object mcServer = getMinecraftServer.get(Bukkit.getServer()); + Object serverConnection = getServerConnection.get(mcServer); + boolean looking = true; + + // We need to synchronize against this list + if (getNetworkMarkers != null) { + networkManagers = (List) getNetworkMarkers.invoke(null, serverConnection); + } else { + networkManagers = new ArrayList<>(); + } + createServerChannelHandler(); + + // Find the correct list, or implicitly throw an exception + for (int i = 0; looking; i++) { + List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); + + for (Object item : list) { + if (!(item instanceof ChannelFuture)) + break; + + // Channel future that contains the server connection + Channel serverChannel = ((ChannelFuture) item).channel(); + + serverChannels.add(serverChannel); + serverChannel.pipeline().addFirst(serverChannelHandler); + looking = false; + } + } + } + + private void unregisterChannelHandler() { + if (serverChannelHandler == null) + return; + + for (Channel serverChannel : serverChannels) { + final ChannelPipeline pipeline = serverChannel.pipeline(); + + // Remove channel handler + serverChannel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + try { + pipeline.remove(serverChannelHandler); + } catch (NoSuchElementException e) { + // That's fine + } + } + + }); + } + } + + private void registerPlayers(Plugin plugin) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + injectPlayer(player); + } + } + + /** + * Invoked when the server is starting to send a packet to a player. + *

+ * Note that this is not executed on the main thread. + * + * @param receiver - the receiving player, NULL for early login/status packets. + * @param channel - the channel that received the packet. Never NULL. + * @param packet - the packet being sent. + * @return The packet to send instead, or NULL to cancel the transmission. + */ + public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { + return PacketEvents.getAPI().packetManager.write(receiver, channel, packet); + } + + /** + * Invoked when the server has received a packet from a given player. + *

+ * Use {@link Channel#remoteAddress()} to get the remote address of the client. + * + * @param sender - the player that sent the packet, NULL for early login/status packets. + * @param channel - channel that received the packet. Never NULL. + * @param packet - the packet being received. + * @return The packet to recieve instead, or NULL to cancel. + */ + public Object onPacketInAsync(Player sender, Channel channel, Object packet) { + return PacketEvents.getAPI().packetManager.read(sender, channel, packet); + } + + /** + * Send a packet to a particular player. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the destination player. + * @param packet - the packet to send. + */ + public void sendPacket(Player player, Object packet) { + sendPacket(getChannel(player), packet); + } + + /** + * Send a packet to a particular client. + *

+ * Note that {@link #onPacketOutAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet to send. + */ + public void sendPacket(Channel channel, Object packet) { + channel.pipeline().writeAndFlush(packet); + } + + public void sendPacket(Object channel, Object packet) { + sendPacket((Channel) channel, packet); + } + + /** + * Pretend that a given packet has been received from a player. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param player - the player that sent the packet. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Player player, Object packet) { + receivePacket(getChannel(player), packet); + } + + /** + * Pretend that a given packet has been received from a given client. + *

+ * Note that {@link #onPacketInAsync(Player, Channel, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Channel channel, Object packet) { + channel.pipeline().context("encoder").fireChannelRead(packet); + } + + /** + * Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID. + *

+ * Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances. + * + * @return A unique channel handler name. + */ + protected String getHandlerName() { + return "PacketEvents-" + ID.incrementAndGet(); + } + + /** + * Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets. + *

+ * This will automatically be called when a player has logged in. + * + * @param player - the player to inject. + */ + public void injectPlayer(Player player) { + injectChannelInternal(getChannel(player)).player = player; + } + + public void injectPlayerAsync(Player player) { + Channel channel = getChannel(player); + injectChannelInternalAsync(channel).player = player; + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + */ + public void injectChannel(Channel channel) { + injectChannelInternal(channel); + } + + public void injectChannelAsync(Channel channel) { + injectChannelInternalAsync(channel); + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The packet interceptor. + */ + private PacketInterceptor injectChannelInternal(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + channel.pipeline().addBefore("packet_handler", handlerName, interceptor); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + private PacketInterceptor injectChannelInternalAsync(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + final PacketInterceptor pi = interceptor; + PacketEvents.packetHandlingExecutorService.execute(new Runnable() { + @Override + public void run() { + try { + channel.pipeline().addBefore("packet_handler", handlerName, pi); + } + catch(Exception ex) { + //kick them + Object packet = new WrappedPacketOutKickDisconnect("We unfortunately failed to inject you. Please try rejoining!").asNMSPacket(); + sendPacket(channel, packet); + } + } + }); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + /** + * Retrieve the Netty channel associated with a player. This is cached. + * + * @param player - the player. + * @return The Netty channel. + */ + public Channel getChannel(Player player) { + Channel channel = channelLookup.get(player.getName()); + + // Lookup channel again + if (channel == null) { + Object connection = getConnection.get(getPlayerHandle.invoke(player)); + Object manager = getManager.get(connection); + + channelLookup.put(player.getName(), channel = getChannel.get(manager)); + } + + return channel; + } + + /** + * Uninject a specific player. + * + * @param player - the injected player. + */ + public void uninjectPlayer(Player player) { + uninjectChannel(getChannel(player)); + } + + public void uninjectPlayerAsync(Player player) { + Channel channel = getChannel(player); + uninjectChannelAsync(channel); + } + + public void ejectChannelSync(Object ch) { + uninjectChannel((Channel) ch); + } + + public void ejectChannelAsync(Object ch) { + uninjectChannelAsync((Channel) ch); + } + + /** + * Uninject a specific channel. + *

+ * This will also disable the automatic channel injection that occurs when a player has properly logged in. + * + * @param channel - the injected channel. + */ + public void uninjectChannel(final Channel channel) { + // See ChannelInjector in ProtocolLib, line 590 + channel.pipeline().remove(handlerName); + } + + public void uninjectChannelAsync(Channel channel) { + // See ChannelInjector in ProtocolLib, line 590 + PacketEvents.packetHandlingExecutorService.execute(new Runnable() { + @Override + public void run() { + channel.pipeline().remove(handlerName); + } + }); + + } + + /** + * Determine if the given player has been injected by TinyProtocol. + * + * @param player - the player. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Player player) { + return hasInjected(getChannel(player)); + } + + /** + * Determine if the given channel has been injected by TinyProtocol. + * + * @param channel - the channel. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Channel channel) { + return channel.pipeline().get(handlerName) != null; + } + + /** + * Cease listening for packets. This is called automatically when your plugin is disabled. + */ + public final void close() { + if (!closed) { + closed = true; + + // Remove our handlers + for (Player player : plugin.getServer().getOnlinePlayers()) { + uninjectPlayer(player); + } + + // Clean up Bukkit + unregisterChannelHandler(); + } + } + + public void handleQueuedKicks() { + for(Channel channel : queueingChannelKicks) { + Object packet = new WrappedPacketOutKickDisconnect("We failed to inject you. Please try rejoining!").asNMSPacket(); + sendPacket(channel, packet); + } + } + + /** + * Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets. + * + * @author Kristian + */ + private final class PacketInterceptor extends ChannelDuplexHandler { + // Updated by the login event + public volatile Player player; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // Intercept channel + final Channel channel = ctx.channel(); + handleLoginStart(channel, msg); + + msg = onPacketInAsync(player, channel, msg); + + if (msg != null) { + super.channelRead(ctx, msg); + PacketEvents.getAPI().packetManager.postRead(player, msg); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + msg = onPacketOutAsync(player, ctx.channel(), msg); + + if (msg != null) { + super.write(ctx, msg, promise); + PacketEvents.getAPI().packetManager.postWrite(player, msg); + } + } + + private void handleLoginStart(Channel channel, Object packet) { + if (PACKET_LOGIN_IN_START.isInstance(packet)) { + GameProfile profile = getGameProfile.get(packet); + channelLookup.put(profile.getName(), channel); + } + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packettype/PacketType.java b/src/main/java/io/github/retrooper/packetevents/packettype/PacketType.java new file mode 100644 index 0000000..7b4a134 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packettype/PacketType.java @@ -0,0 +1,283 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packettype; + +import java.util.HashMap; +import java.util.Map; + +public class PacketType { + public static class Status { + public static final Map, Byte> packetIds = new HashMap<>(); + public static final byte IN_START = 0, PING = 1, PONG = 2, SERVER_INFO = 3; + + public static void init() { + packetIds.put(PacketTypeClasses.Status.IN_START, IN_START); + packetIds.put(PacketTypeClasses.Status.PING, PING); + packetIds.put(PacketTypeClasses.Status.PONG, PONG); + packetIds.put(PacketTypeClasses.Status.SERVER_INFO, SERVER_INFO); + } + } + + public static class Login { + public static final Map, Byte> packetIds = new HashMap<>(); + public static final byte HANDSHAKE = 0, + IN_CUSTOM_PAYLOAD = 1, OUT_CUSTOM_PAYLOAD = 2, IN_START = 3, IN_ENCRYPTION_BEGIN = 4, + DISCONNECT = 5, OUT_ENCRYPTION_BEGIN = 6, OUT_SUCCESS = 7; + + public static void init() { + packetIds.put(PacketTypeClasses.Login.HANDSHAKE, HANDSHAKE); + packetIds.put(PacketTypeClasses.Login.IN_CUSTOM_PAYLOAD, IN_CUSTOM_PAYLOAD); + packetIds.put(PacketTypeClasses.Login.OUT_CUSTOM_PAYLOAD, OUT_CUSTOM_PAYLOAD); + packetIds.put(PacketTypeClasses.Login.IN_START, IN_START); + packetIds.put(PacketTypeClasses.Login.IN_ENCRYPTION_BEGIN, IN_ENCRYPTION_BEGIN); + packetIds.put(PacketTypeClasses.Login.DISCONNECT, DISCONNECT); + packetIds.put(PacketTypeClasses.Login.OUT_ENCRYPTION_BEGIN, OUT_ENCRYPTION_BEGIN); + packetIds.put(PacketTypeClasses.Login.OUT_SUCCESS, OUT_SUCCESS); + } + } + + public static class Client { + public static final Map, Byte> packetIds = new HashMap<>(); + public static final byte TELEPORT_ACCEPT = 0, + TILE_NBT_QUERY = 1, DIFFICULTY_CHANGE = 2, CHAT = 3, CLIENT_COMMAND = 4, + SETTINGS = 5, TAB_COMPLETE = 6, TRANSACTION = 7, ENCHANT_ITEM = 8, + WINDOW_CLICK = 9, CLOSE_WINDOW = 10, CUSTOM_PAYLOAD = 11, B_EDIT = 12, + ENTITY_NBT_QUERY = 13, USE_ENTITY = 14, JIGSAW_GENERATE = 15, KEEP_ALIVE = 16, + DIFFICULTY_LOCK = 17, POSITION = 18, POSITION_LOOK = 19, LOOK = 20, + FLYING = 21, VEHICLE_MOVE = 22, BOAT_MOVE = 23, PICK_ITEM = 24, + AUTO_RECIPE = 25, ABILITIES = 26, BLOCK_DIG = 27, ENTITY_ACTION = 28, + STEER_VEHICLE = 29, RECIPE_DISPLAYED = 30, ITEM_NAME = 31, RESOURCE_PACK_STATUS = 32, + ADVANCEMENTS = 33, TR_SEL = 34, BEACON = 35, HELD_ITEM_SLOT = 36, + SET_COMMAND_BLOCK = 37, SET_COMMAND_MINECART = 38, SET_CREATIVE_SLOT = 39, SET_JIGSAW = 40, + STRUCT = 41, UPDATE_SIGN = 42, ARM_ANIMATION = 43, SPECTATE = 44, + USE_ITEM = 45, BLOCK_PLACE = 46; + + public static void init() { + packetIds.put(PacketTypeClasses.Client.TELEPORT_ACCEPT, TELEPORT_ACCEPT); + packetIds.put(PacketTypeClasses.Client.TILE_NBT_QUERY, TILE_NBT_QUERY); + packetIds.put(PacketTypeClasses.Client.DIFFICULTY_CHANGE, DIFFICULTY_CHANGE); + packetIds.put(PacketTypeClasses.Client.CHAT, CHAT); + packetIds.put(PacketTypeClasses.Client.CLIENT_COMMAND, CLIENT_COMMAND); + packetIds.put(PacketTypeClasses.Client.SETTINGS, SETTINGS); + packetIds.put(PacketTypeClasses.Client.TAB_COMPLETE, TAB_COMPLETE); + packetIds.put(PacketTypeClasses.Client.TRANSACTION, TRANSACTION); + packetIds.put(PacketTypeClasses.Client.ENCHANT_ITEM, ENCHANT_ITEM); + packetIds.put(PacketTypeClasses.Client.WINDOW_CLICK, WINDOW_CLICK); + packetIds.put(PacketTypeClasses.Client.CLOSE_WINDOW, CLOSE_WINDOW); + packetIds.put(PacketTypeClasses.Client.CUSTOM_PAYLOAD, CUSTOM_PAYLOAD); + packetIds.put(PacketTypeClasses.Client.B_EDIT, B_EDIT); + packetIds.put(PacketTypeClasses.Client.ENTITY_NBT_QUERY, ENTITY_NBT_QUERY); + packetIds.put(PacketTypeClasses.Client.USE_ENTITY, USE_ENTITY); + packetIds.put(PacketTypeClasses.Client.JIGSAW_GENERATE, JIGSAW_GENERATE); + packetIds.put(PacketTypeClasses.Client.KEEP_ALIVE, KEEP_ALIVE); + packetIds.put(PacketTypeClasses.Client.DIFFICULTY_LOCK, DIFFICULTY_LOCK); + packetIds.put(PacketTypeClasses.Client.POSITION, POSITION); + packetIds.put(PacketTypeClasses.Client.POSITION_LOOK, POSITION_LOOK); + packetIds.put(PacketTypeClasses.Client.LOOK, LOOK); + packetIds.put(PacketTypeClasses.Client.FLYING, FLYING); + packetIds.put(PacketTypeClasses.Client.VEHICLE_MOVE, VEHICLE_MOVE); + packetIds.put(PacketTypeClasses.Client.BOAT_MOVE, BOAT_MOVE); + packetIds.put(PacketTypeClasses.Client.PICK_ITEM, PICK_ITEM); + packetIds.put(PacketTypeClasses.Client.AUTO_RECIPE, AUTO_RECIPE); + packetIds.put(PacketTypeClasses.Client.ABILITIES, ABILITIES); + packetIds.put(PacketTypeClasses.Client.BLOCK_DIG, BLOCK_DIG); + packetIds.put(PacketTypeClasses.Client.ENTITY_ACTION, ENTITY_ACTION); + packetIds.put(PacketTypeClasses.Client.STEER_VEHICLE, STEER_VEHICLE); + packetIds.put(PacketTypeClasses.Client.RECIPE_DISPLAYED, RECIPE_DISPLAYED); + packetIds.put(PacketTypeClasses.Client.ITEM_NAME, ITEM_NAME); + packetIds.put(PacketTypeClasses.Client.RESOURCE_PACK_STATUS, RESOURCE_PACK_STATUS); + packetIds.put(PacketTypeClasses.Client.ADVANCEMENTS, ADVANCEMENTS); + packetIds.put(PacketTypeClasses.Client.TR_SEL, TR_SEL); + packetIds.put(PacketTypeClasses.Client.BEACON, BEACON); + packetIds.put(PacketTypeClasses.Client.HELD_ITEM_SLOT, HELD_ITEM_SLOT); + packetIds.put(PacketTypeClasses.Client.SET_COMMAND_BLOCK, SET_COMMAND_BLOCK); + packetIds.put(PacketTypeClasses.Client.SET_COMMAND_MINECART, SET_COMMAND_MINECART); + packetIds.put(PacketTypeClasses.Client.SET_CREATIVE_SLOT, SET_CREATIVE_SLOT); + packetIds.put(PacketTypeClasses.Client.SET_JIGSAW, SET_JIGSAW); + packetIds.put(PacketTypeClasses.Client.STRUCT, STRUCT); + packetIds.put(PacketTypeClasses.Client.UPDATE_SIGN, UPDATE_SIGN); + packetIds.put(PacketTypeClasses.Client.ARM_ANIMATION, ARM_ANIMATION); + packetIds.put(PacketTypeClasses.Client.SPECTATE, SPECTATE); + packetIds.put(PacketTypeClasses.Client.USE_ITEM, USE_ITEM); + packetIds.put(PacketTypeClasses.Client.BLOCK_PLACE, BLOCK_PLACE); + } + + public static class Util { + /** + * Is the packet an instance of the PacketPlayInFlying packet? + * + * @param packetID + * @return packetID == FLYING or POSITION or POSITION_LOOK or LOOK + */ + public static boolean isInstanceOfFlying(final byte packetID) { + return packetID == FLYING + || packetID == POSITION + || packetID == POSITION_LOOK + || packetID == LOOK; + } + } + } + + public static class Server { + public static final Map, Byte> packetIds = new HashMap<>(); + public static final byte SPAWN_ENTITY = 0, SPAWN_ENTITY_EXPERIENCE_ORB = 1, SPAWN_ENTITY_WEATHER = 2, SPAWN_ENTITY_LIVING = 3, + SPAWN_ENTITY_PAINTING = 4, SPAWN_ENTITY_SPAWN = 5, ANIMATION = 6, STATISTIC = 7, + BLOCK_BREAK = 8, BLOCK_BREAK_ANIMATION = 9, TILE_ENTITY_DATA = 10, BLOCK_ACTION = 11, + BLOCK_CHANGE = 12, BOSS = 13, SERVER_DIFFICULTY = 14, CHAT = 15, MULTI_BLOCK_CHANGE = 16, + TAB_COMPLETE = 17, COMMANDS = 18, TRANSACTION = 19, CLOSE_WINDOW = 20, + WINDOW_ITEMS = 21, WINDOW_DATA = 22, SET_SLOT = 23, SET_COOLDOWN = 24, + CUSTOM_PAYLOAD = 25, CUSTOM_SOUND_EFFECT = 26, KICK_DISCONNECT = 27, ENTITY_STATUS = 28, + EXPLOSION = 29, UNLOAD_CHUNK = 30, GAME_STATE_CHANGE = 31, OPEN_WINDOW_HORSE = 32, + KEEP_ALIVE = 33, MAP_CHUNK = 34, WORLD_EVENT = 35, WORLD_PARTICLES = 36, + LIGHT_UPDATE = 37, LOGIN = 38, MAP = 39, OPEN_WINDOW_MERCHANT = 40, + REL_ENTITY_MOVE = 41, REL_ENTITY_MOVE_LOOK = 42, ENTITY_LOOK = 43, ENTITY = 44, + VEHICLE_MOVE = 45, OPEN_BOOK = 46, OPEN_WINDOW = 47, OPEN_SIGN_EDITOR = 48, + AUTO_RECIPE = 49, ABILITIES = 50, COMBAT_EVENT = 51, PLAYER_INFO = 52, + LOOK_AT = 53, POSITION = 54, RECIPES = 55, ENTITY_DESTROY = 56, + REMOVE_ENTITY_EFFECT = 57, RESOURCE_PACK_SEND = 58, RESPAWN = 59, ENTITY_HEAD_ROTATION = 60, + SELECT_ADVANCEMENT_TAB = 61, WORLD_BORDER = 62, CAMERA = 63, HELD_ITEM_SLOT = 64, + VIEW_CENTRE = 65, VIEW_DISTANCE = 66, SCOREBOARD_DISPLAY_OBJECTIVE = 67, ENTITY_METADATA = 68, + ATTACH_ENTITY = 69, ENTITY_VELOCITY = 70, ENTITY_EQUIPMENT = 71, EXPERIENCE = 72, + UPDATE_HEALTH = 73, SCOREBOARD_OBJECTIVE = 74, MOUNT = 75, SCOREBOARD_TEAM = 76, + SCOREBOARD_SCORE = 77, SPAWN_POSITION = 78, UPDATE_TIME = 79, TITLE = 80, + ENTITY_SOUND = 81, NAMED_SOUND_EFFECT = 82, STOP_SOUND = 83, PLAYER_LIST_HEADER_FOOTER = 84, + NBT_QUERY = 85, COLLECT = 86, ENTITY_TELEPORT = 87, ADVANCEMENTS = 88, UPDATE_ATTRIBUTES = 89, + ENTITY_EFFECT = 90, RECIPE_UPDATE = 91, TAGS = 92, MAP_CHUNK_BULK = 93; + + public static void init() { + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY, SPAWN_ENTITY); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY_EXPERIENCE_ORB, SPAWN_ENTITY_EXPERIENCE_ORB); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY_WEATHER, SPAWN_ENTITY_WEATHER); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY_LIVING, SPAWN_ENTITY_LIVING); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_PAINTING); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY_SPAWN, SPAWN_ENTITY_SPAWN); + packetIds.put(PacketTypeClasses.Server.ANIMATION, ANIMATION); + packetIds.put(PacketTypeClasses.Server.STATISTIC, STATISTIC); + packetIds.put(PacketTypeClasses.Server.BLOCK_BREAK, BLOCK_BREAK); + packetIds.put(PacketTypeClasses.Server.BLOCK_BREAK_ANIMATION, BLOCK_BREAK_ANIMATION); + packetIds.put(PacketTypeClasses.Server.TILE_ENTITY_DATA, TILE_ENTITY_DATA); + packetIds.put(PacketTypeClasses.Server.BLOCK_ACTION, BLOCK_ACTION); + packetIds.put(PacketTypeClasses.Server.BLOCK_CHANGE, BLOCK_CHANGE); + packetIds.put(PacketTypeClasses.Server.BOSS, BOSS); + packetIds.put(PacketTypeClasses.Server.SPAWN_ENTITY, SPAWN_ENTITY); + packetIds.put(PacketTypeClasses.Server.SERVER_DIFFICULTY, SERVER_DIFFICULTY); + packetIds.put(PacketTypeClasses.Server.CHAT, CHAT); + packetIds.put(PacketTypeClasses.Server.MULTI_BLOCK_CHANGE, MULTI_BLOCK_CHANGE); + packetIds.put(PacketTypeClasses.Server.TAB_COMPLETE, TAB_COMPLETE); + packetIds.put(PacketTypeClasses.Server.COMMANDS, COMMANDS); + packetIds.put(PacketTypeClasses.Server.TRANSACTION, TRANSACTION); + packetIds.put(PacketTypeClasses.Server.CLOSE_WINDOW, CLOSE_WINDOW); + packetIds.put(PacketTypeClasses.Server.WINDOW_ITEMS, WINDOW_ITEMS); + packetIds.put(PacketTypeClasses.Server.WINDOW_DATA, WINDOW_DATA); + packetIds.put(PacketTypeClasses.Server.SET_SLOT, SET_SLOT); + packetIds.put(PacketTypeClasses.Server.SET_COOLDOWN, SET_COOLDOWN); + packetIds.put(PacketTypeClasses.Server.CUSTOM_PAYLOAD, CUSTOM_PAYLOAD); + packetIds.put(PacketTypeClasses.Server.CUSTOM_SOUND_EFFECT, CUSTOM_SOUND_EFFECT); + packetIds.put(PacketTypeClasses.Server.KICK_DISCONNECT, KICK_DISCONNECT); + packetIds.put(PacketTypeClasses.Server.ENTITY_STATUS, ENTITY_STATUS); + packetIds.put(PacketTypeClasses.Server.EXPLOSION, EXPLOSION); + packetIds.put(PacketTypeClasses.Server.UNLOAD_CHUNK, UNLOAD_CHUNK); + packetIds.put(PacketTypeClasses.Server.GAME_STATE_CHANGE, GAME_STATE_CHANGE); + packetIds.put(PacketTypeClasses.Server.OPEN_WINDOW_HORSE, OPEN_WINDOW_HORSE); + packetIds.put(PacketTypeClasses.Server.KEEP_ALIVE, KEEP_ALIVE); + packetIds.put(PacketTypeClasses.Server.MAP_CHUNK, MAP_CHUNK); + packetIds.put(PacketTypeClasses.Server.WORLD_EVENT, WORLD_EVENT); + packetIds.put(PacketTypeClasses.Server.WORLD_EVENT, SPAWN_ENTITY); + packetIds.put(PacketTypeClasses.Server.WORLD_PARTICLES, WORLD_PARTICLES); + packetIds.put(PacketTypeClasses.Server.LIGHT_UPDATE, LIGHT_UPDATE); + packetIds.put(PacketTypeClasses.Server.LOGIN, LOGIN); + packetIds.put(PacketTypeClasses.Server.MAP, MAP); + packetIds.put(PacketTypeClasses.Server.OPEN_WINDOW_MERCHANT, OPEN_WINDOW_MERCHANT); + packetIds.put(PacketTypeClasses.Server.REL_ENTITY_MOVE, REL_ENTITY_MOVE); + packetIds.put(PacketTypeClasses.Server.REL_ENTITY_MOVE_LOOK, REL_ENTITY_MOVE_LOOK); + packetIds.put(PacketTypeClasses.Server.ENTITY_LOOK, ENTITY_LOOK); + packetIds.put(PacketTypeClasses.Server.ENTITY, ENTITY); + packetIds.put(PacketTypeClasses.Server.VEHICLE_MOVE, VEHICLE_MOVE); + packetIds.put(PacketTypeClasses.Server.OPEN_BOOK, OPEN_BOOK); + packetIds.put(PacketTypeClasses.Server.OPEN_WINDOW, OPEN_WINDOW); + packetIds.put(PacketTypeClasses.Server.OPEN_SIGN_EDITOR, OPEN_SIGN_EDITOR); + packetIds.put(PacketTypeClasses.Server.AUTO_RECIPE, AUTO_RECIPE); + packetIds.put(PacketTypeClasses.Server.ABILITIES, ABILITIES); + packetIds.put(PacketTypeClasses.Server.COMBAT_EVENT, COMBAT_EVENT); + packetIds.put(PacketTypeClasses.Server.PLAYER_INFO, PLAYER_INFO); + packetIds.put(PacketTypeClasses.Server.LOOK_AT, LOOK_AT); + packetIds.put(PacketTypeClasses.Server.POSITION, POSITION); + packetIds.put(PacketTypeClasses.Server.RECIPES, RECIPES); + packetIds.put(PacketTypeClasses.Server.ENTITY_DESTROY, ENTITY_DESTROY); + packetIds.put(PacketTypeClasses.Server.REMOVE_ENTITY_EFFECT, REMOVE_ENTITY_EFFECT); + packetIds.put(PacketTypeClasses.Server.RESOURCE_PACK_SEND, RESOURCE_PACK_SEND); + packetIds.put(PacketTypeClasses.Server.RESPAWN, RESPAWN); + packetIds.put(PacketTypeClasses.Server.ENTITY_HEAD_ROTATION, ENTITY_HEAD_ROTATION); + packetIds.put(PacketTypeClasses.Server.SELECT_ADVANCEMENT_TAB, SELECT_ADVANCEMENT_TAB); + packetIds.put(PacketTypeClasses.Server.WORLD_BORDER, WORLD_BORDER); + packetIds.put(PacketTypeClasses.Server.CAMERA, CAMERA); + packetIds.put(PacketTypeClasses.Server.HELD_ITEM_SLOT, HELD_ITEM_SLOT); + packetIds.put(PacketTypeClasses.Server.VIEW_CENTRE, VIEW_CENTRE); + packetIds.put(PacketTypeClasses.Server.VIEW_DISTANCE, VIEW_DISTANCE); + packetIds.put(PacketTypeClasses.Server.SCOREBOARD_DISPLAY_OBJECTIVE, SCOREBOARD_DISPLAY_OBJECTIVE); + packetIds.put(PacketTypeClasses.Server.ENTITY_METADATA, ENTITY_METADATA); + packetIds.put(PacketTypeClasses.Server.ATTACH_ENTITY, ATTACH_ENTITY); + packetIds.put(PacketTypeClasses.Server.ENTITY_VELOCITY, ENTITY_VELOCITY); + packetIds.put(PacketTypeClasses.Server.ENTITY_EQUIPMENT, ENTITY_EQUIPMENT); + packetIds.put(PacketTypeClasses.Server.EXPERIENCE, EXPERIENCE); + packetIds.put(PacketTypeClasses.Server.UPDATE_HEALTH, UPDATE_HEALTH); + packetIds.put(PacketTypeClasses.Server.SCOREBOARD_OBJECTIVE, SCOREBOARD_OBJECTIVE); + packetIds.put(PacketTypeClasses.Server.MOUNT, MOUNT); + packetIds.put(PacketTypeClasses.Server.SCOREBOARD_TEAM, SCOREBOARD_TEAM); + packetIds.put(PacketTypeClasses.Server.SCOREBOARD_SCORE, SCOREBOARD_SCORE); + packetIds.put(PacketTypeClasses.Server.SPAWN_POSITION, SPAWN_POSITION); + packetIds.put(PacketTypeClasses.Server.UPDATE_TIME, UPDATE_TIME); + packetIds.put(PacketTypeClasses.Server.TITLE, TITLE); + packetIds.put(PacketTypeClasses.Server.ENTITY_SOUND, ENTITY_SOUND); + packetIds.put(PacketTypeClasses.Server.NAMED_SOUND_EFFECT, NAMED_SOUND_EFFECT); + packetIds.put(PacketTypeClasses.Server.STOP_SOUND, STOP_SOUND); + packetIds.put(PacketTypeClasses.Server.PLAYER_LIST_HEADER_FOOTER, PLAYER_LIST_HEADER_FOOTER); + packetIds.put(PacketTypeClasses.Server.NBT_QUERY, NBT_QUERY); + packetIds.put(PacketTypeClasses.Server.COLLECT, COLLECT); + packetIds.put(PacketTypeClasses.Server.ENTITY_TELEPORT, ENTITY_TELEPORT); + packetIds.put(PacketTypeClasses.Server.ADVANCEMENTS, ADVANCEMENTS); + packetIds.put(PacketTypeClasses.Server.UPDATE_ATTRIBUTES, UPDATE_ATTRIBUTES); + packetIds.put(PacketTypeClasses.Server.ENTITY_EFFECT, ENTITY_EFFECT); + packetIds.put(PacketTypeClasses.Server.RECIPE_UPDATE, RECIPE_UPDATE); + packetIds.put(PacketTypeClasses.Server.TAGS, TAGS); + packetIds.put(PacketTypeClasses.Server.MAP_CHUNK_BULK, MAP_CHUNK_BULK); + } + + public static class Util { + /** + * Is the packet an instance of the PacketPlayOutEntity packet? + * + * @param packetID + * @return packetID == ENTITY or REL_ENTITY_MOVE or REL_ENTITY_MOVE_LOOK or ENTITY_LOOK + */ + public static boolean isInstanceOfEntity(final byte packetID) { + return packetID == ENTITY || packetID == REL_ENTITY_MOVE || + packetID == REL_ENTITY_MOVE_LOOK || packetID == ENTITY_LOOK; + } + } + } + + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packettype/PacketTypeClasses.java b/src/main/java/io/github/retrooper/packetevents/packettype/PacketTypeClasses.java new file mode 100644 index 0000000..be36513 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packettype/PacketTypeClasses.java @@ -0,0 +1,286 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packettype; + +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; + +public class PacketTypeClasses { + public static class Status { + public static Class IN_START, PING, PONG, SERVER_INFO; + + public static void load() { + IN_START = NMSUtils.getNMSClassWithoutException("PacketStatusInStart"); + PING = NMSUtils.getNMSClassWithoutException("PacketStatusInPing"); + PONG = NMSUtils.getNMSClassWithoutException("PacketStatusOutPong"); + SERVER_INFO = NMSUtils.getNMSClassWithoutException("PacketStatusOutServerInfo"); + PacketType.Status.init(); + } + } + + public static class Login { + public static Class HANDSHAKE, + IN_CUSTOM_PAYLOAD, OUT_CUSTOM_PAYLOAD, IN_START, IN_ENCRYPTION_BEGIN, + DISCONNECT, OUT_ENCRYPTION_BEGIN, OUT_SUCCESS; + + public static void load() { + HANDSHAKE = NMSUtils.getNMSClassWithoutException("PacketHandshakingInSetProtocol"); + //In and Out custom payload login packets have been here since AROUND 1.13.2. + IN_CUSTOM_PAYLOAD = NMSUtils.getNMSClassWithoutException("PacketLoginInCustomPayload"); + OUT_CUSTOM_PAYLOAD = NMSUtils.getNMSClassWithoutException("PacketLoginOutCustomPayload"); + IN_START = NMSUtils.getNMSClassWithoutException("PacketLoginInStart"); + IN_ENCRYPTION_BEGIN = NMSUtils.getNMSClassWithoutException("PacketLoginInEncryptionBegin"); + DISCONNECT = NMSUtils.getNMSClassWithoutException("PacketLoginOutDisconnect"); + OUT_ENCRYPTION_BEGIN = NMSUtils.getNMSClassWithoutException("PacketLoginOutEncryptionBegin"); + OUT_SUCCESS = NMSUtils.getNMSClassWithoutException("PacketLoginOutSuccess"); + PacketType.Login.init(); + } + } + + public static class Client { + private static final String c = "PacketPlayIn"; + public static Class FLYING, POSITION, POSITION_LOOK, LOOK, CLIENT_COMMAND, + TRANSACTION, BLOCK_DIG, ENTITY_ACTION, USE_ENTITY, + WINDOW_CLICK, STEER_VEHICLE, CUSTOM_PAYLOAD, ARM_ANIMATION, + BLOCK_PLACE, USE_ITEM, ABILITIES, HELD_ITEM_SLOT, + CLOSE_WINDOW, TAB_COMPLETE, CHAT, SET_CREATIVE_SLOT, + KEEP_ALIVE, SETTINGS, ENCHANT_ITEM, TELEPORT_ACCEPT, + TILE_NBT_QUERY, DIFFICULTY_CHANGE, B_EDIT, ENTITY_NBT_QUERY, + JIGSAW_GENERATE, DIFFICULTY_LOCK, VEHICLE_MOVE, BOAT_MOVE, PICK_ITEM, + AUTO_RECIPE, RECIPE_DISPLAYED, ITEM_NAME, RESOURCE_PACK_STATUS, + ADVANCEMENTS, TR_SEL, BEACON, SET_COMMAND_BLOCK, + SET_COMMAND_MINECART, SET_JIGSAW, STRUCT, UPDATE_SIGN, SPECTATE; + + /** + * Initiate all server-bound packet classes. + */ + public static void load() { + FLYING = NMSUtils.getNMSClassWithoutException(c + "Flying"); + try { + POSITION = NMSUtils.getNMSClass(c + "Position"); + POSITION_LOOK = NMSUtils.getNMSClass(c + "PositionLook"); + LOOK = NMSUtils.getNMSClass(c + "Look"); + } catch (ClassNotFoundException e) { + POSITION = SubclassUtil.getSubClass(FLYING, c + "Position"); + POSITION_LOOK = SubclassUtil.getSubClass(FLYING, c + "PositionLook"); + LOOK = SubclassUtil.getSubClass(FLYING, c + "Look"); + } + + try { + SETTINGS = NMSUtils.getNMSClass(c + "Settings"); + ENCHANT_ITEM = NMSUtils.getNMSClass(c + "EnchantItem"); + + CLIENT_COMMAND = NMSUtils.getNMSClass(c + "ClientCommand"); + TRANSACTION = NMSUtils.getNMSClass(c + "Transaction"); + BLOCK_DIG = NMSUtils.getNMSClass(c + "BlockDig"); + ENTITY_ACTION = NMSUtils.getNMSClass(c + "EntityAction"); + USE_ENTITY = NMSUtils.getNMSClass(c + "UseEntity"); + WINDOW_CLICK = NMSUtils.getNMSClass(c + "WindowClick"); + STEER_VEHICLE = NMSUtils.getNMSClass(c + "SteerVehicle"); + CUSTOM_PAYLOAD = NMSUtils.getNMSClass(c + "CustomPayload"); + ARM_ANIMATION = NMSUtils.getNMSClass(c + "ArmAnimation"); + ABILITIES = NMSUtils.getNMSClass(c + "Abilities"); + HELD_ITEM_SLOT = NMSUtils.getNMSClass(c + "HeldItemSlot"); + CLOSE_WINDOW = NMSUtils.getNMSClass(c + "CloseWindow"); + TAB_COMPLETE = NMSUtils.getNMSClass(c + "TabComplete"); + CHAT = NMSUtils.getNMSClass(c + "Chat"); + SET_CREATIVE_SLOT = NMSUtils.getNMSClass(c + "SetCreativeSlot"); + KEEP_ALIVE = NMSUtils.getNMSClass(c + "KeepAlive"); + UPDATE_SIGN = NMSUtils.getNMSClassWithoutException(c + "UpdateSign"); + + TELEPORT_ACCEPT = NMSUtils.getNMSClassWithoutException(c + "TeleportAccept"); + TILE_NBT_QUERY = NMSUtils.getNMSClassWithoutException(c + "TileNBTQuery"); + DIFFICULTY_CHANGE = NMSUtils.getNMSClassWithoutException(c + "DifficultyChange"); + B_EDIT = NMSUtils.getNMSClassWithoutException(c + "BEdit"); + ENTITY_NBT_QUERY = NMSUtils.getNMSClassWithoutException(c + "EntityNBTQuery"); + JIGSAW_GENERATE = NMSUtils.getNMSClassWithoutException(c + "JigsawGenerate"); + DIFFICULTY_LOCK = NMSUtils.getNMSClassWithoutException(c + "DifficultyLock"); + VEHICLE_MOVE = NMSUtils.getNMSClassWithoutException(c + "VehicleMove"); + BOAT_MOVE = NMSUtils.getNMSClassWithoutException(c + "BoatMove"); + PICK_ITEM = NMSUtils.getNMSClassWithoutException(c + "PickItem"); + AUTO_RECIPE = NMSUtils.getNMSClassWithoutException(c + "AutoRecipe"); + RECIPE_DISPLAYED = NMSUtils.getNMSClassWithoutException(c + "RecipeDisplayed"); + ITEM_NAME = NMSUtils.getNMSClassWithoutException(c + "ItemName"); + //1.8+ + RESOURCE_PACK_STATUS = NMSUtils.getNMSClassWithoutException(c + "ResourcePackStatus"); + + ADVANCEMENTS = NMSUtils.getNMSClassWithoutException(c + "Advancements"); + TR_SEL = NMSUtils.getNMSClassWithoutException(c + "TrSel"); + BEACON = NMSUtils.getNMSClassWithoutException(c + "Beacon"); + SET_COMMAND_BLOCK = NMSUtils.getNMSClassWithoutException(c + "SetCommandBlock"); + SET_COMMAND_MINECART = NMSUtils.getNMSClassWithoutException(c + "SetCommandMinecart"); + SET_JIGSAW = NMSUtils.getNMSClassWithoutException(c + "SetJigsaw"); + STRUCT = NMSUtils.getNMSClassWithoutException(c + "Struct"); + SPECTATE = NMSUtils.getNMSClassWithoutException(c + "Spectate"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + try { + BLOCK_PLACE = NMSUtils.getNMSClass(c + "BlockPlace"); + USE_ITEM = NMSUtils.getNMSClass(c + "UseItem"); + } catch (ClassNotFoundException ignored) { + + } + + PacketType.Client.init(); + } + } + + public static class Server { + private static final String s = "PacketPlayOut"; + public static Class SPAWN_ENTITY, SPAWN_ENTITY_EXPERIENCE_ORB, SPAWN_ENTITY_WEATHER, SPAWN_ENTITY_LIVING, + SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_SPAWN, ANIMATION, STATISTIC, + BLOCK_BREAK, BLOCK_BREAK_ANIMATION, TILE_ENTITY_DATA, BLOCK_ACTION, + BLOCK_CHANGE, BOSS, SERVER_DIFFICULTY, CHAT, MULTI_BLOCK_CHANGE, + TAB_COMPLETE, COMMANDS, TRANSACTION, CLOSE_WINDOW, + WINDOW_ITEMS, WINDOW_DATA, SET_SLOT, SET_COOLDOWN, + CUSTOM_PAYLOAD, CUSTOM_SOUND_EFFECT, KICK_DISCONNECT, ENTITY_STATUS, + EXPLOSION, UNLOAD_CHUNK, GAME_STATE_CHANGE, OPEN_WINDOW_HORSE, + KEEP_ALIVE, MAP_CHUNK, WORLD_EVENT, WORLD_PARTICLES, + LIGHT_UPDATE, LOGIN, MAP, OPEN_WINDOW_MERCHANT, + REL_ENTITY_MOVE, REL_ENTITY_MOVE_LOOK, ENTITY_LOOK, ENTITY, + VEHICLE_MOVE, OPEN_BOOK, OPEN_WINDOW, OPEN_SIGN_EDITOR, + AUTO_RECIPE, ABILITIES, COMBAT_EVENT, PLAYER_INFO, + LOOK_AT, POSITION, RECIPES, ENTITY_DESTROY, + REMOVE_ENTITY_EFFECT, RESOURCE_PACK_SEND, RESPAWN, ENTITY_HEAD_ROTATION, + SELECT_ADVANCEMENT_TAB, WORLD_BORDER, CAMERA, HELD_ITEM_SLOT, + VIEW_CENTRE, VIEW_DISTANCE, SCOREBOARD_DISPLAY_OBJECTIVE, ENTITY_METADATA, + ATTACH_ENTITY, ENTITY_VELOCITY, ENTITY_EQUIPMENT, EXPERIENCE, + UPDATE_HEALTH, SCOREBOARD_OBJECTIVE, MOUNT, SCOREBOARD_TEAM, + SCOREBOARD_SCORE, SPAWN_POSITION, UPDATE_TIME, TITLE, + ENTITY_SOUND, NAMED_SOUND_EFFECT, STOP_SOUND, PLAYER_LIST_HEADER_FOOTER, + NBT_QUERY, COLLECT, ENTITY_TELEPORT, ADVANCEMENTS, UPDATE_ATTRIBUTES, + ENTITY_EFFECT, RECIPE_UPDATE, TAGS, MAP_CHUNK_BULK; + + /** + * Initiate all client-bound packet classes. + */ + public static void load() { + SPAWN_ENTITY = NMSUtils.getNMSClassWithoutException(s + "SpawnEntity"); + SPAWN_ENTITY_EXPERIENCE_ORB = NMSUtils.getNMSClassWithoutException(s + "SpawnEntityExperienceOrb"); + SPAWN_ENTITY_WEATHER = NMSUtils.getNMSClassWithoutException(s + "SpawnEntityWeather"); + SPAWN_ENTITY_LIVING = NMSUtils.getNMSClassWithoutException(s + "SpawnEntityLiving"); + SPAWN_ENTITY_PAINTING = NMSUtils.getNMSClassWithoutException(s + "SpawnEntityPainting"); + SPAWN_ENTITY_SPAWN = NMSUtils.getNMSClassWithoutException(s + "SpawnEntitySpawn"); + ANIMATION = NMSUtils.getNMSClassWithoutException(s + "Animation"); + STATISTIC = NMSUtils.getNMSClassWithoutException(s + "Statistic"); + BLOCK_BREAK = NMSUtils.getNMSClassWithoutException(s + "BlockBreak"); + BLOCK_BREAK_ANIMATION = NMSUtils.getNMSClassWithoutException(s + "BlockBreakAnimation"); + TILE_ENTITY_DATA = NMSUtils.getNMSClassWithoutException(s + "TileEntityData"); + BLOCK_ACTION = NMSUtils.getNMSClassWithoutException(s + "BlockAction"); + BLOCK_CHANGE = NMSUtils.getNMSClassWithoutException(s + "BlockChange"); + BOSS = NMSUtils.getNMSClassWithoutException(s + "Boss"); + SERVER_DIFFICULTY = NMSUtils.getNMSClassWithoutException(s + "ServerDifficulty"); + CHAT = NMSUtils.getNMSClassWithoutException(s + "Chat"); + MULTI_BLOCK_CHANGE = NMSUtils.getNMSClassWithoutException(s + "MultiBlockChange"); + TAB_COMPLETE = NMSUtils.getNMSClassWithoutException(s + "TabComplete"); + COMMANDS = NMSUtils.getNMSClassWithoutException(s + "Commands"); + TRANSACTION = NMSUtils.getNMSClassWithoutException(s + "Transaction"); + CLOSE_WINDOW = NMSUtils.getNMSClassWithoutException(s + "CloseWindow"); + WINDOW_ITEMS = NMSUtils.getNMSClassWithoutException(s + "WindowItems"); + WINDOW_DATA = NMSUtils.getNMSClassWithoutException(s + "WindowData"); + SET_SLOT = NMSUtils.getNMSClassWithoutException(s + "SetSlot"); + SET_COOLDOWN = NMSUtils.getNMSClassWithoutException(s + "SetCooldown"); + CUSTOM_PAYLOAD = NMSUtils.getNMSClassWithoutException(s + "CustomPayload"); + CUSTOM_SOUND_EFFECT = NMSUtils.getNMSClassWithoutException(s + "CustomSoundEffect"); + KICK_DISCONNECT = NMSUtils.getNMSClassWithoutException(s + "KickDisconnect"); + ENTITY_STATUS = NMSUtils.getNMSClassWithoutException(s + "EntityStatus"); + EXPLOSION = NMSUtils.getNMSClassWithoutException(s + "Explosion"); + UNLOAD_CHUNK = NMSUtils.getNMSClassWithoutException(s + "UnloadChunk"); + GAME_STATE_CHANGE = NMSUtils.getNMSClassWithoutException(s + "GameStateChange"); + OPEN_WINDOW_HORSE = NMSUtils.getNMSClassWithoutException(s + "OpenWindowHorse"); + KEEP_ALIVE = NMSUtils.getNMSClassWithoutException(s + "KeepAlive"); + MAP_CHUNK = NMSUtils.getNMSClassWithoutException(s + "MapChunk"); + WORLD_EVENT = NMSUtils.getNMSClassWithoutException(s + "WorldEvent"); + WORLD_PARTICLES = NMSUtils.getNMSClassWithoutException(s + "WorldParticles"); + LIGHT_UPDATE = NMSUtils.getNMSClassWithoutException(s + "LightUpdate"); + LOGIN = NMSUtils.getNMSClassWithoutException(s + "Login"); + MAP = NMSUtils.getNMSClassWithoutException(s + "Map"); + OPEN_WINDOW_MERCHANT = NMSUtils.getNMSClassWithoutException(s + "OpenWindowMerchant"); + ENTITY = NMSUtils.getNMSClassWithoutException(s + "Entity"); + REL_ENTITY_MOVE = SubclassUtil.getSubClass(ENTITY, s + "RelEntityMove"); + REL_ENTITY_MOVE_LOOK = SubclassUtil.getSubClass(ENTITY, s + "RelEntityMoveLook"); + ENTITY_LOOK = SubclassUtil.getSubClass(ENTITY, s + "EntityLook"); + if (REL_ENTITY_MOVE == null) { + //is not a subclass and should be accessed normally + REL_ENTITY_MOVE = NMSUtils.getNMSClassWithoutException(s + "RelEntityMove"); + REL_ENTITY_MOVE_LOOK = NMSUtils.getNMSClassWithoutException(s + "RelEntityMoveLook"); + ENTITY_LOOK = NMSUtils.getNMSClassWithoutException(s + "RelEntityLook"); + } + VEHICLE_MOVE = NMSUtils.getNMSClassWithoutException(s + "VehicleMove"); + OPEN_BOOK = NMSUtils.getNMSClassWithoutException(s + "OpenBook"); + OPEN_WINDOW = NMSUtils.getNMSClassWithoutException(s + "OpenWindow"); + OPEN_SIGN_EDITOR = NMSUtils.getNMSClassWithoutException(s + "OpenSignEditor"); + AUTO_RECIPE = NMSUtils.getNMSClassWithoutException(s + "AutoRecipe"); + ABILITIES = NMSUtils.getNMSClassWithoutException(s + "Abilities"); + COMBAT_EVENT = NMSUtils.getNMSClassWithoutException(s + "CombatEvent"); + PLAYER_INFO = NMSUtils.getNMSClassWithoutException(s + "PlayerInfo"); + LOOK_AT = NMSUtils.getNMSClassWithoutException(s + "LookAt"); + POSITION = NMSUtils.getNMSClassWithoutException(s + "Position"); + RECIPES = NMSUtils.getNMSClassWithoutException(s + "Recipes"); + ENTITY_DESTROY = NMSUtils.getNMSClassWithoutException(s + "EntityDestroy"); + REMOVE_ENTITY_EFFECT = NMSUtils.getNMSClassWithoutException(s + "RemoveEntityEffect"); + RESOURCE_PACK_SEND = NMSUtils.getNMSClassWithoutException(s + "ResourcePackSend"); + RESPAWN = NMSUtils.getNMSClassWithoutException(s + "Respawn"); + ENTITY_HEAD_ROTATION = NMSUtils.getNMSClassWithoutException(s + "EntityHeadRotation"); + SELECT_ADVANCEMENT_TAB = NMSUtils.getNMSClassWithoutException(s + "SelectAdvancementTab"); + WORLD_BORDER = NMSUtils.getNMSClassWithoutException(s + "WorldBorder"); + CAMERA = NMSUtils.getNMSClassWithoutException(s + "Camera"); + HELD_ITEM_SLOT = NMSUtils.getNMSClassWithoutException(s + "HeldItemSlot"); + VIEW_CENTRE = NMSUtils.getNMSClassWithoutException(s + "ViewCentre"); + VIEW_DISTANCE = NMSUtils.getNMSClassWithoutException(s + "ViewDistance"); + SCOREBOARD_DISPLAY_OBJECTIVE = NMSUtils.getNMSClassWithoutException(s + "ScoreboardDisplayObjective"); + ENTITY_METADATA = NMSUtils.getNMSClassWithoutException(s + "EntityMetadata"); + ATTACH_ENTITY = NMSUtils.getNMSClassWithoutException(s + "AttachEntity"); + ENTITY_VELOCITY = NMSUtils.getNMSClassWithoutException(s + "EntityVelocity"); + ENTITY_EQUIPMENT = NMSUtils.getNMSClassWithoutException(s + "EntityEquipment"); + EXPERIENCE = NMSUtils.getNMSClassWithoutException(s + "Experience"); + UPDATE_HEALTH = NMSUtils.getNMSClassWithoutException(s + "UpdateHealth"); + SCOREBOARD_OBJECTIVE = NMSUtils.getNMSClassWithoutException(s + "ScoreboardObjective"); + MOUNT = NMSUtils.getNMSClassWithoutException(s + "Mount"); + SCOREBOARD_TEAM = NMSUtils.getNMSClassWithoutException(s + "ScoreboardTeam"); + SCOREBOARD_SCORE = NMSUtils.getNMSClassWithoutException(s + "ScoreboardScore"); + SPAWN_POSITION = NMSUtils.getNMSClassWithoutException(s + "SpawnPosition"); + UPDATE_TIME = NMSUtils.getNMSClassWithoutException(s + "UpdateTime"); + TITLE = NMSUtils.getNMSClassWithoutException(s + "Title"); + ENTITY_SOUND = NMSUtils.getNMSClassWithoutException(s + "EntitySound"); + NAMED_SOUND_EFFECT = NMSUtils.getNMSClassWithoutException(s + "NamedSoundEffect"); + STOP_SOUND = NMSUtils.getNMSClassWithoutException(s + "StopSound"); + PLAYER_LIST_HEADER_FOOTER = NMSUtils.getNMSClassWithoutException(s + "PlayerListHeaderFooter"); + NBT_QUERY = NMSUtils.getNMSClassWithoutException(s + "NBTQuery"); + COLLECT = NMSUtils.getNMSClassWithoutException(s + "Collect"); + ENTITY_TELEPORT = NMSUtils.getNMSClassWithoutException(s + "EntityTeleport"); + ADVANCEMENTS = NMSUtils.getNMSClassWithoutException(s + "Advancements"); + UPDATE_ATTRIBUTES = NMSUtils.getNMSClassWithoutException(s + "UpdateAttributes"); + ENTITY_EFFECT = NMSUtils.getNMSClassWithoutException(s + "EntityEffect"); + RECIPE_UPDATE = NMSUtils.getNMSClassWithoutException(s + "RecipeUpdate"); + TAGS = NMSUtils.getNMSClassWithoutException(s + "Tags"); + MAP_CHUNK_BULK = NMSUtils.getNMSClassWithoutException(s + "MapChunkBulk"); + PacketType.Server.init(); + } + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/SendableWrapper.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/SendableWrapper.java new file mode 100644 index 0000000..97be372 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/SendableWrapper.java @@ -0,0 +1,32 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers; + +/** + * This interface indicates that a packet wrapper supports being sent to a client. + */ +public interface SendableWrapper { + Object asNMSPacket(); +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrappedPacket.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrappedPacket.java new file mode 100644 index 0000000..4fd9464 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrappedPacket.java @@ -0,0 +1,614 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers; + +import io.github.retrooper.packetevents.exceptions.WrapperFieldNotFoundException; +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import io.github.retrooper.packetevents.packetwrappers.in.blockplace.WrappedPacketInBlockPlace; +import io.github.retrooper.packetevents.packetwrappers.in.clientcommand.WrappedPacketInClientCommand; +import io.github.retrooper.packetevents.packetwrappers.in.custompayload.WrappedPacketInCustomPayload; +import io.github.retrooper.packetevents.packetwrappers.in.entityaction.WrappedPacketInEntityAction; +import io.github.retrooper.packetevents.packetwrappers.in.keepalive.WrappedPacketInKeepAlive; +import io.github.retrooper.packetevents.packetwrappers.in.settings.WrappedPacketInSettings; +import io.github.retrooper.packetevents.packetwrappers.in.updatesign.WrappedPacketInUpdateSign; +import io.github.retrooper.packetevents.packetwrappers.in.useentity.WrappedPacketInUseEntity; +import io.github.retrooper.packetevents.packetwrappers.in.windowclick.WrappedPacketInWindowClick; +import io.github.retrooper.packetevents.packetwrappers.out.abilities.WrappedPacketOutAbilities; +import io.github.retrooper.packetevents.packetwrappers.out.animation.WrappedPacketOutAnimation; +import io.github.retrooper.packetevents.packetwrappers.out.chat.WrappedPacketOutChat; +import io.github.retrooper.packetevents.packetwrappers.out.custompayload.WrappedPacketOutCustomPayload; +import io.github.retrooper.packetevents.packetwrappers.out.entity.WrappedPacketOutEntity; +import io.github.retrooper.packetevents.packetwrappers.out.entitystatus.WrappedPacketOutEntityStatus; +import io.github.retrooper.packetevents.packetwrappers.out.entityteleport.WrappedPacketOutEntityTeleport; +import io.github.retrooper.packetevents.packetwrappers.out.entityvelocity.WrappedPacketOutEntityVelocity; +import io.github.retrooper.packetevents.packetwrappers.out.experience.WrappedPacketOutExperience; +import io.github.retrooper.packetevents.packetwrappers.out.explosion.WrappedPacketOutExplosion; +import io.github.retrooper.packetevents.packetwrappers.out.gamestatechange.WrappedPacketOutGameStateChange; +import io.github.retrooper.packetevents.packetwrappers.out.helditemslot.WrappedPacketOutHeldItemSlot; +import io.github.retrooper.packetevents.packetwrappers.out.keepalive.WrappedPacketOutKeepAlive; +import io.github.retrooper.packetevents.packetwrappers.out.kickdisconnect.WrappedPacketOutKickDisconnect; +import io.github.retrooper.packetevents.packetwrappers.out.position.WrappedPacketOutPosition; +import io.github.retrooper.packetevents.packetwrappers.out.transaction.WrappedPacketOutTransaction; +import io.github.retrooper.packetevents.packetwrappers.out.updatehealth.WrappedPacketOutUpdateHealth; +import io.github.retrooper.packetevents.utils.reflection.ClassUtil; +import io.github.retrooper.packetevents.utils.server.ServerVersion; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WrappedPacket implements WrapperPacketReader, WrapperPacketWriter { + private static final Map, Map, Field[]>> fieldCache = new HashMap<>(); + public static ServerVersion version; + protected Object packet; + private Class packetClass; + + public WrappedPacket() { + + } + + public WrappedPacket(final Object packet) { + this(packet, packet.getClass()); + } + + public WrappedPacket(final Object packet, Class packetClass) { + if (packet.getClass().getSuperclass().equals(PacketTypeClasses.Client.FLYING)) { + packetClass = PacketTypeClasses.Client.FLYING; + } else if (packet.getClass().getSuperclass().equals(PacketTypeClasses.Server.ENTITY)) { + packetClass = PacketTypeClasses.Server.ENTITY; + } + this.packetClass = packetClass; + + + if (!fieldCache.containsKey(packetClass)) { + final Field[] declaredFields = packetClass.getDeclaredFields(); + for (Field f : declaredFields) { + f.setAccessible(true); + } + List boolFields = getFields(boolean.class, declaredFields); + List byteFields = getFields(byte.class, declaredFields); + List shortFields = getFields(short.class, declaredFields); + List intFields = getFields(int.class, declaredFields); + List longFields = getFields(long.class, declaredFields); + List floatFields = getFields(float.class, declaredFields); + List doubleFields = getFields(double.class, declaredFields); + List stringFields = getFields(String.class, declaredFields); + + List boolArrayFields = getFields(boolean[].class, declaredFields); + List byteArrayFields = getFields(byte[].class, declaredFields); + List shortArrayFields = getFields(short[].class, declaredFields); + List intArrayFields = getFields(int[].class, declaredFields); + List longArrayFields = getFields(long[].class, declaredFields); + List floatArrayFields = getFields(float[].class, declaredFields); + List doubleArrayFields = getFields(double[].class, declaredFields); + List stringArrayFields = getFields(String[].class, declaredFields); + + + Field[] tmp = new Field[0]; + + Map, Field[]> map = new HashMap<>(); + + map.put(boolean.class, boolFields.toArray(tmp)); + map.put(byte.class, byteFields.toArray(tmp)); + map.put(short.class, shortFields.toArray(tmp)); + map.put(int.class, intFields.toArray(tmp)); + map.put(long.class, longFields.toArray(tmp)); + map.put(float.class, floatFields.toArray(tmp)); + map.put(double.class, doubleFields.toArray(tmp)); + + map.put(String.class, stringFields.toArray(tmp)); + map.put(boolean[].class, boolArrayFields.toArray(tmp)); + map.put(byte[].class, byteArrayFields.toArray(tmp)); + map.put(short[].class, shortArrayFields.toArray(tmp)); + map.put(int[].class, intArrayFields.toArray(tmp)); + map.put(long[].class, longArrayFields.toArray(tmp)); + map.put(float[].class, floatArrayFields.toArray(tmp)); + map.put(double[].class, doubleArrayFields.toArray(tmp)); + map.put(String[].class, stringArrayFields.toArray(tmp)); + fieldCache.put(packetClass, map); + } + this.packet = packet; + setup(); + } + + public static void loadAllWrappers() { + //SERVER BOUND + WrappedPacketInBlockDig.load(); + WrappedPacketInBlockPlace.load(); + WrappedPacketInClientCommand.load(); + WrappedPacketInCustomPayload.load(); + WrappedPacketInEntityAction.load(); + WrappedPacketInKeepAlive.load(); + WrappedPacketInSettings.load(); + WrappedPacketInUseEntity.load(); + WrappedPacketInUpdateSign.load(); + WrappedPacketInWindowClick.load(); + + //CLIENTBOUND + WrappedPacketOutAbilities.load(); + WrappedPacketOutAnimation.load(); + WrappedPacketOutChat.load(); + WrappedPacketOutEntity.load(); + WrappedPacketOutEntityVelocity.load(); + WrappedPacketOutEntityTeleport.load(); + WrappedPacketOutKeepAlive.load(); + WrappedPacketOutKickDisconnect.load(); + WrappedPacketOutPosition.load(); + WrappedPacketOutTransaction.load(); + WrappedPacketOutUpdateHealth.load(); + WrappedPacketOutGameStateChange.load(); + WrappedPacketOutCustomPayload.load(); + WrappedPacketOutExplosion.load(); + WrappedPacketOutEntityStatus.load(); + WrappedPacketOutExperience.load(); + WrappedPacketOutHeldItemSlot.load(); + } + + protected void setup() { + + } + + @Override + public boolean readBoolean(int index) { + return (boolean) read(boolean.class, index); + } + + @Override + public byte readByte(int index) { + return (byte) read(byte.class, index); + } + + @Override + public short readShort(int index) { + return (short) read(short.class, index); + } + + @Override + public int readInt(int index) { + return (int) read(int.class, index); + } + + @Override + public long readLong(int index) { + return (long) read(long.class, index); + } + + @Override + public float readFloat(int index) { + return (float) read(float.class, index); + } + + @Override + public double readDouble(int index) { + return (double) read(double.class, index); + } + + @Override + public boolean[] readBooleanArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (boolean[]) cached.get(boolean[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (boolean[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (boolean[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, boolean[].class, index); + } + + @Override + public byte[] readByteArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (byte[]) cached.get(byte[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (byte[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (byte[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, byte[].class, index); + } + + @Override + public short[] readShortArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (short[]) cached.get(short[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (short[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (short[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, short[].class, index); + } + + @Override + public int[] readIntArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (int[]) cached.get(int[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (int[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (int[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, int[].class, index); + } + + @Override + public long[] readLongArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (long[]) cached.get(long[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (long[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (long[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, long[].class, index); + } + + @Override + public float[] readFloatArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (float[]) cached.get(float[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (float.class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (float[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, float[].class, index); + } + + @Override + public double[] readDoubleArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + return (double[]) cached.get(double[].class)[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + int currentIndex = 0; + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (double[].class.isAssignableFrom(f.getType())) { + if (index == currentIndex++) { + try { + return (double[]) f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + throw new WrapperFieldNotFoundException(packetClass, double[].class, index); + } + + @Override + public String[] readStringArray(int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + try { + Object[] array = (Object[]) cached.get(String[].class)[index].get(packet); + int len = array.length; + String[] stringArray = new String[len]; + for (int i = 0; i < len; i++) { + stringArray[i] = array[i].toString(); + } + return stringArray; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + throw new WrapperFieldNotFoundException(packetClass, String[].class, index); + } + + @Override + public String readString(int index) { + return (String) read(String.class, index); + } + + public Object readAnyObject(int index) { + try { + Field f = packetClass.getDeclaredFields()[index]; + if (!f.isAccessible()) { + f.setAccessible(true); + } + try { + return f.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new WrapperFieldNotFoundException("PacketEvents failed to find any field indexed " + index + " in the " + ClassUtil.getClassSimpleName(packetClass) + " class!"); + } + return null; + } + + public Object readObject(int index, Class type) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + Field[] cachedFields = cached.get(type); + if (cachedFields == null) { + List typeFields = new ArrayList<>(); + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (f.getType().equals(type)) { + typeFields.add(f); + } + } + if (!typeFields.isEmpty()) { + cached.put(type, typeFields.toArray(new Field[0])); + cachedFields = cached.get(type); + } else { + throw new WrapperFieldNotFoundException("The class you are trying to read fields from does not contain any fields!"); + } + } + try { + return cachedFields[index].get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + throw new WrapperFieldNotFoundException(packetClass, type, index); + } + + public boolean doesObjectExist(int index, Class type) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached != null) { + Field[] cachedFields = cached.get(type); + if (cachedFields == null) { + List typeFields = new ArrayList<>(); + for (Field f : packetClass.getDeclaredFields()) { + f.setAccessible(true); + if (f.getType().equals(type)) { + typeFields.add(f); + } + } + if (!typeFields.isEmpty()) { + cached.put(type, typeFields.toArray(new Field[0])); + cachedFields = cached.get(type); + } else { + return false; + } + } + if (cachedFields == null) { + return false; + } else return index <= cachedFields.length + 1; + } + return false; + } + + public Object[] readObjectArray(int index, Class type) { + if (type.equals(String.class)) { + return readStringArray(index); + } + try { + return (Object[]) readObject(index, getArrayClass(type)); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("PacketEvents failed to find the array class of type " + type.getName()); + } + } + + @Override + public void writeBoolean(int index, boolean value) { + write(boolean.class, index, value); + } + + @Override + public void writeByte(int index, byte value) { + write(byte.class, index, value); + } + + @Override + public void writeShort(int index, short value) { + write(short.class, index, value); + } + + @Override + public void writeInt(int index, int value) { + write(int.class, index, value); + } + + @Override + public void writeLong(int index, long value) { + write(long.class, index, value); + } + + @Override + public void writeFloat(int index, float value) { + write(float.class, index, value); + } + + @Override + public void writeDouble(int index, double value) { + write(double.class, index, value); + } + + @Override + public void writeString(int index, String value) { + write(String.class, index, value); + } + + private void write(Class type, int index, Object value) throws WrapperFieldNotFoundException { + Field field = getField(type, index); + if (field == null) { + throw new WrapperFieldNotFoundException(packetClass, type, index); + } + try { + field.set(packet, value); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + private Object read(Class type, int index) throws WrapperFieldNotFoundException { + Field field = getField(type, index); + if (field == null) { + throw new WrapperFieldNotFoundException(packetClass, type, index); + } + try { + return field.get(packet); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + private Field getField(Class type, int index) { + Map, Field[]> cached = fieldCache.get(packetClass); + if (cached == null) { + return null; + } + Field[] fields = cached.get(type); + if (fields != null) { + return fields[index]; + } + return null; + } + + private List getFields(Class type, Field[] fields) { + List ret = new ArrayList<>(); + for (Field field : fields) { + if (field.getType() == type) { + ret.add(field); + } + } + return ret; + } + + private Class getArrayClass(Class componentType) throws ClassNotFoundException { + ClassLoader classLoader = componentType.getClassLoader(); + String name; + if (componentType.isArray()) { + // just add a leading "[" + name = "[" + componentType.getName(); + } else if (componentType == boolean.class) { + name = "[Z"; + } else if (componentType == byte.class) { + name = "[B"; + } else if (componentType == char.class) { + name = "[C"; + } else if (componentType == double.class) { + name = "[D"; + } else if (componentType == float.class) { + name = "[F"; + } else if (componentType == int.class) { + name = "[I"; + } else if (componentType == long.class) { + name = "[J"; + } else if (componentType == short.class) { + name = "[S"; + } else { + // must be an object non-array class + name = "[L" + componentType.getName() + ";"; + } + return classLoader != null ? classLoader.loadClass(name) : Class.forName(name); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketReader.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketReader.java new file mode 100644 index 0000000..3536577 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketReader.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers; + +public interface WrapperPacketReader { + + boolean readBoolean(int index); + + byte readByte(int index); + + short readShort(int index); + + int readInt(int index); + + long readLong(int index); + + float readFloat(int index); + + double readDouble(int index); + + boolean[] readBooleanArray(int index); + + byte[] readByteArray(int index); + + short[] readShortArray(int index); + + int[] readIntArray(int index); + + long[] readLongArray(int index); + + float[] readFloatArray(int index); + + double[] readDoubleArray(int index); + + String[] readStringArray(int index); + + String readString(int index); + + Object readObject(int index, Class type); + + Object readAnyObject(int index); + + Object[] readObjectArray(int index, Class type); +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketWriter.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketWriter.java new file mode 100644 index 0000000..9a91a61 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/WrapperPacketWriter.java @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers; + +public interface WrapperPacketWriter { + + // TODO write for the arrays + void writeBoolean(int index, boolean value); + + void writeByte(int index, byte value); + + void writeShort(int index, short value); + + void writeInt(int index, int value); + + void writeLong(int index, long value); + + void writeFloat(int index, float value); + + void writeDouble(int index, double value); + + void writeString(int index, String value); +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/abilities/WrappedPacketInAbilities.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/abilities/WrappedPacketInAbilities.java new file mode 100644 index 0000000..7c12b27 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/abilities/WrappedPacketInAbilities.java @@ -0,0 +1,95 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.abilities; + +import io.github.retrooper.packetevents.annotations.NotNull; +import io.github.retrooper.packetevents.annotations.Nullable; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public final class WrappedPacketInAbilities extends WrappedPacket { + + public WrappedPacketInAbilities(Object packet) { + super(packet); + } + /** + * This will return null if the server version is not available in 1.16.x and above + * + * @return Whether the player is vulnerable to damage or not. + */ + @Deprecated + @Nullable + public boolean isVulnerable() { + return readBoolean(0); + } + + @NotNull + public boolean isFlying() { + return readBoolean(1); + } + + /** + * This will return null if the server version is not available in 1.16.x and above + * + * @return Whether or not the player can fly. + */ + @Deprecated + @Nullable + public boolean isFlightAllowed() { + return readBoolean(2); + } + + /** + * This will return null if the server version is not available in 1.16.x and above + * + * @return Whether or not the player can break blocks instantly. + */ + @Deprecated + @Nullable + public boolean canInstantlyBuild() { + return readBoolean(3); + } + + /** + * This will return null if the server version is not available in 1.16.x and above + * + * @return The speed at which the player can fly, as a float. + */ + @Deprecated + @Nullable + public float getFlySpeed() { + return readFloat(0); + } + + /** + * This will return null if the server version is not available in 1.16.x and above + * + * @return The speed at which the player can walk, as a float. + */ + @Deprecated + @Nullable + public float getWalkSpeed() { + return readFloat(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockdig/WrappedPacketInBlockDig.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockdig/WrappedPacketInBlockDig.java new file mode 100644 index 0000000..fdaff44 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockdig/WrappedPacketInBlockDig.java @@ -0,0 +1,172 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.blockdig; + +import io.github.retrooper.packetevents.enums.Direction; +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; +import io.github.retrooper.packetevents.utils.server.ServerVersion; + +import java.lang.reflect.InvocationTargetException; + +public final class WrappedPacketInBlockDig extends WrappedPacket { + private static Class blockDigClass, blockPositionClass, enumDirectionClass, digTypeClass; + private static boolean isVersionLowerThan_v_1_8; + private Object blockPosObj; + private Object enumDirObj; + + public WrappedPacketInBlockDig(Object packet) { + super(packet); + } + + public static void load() { + blockDigClass = PacketTypeClasses.Client.BLOCK_DIG; + try { + if (version.isHigherThan(ServerVersion.v_1_7_10)) { + blockPositionClass = NMSUtils.getNMSClass("BlockPosition"); + enumDirectionClass = NMSUtils.getNMSClass("EnumDirection"); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + isVersionLowerThan_v_1_8 = version.isLowerThan(ServerVersion.v_1_8); + + if (version != ServerVersion.v_1_7_10) { + try { + digTypeClass = NMSUtils.getNMSClass("EnumPlayerDigType"); + } catch (ClassNotFoundException e) { + //It is probably a subclass + digTypeClass = SubclassUtil.getSubClass(blockDigClass, "EnumPlayerDigType"); + } + } + } + + /** + * Get X position of the block + * @return Block Position X + */ + public int getX() { + if(isVersionLowerThan_v_1_8) { + return readInt(0); + } + else { + if(blockPosObj == null) { + blockPosObj = readObject(0, blockPositionClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + /** + * Get Y position of the block + * @return Block Position Y + */ + public int getY() { + if(isVersionLowerThan_v_1_8) { + return readInt(1); + } + else { + if(blockPosObj == null) { + blockPosObj = readObject(0, blockPositionClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + /** + * Get Z position of the block + * @return Block Position Z + */ + public int getZ() { + if(isVersionLowerThan_v_1_8) { + return readInt(2); + } + else { + if(blockPosObj == null) { + blockPosObj = readObject(0, blockPositionClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + /** + * Get the direction / Get the face. + * @return Direction + */ + public Direction getDirection() { + if(isVersionLowerThan_v_1_8) { + return Direction.values()[readInt(3)]; + } + else { + if(enumDirObj == null) { + enumDirObj = readObject(0, enumDirectionClass); + } + return Direction.valueOf(((Enum)enumDirObj).name()); + } + } + + /** + * Get the PlayerDigType enum sent in this packet. + * @return Dig Type + */ + public PlayerDigType getDigType() { + if(isVersionLowerThan_v_1_8) { + return PlayerDigType.values()[readInt(4)]; + } + else { + return PlayerDigType.valueOf(((Enum)readObject(0, digTypeClass)).name()); + } + } + + public enum PlayerDigType { + START_DESTROY_BLOCK, + ABORT_DESTROY_BLOCK, + STOP_DESTROY_BLOCK, + DROP_ALL_ITEMS, + DROP_ITEM, + RELEASE_USE_ITEM, + SWAP_HELD_ITEMS, + SWAP_ITEM_WITH_OFFHAND, + WRONG_PACKET + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace.java new file mode 100644 index 0000000..65edf8b --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace.java @@ -0,0 +1,98 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.blockplace; + +import io.github.retrooper.packetevents.enums.Direction; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.server.ServerVersion; + + +public final class WrappedPacketInBlockPlace extends WrappedPacket { + private static boolean isHigherThan_v_1_8_8, isHigherThan_v_1_7_10; + + public WrappedPacketInBlockPlace(final Object packet) { + super(packet); + } + + public static void load() { + isHigherThan_v_1_8_8 = version.isHigherThan(ServerVersion.v_1_8_8); + isHigherThan_v_1_7_10 = version.isHigherThan(ServerVersion.v_1_7_10); + WrappedPacketInBlockPlace_1_9.load(); + } + + public Direction getDirection() { + if (isHigherThan_v_1_8_8) { + WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet); + return Direction.valueOf(((Enum) blockPlace_1_9.getEnumDirectionObject()).name()); + } else if (isHigherThan_v_1_7_10) { + WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet); + return Direction.values()[blockPlace_1_8.getFace()]; + } else { + WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet); + return Direction.values()[blockPlace_1_7_10.face]; + } + } + + public int getX() { + if (isHigherThan_v_1_8_8) { + WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet); + return blockPlace_1_9.getX(); + } else if (isHigherThan_v_1_7_10) { + WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet); + return blockPlace_1_8.getX(); + } else { + WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet); + return blockPlace_1_7_10.x; + } + } + + public int getY() { + if (isHigherThan_v_1_8_8) { + WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet); + return blockPlace_1_9.getY(); + + } else if (isHigherThan_v_1_7_10) { + WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet); + return blockPlace_1_8.getY(); + } else { + WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet); + return blockPlace_1_7_10.y; + } + } + + public int getZ() { + if (isHigherThan_v_1_8_8) { + WrappedPacketInBlockPlace_1_9 blockPlace_1_9 = new WrappedPacketInBlockPlace_1_9(packet); + return blockPlace_1_9.getZ(); + } else if (isHigherThan_v_1_7_10) { + WrappedPacketInBlockPlace_1_8 blockPlace_1_8 = new WrappedPacketInBlockPlace_1_8(packet); + return blockPlace_1_8.getZ(); + } else { + WrappedPacketInBlockPlace_1_7_10 blockPlace_1_7_10 = new WrappedPacketInBlockPlace_1_7_10(packet); + return blockPlace_1_7_10.z; + } + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_7_10.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_7_10.java new file mode 100644 index 0000000..c239447 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_7_10.java @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.blockplace; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import org.bukkit.inventory.ItemStack; + +final class WrappedPacketInBlockPlace_1_7_10 extends WrappedPacket { + public int x, y, z; + public ItemStack itemStack; + public int face; + + WrappedPacketInBlockPlace_1_7_10(final Object packet) { + super(packet); + } + + + @Override + protected void setup() { + final net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace blockPlace = + (net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace) packet; + + x = blockPlace.c(); + y = blockPlace.d(); + z = blockPlace.e(); + + this.face = blockPlace.d(); + + net.minecraft.server.v1_7_R4.ItemStack stack = blockPlace.getItemStack(); + this.itemStack = org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack.asBukkitCopy(stack); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_8.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_8.java new file mode 100644 index 0000000..bdfd49f --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_8.java @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.blockplace; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.InvocationTargetException; + +final class WrappedPacketInBlockPlace_1_8 extends WrappedPacket { + private Object blockPosObj; + + WrappedPacketInBlockPlace_1_8(final Object packet) { + super(packet); + } + + public int getX() { + if(blockPosObj == null) { + blockPosObj = readObject(1, NMSUtils.blockPosClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + + public int getY() { + if(blockPosObj == null) { + blockPosObj = readObject(1, NMSUtils.blockPosClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + public int getZ() { + if(blockPosObj == null) { + blockPosObj = readObject(1, NMSUtils.blockPosClass); + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + public ItemStack getItemStack() { + return NMSUtils.toBukkitItemStack(readObject(0, NMSUtils.nmsItemStackClass)); + } + + public int getFace() { + return readInt(0); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_9.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_9.java new file mode 100644 index 0000000..4c9a5f4 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/blockplace/WrappedPacketInBlockPlace_1_9.java @@ -0,0 +1,109 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.blockplace; + + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; + +import java.lang.reflect.InvocationTargetException; + +final class WrappedPacketInBlockPlace_1_9 extends WrappedPacket { + private Object blockPosObj; + private static Class movingObjectPositionBlockClass; + + public static void load() { + movingObjectPositionBlockClass = NMSUtils.getNMSClassWithoutException("MovingObjectPositionBlock"); + } + + public WrappedPacketInBlockPlace_1_9(final Object packet) { + super(packet); + } + + public int getX() { + if (blockPosObj == null) { + if (movingObjectPositionBlockClass == null) { + blockPosObj = readObject(0, NMSUtils.blockPosClass); + } else { + Object movingObjectPos = readObject(0, movingObjectPositionBlockClass); + WrappedPacket movingObjectPosWrapper = new WrappedPacket(movingObjectPos); + blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass); + } + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + public int getY() { + if (blockPosObj == null) { + if (movingObjectPositionBlockClass == null) { + blockPosObj = readObject(0, NMSUtils.blockPosClass); + } else { + Object movingObjectPos = readObject(0, movingObjectPositionBlockClass); + WrappedPacket movingObjectPosWrapper = new WrappedPacket(movingObjectPos); + blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass); + } + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + public int getZ() { + if (blockPosObj == null) { + if (movingObjectPositionBlockClass == null) { + blockPosObj = readObject(0, NMSUtils.blockPosClass); + } else { + Object movingObjectPos = readObject(0, movingObjectPositionBlockClass); + WrappedPacket movingObjectPosWrapper = new WrappedPacket(movingObjectPos); + blockPosObj = movingObjectPosWrapper.readObject(0, NMSUtils.blockPosClass); + } + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return 0; + } + + public Object getEnumDirectionObject() { + if (movingObjectPositionBlockClass == null) { + return readObject(0, NMSUtils.enumDirectionClass); + } else { + Object movingObjectPos = readObject(0, movingObjectPositionBlockClass); + WrappedPacket movingObjectPosWrapper = new WrappedPacket(movingObjectPos); + return movingObjectPosWrapper.readObject(0, NMSUtils.enumDirectionClass); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/chat/WrappedPacketInChat.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/chat/WrappedPacketInChat.java new file mode 100644 index 0000000..febedde --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/chat/WrappedPacketInChat.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.chat; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public final class WrappedPacketInChat extends WrappedPacket { + public WrappedPacketInChat(Object packet) { + super(packet); + } + + /** + * Get the message. + * + * @return Chat Message + */ + public String getMessage() { + return readString(0); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/clientcommand/WrappedPacketInClientCommand.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/clientcommand/WrappedPacketInClientCommand.java new file mode 100644 index 0000000..9238266 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/clientcommand/WrappedPacketInClientCommand.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.clientcommand; + +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; + +public final class WrappedPacketInClientCommand extends WrappedPacket { + private static Class enumClientCommandClass; + private Object enumObj; + + public WrappedPacketInClientCommand(Object packet) { + super(packet); + } + + public static void load() { + Class packetClass = PacketTypeClasses.Client.CLIENT_COMMAND; + + try { + enumClientCommandClass = NMSUtils.getNMSClass("EnumClientCommand"); + } catch (ClassNotFoundException e) { + //Probably a subclass + enumClientCommandClass = SubclassUtil.getSubClass(packetClass, "EnumClientCommand"); + } + } + + /** + * Get the Client Command enum sent in the packet + * + * @return ClientCommand + */ + public ClientCommand getClientCommand() { + if(enumObj == null) { + enumObj = readObject(0, enumClientCommandClass); + } + return ClientCommand.valueOf(enumObj.toString()); + } + + public enum ClientCommand { + PERFORM_RESPAWN, + REQUEST_STATS, + OPEN_INVENTORY_ACHIEVEMENT + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/custompayload/WrappedPacketInCustomPayload.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/custompayload/WrappedPacketInCustomPayload.java new file mode 100644 index 0000000..0448a69 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/custompayload/WrappedPacketInCustomPayload.java @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.custompayload; + +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.bytebuf.ByteBufUtil; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; + +public final class WrappedPacketInCustomPayload extends WrappedPacket { + private static Class nmsMinecraftKey, packetDataSerializerClass, byteBufClass; + + private static boolean strPresent, byteArrayPresent; + + public WrappedPacketInCustomPayload(Object packet) { + super(packet); + } + + public static void load() { + Class packetClass = PacketTypeClasses.Client.CUSTOM_PAYLOAD; + strPresent = Reflection.getField(packetClass, String.class, 0) != null; + byteArrayPresent = Reflection.getField(packetClass, byte[].class, 0) != null; + packetDataSerializerClass = NMSUtils.getNMSClassWithoutException("PacketDataSerializer"); + try { + byteBufClass = NMSUtils.getNettyClass("buffer.ByteBuf"); + } catch (ClassNotFoundException ignored) { + + } + try { + //Only on 1.13+ + nmsMinecraftKey = NMSUtils.getNMSClass("MinecraftKey"); + } catch (ClassNotFoundException e) { + //Its okay, this means they are on versions 1.7.10 - 1.12.2 + } + } + + public String getTag() { + if (strPresent) { + return readString(0); + } else { + Object minecraftKey = readObject(0, nmsMinecraftKey); + WrappedPacket minecraftKeyWrapper = new WrappedPacket(minecraftKey); + return minecraftKeyWrapper.readString(1); + } + } + + public byte[] getData() { + if (byteArrayPresent) { + return readByteArray(0); + } else { + Object dataSerializer = readObject(0, packetDataSerializerClass); + WrappedPacket byteBufWrapper = new WrappedPacket(dataSerializer); + + Object byteBuf = byteBufWrapper.readObject(0, byteBufClass); + + return ByteBufUtil.getBytes(byteBuf); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/entityaction/WrappedPacketInEntityAction.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/entityaction/WrappedPacketInEntityAction.java new file mode 100644 index 0000000..b35ad74 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/entityaction/WrappedPacketInEntityAction.java @@ -0,0 +1,140 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.entityaction; + +import io.github.retrooper.packetevents.annotations.Nullable; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; +import org.bukkit.entity.Entity; + +import java.util.HashMap; + +public final class WrappedPacketInEntityAction extends WrappedPacket { + private static final HashMap cachedPlayerActionNames = new HashMap<>(); + private static final HashMap cachedPlayerActionIDs = new HashMap<>(); + @Nullable + private static Class enumPlayerActionClass; + private static boolean isLowerThan_v_1_8; + private Entity entity; + private int entityId = -1; + + public WrappedPacketInEntityAction(final Object packet) { + super(packet); + } + + public static void load() { + Class entityActionClass = NMSUtils.getNMSClassWithoutException("PacketPlayInEntityAction"); + isLowerThan_v_1_8 = version.isLowerThan(ServerVersion.v_1_8); + if (!isLowerThan_v_1_8) { + enumPlayerActionClass = SubclassUtil.getSubClass(entityActionClass, "EnumPlayerAction"); + } + //All the already existing values + for (PlayerAction val : PlayerAction.values()) { + cachedPlayerActionNames.put(val.name(), val); + } + + cachedPlayerActionNames.put("PRESS_SHIFT_KEY", PlayerAction.START_SNEAKING); + cachedPlayerActionNames.put("RELEASE_SHIFT_KEY", PlayerAction.STOP_SNEAKING); + cachedPlayerActionNames.put("RIDING_JUMP", PlayerAction.START_RIDING_JUMP); + + cachedPlayerActionIDs.put(1, PlayerAction.START_SNEAKING); + cachedPlayerActionIDs.put(2, PlayerAction.STOP_SNEAKING); + cachedPlayerActionIDs.put(3, PlayerAction.STOP_SLEEPING); + cachedPlayerActionIDs.put(4, PlayerAction.START_SPRINTING); + cachedPlayerActionIDs.put(5, PlayerAction.STOP_SPRINTING); + cachedPlayerActionIDs.put(6, PlayerAction.START_RIDING_JUMP); //riding jump + cachedPlayerActionIDs.put(7, PlayerAction.OPEN_INVENTORY); //horse interaction + + } + /** + * Lookup the associated entity by the ID that was sent in the packet. + * + * @return Entity + */ + public Entity getEntity() { + if(entity == null) { + return entity = NMSUtils.getEntityById(getEntityId()); + } + return entity; + } + + /** + * Get the ID of the entity. + * If you do not want to use {@link #getEntity()}, + * you lookup the entity by yourself with this entity ID. + * + * @return Entity ID + */ + public int getEntityId() { + if(entityId == -1) { + entityId = readInt(0); + } + return entityId; + } + + /** + * Get the player action. + * + * @return Player Action + */ + public PlayerAction getAction() { + if (isLowerThan_v_1_8) { + int animationIndex = readInt(1); + return cachedPlayerActionIDs.get(animationIndex); + } else { + final Object enumObj = readObject(0, enumPlayerActionClass); + final String enumValueName = enumObj.toString(); + return cachedPlayerActionNames.get(enumValueName); + } + } + + /** + * Get the jump boost integer. + * + * @return Jump Boost + */ + public int getJumpBoost() { + if (isLowerThan_v_1_8) { + return readInt(2); + } else { + return readInt(1); + } + } + + public enum PlayerAction { + START_SNEAKING, + STOP_SNEAKING, + STOP_SLEEPING, + START_SPRINTING, + STOP_SPRINTING, + START_RIDING_JUMP, + STOP_RIDING_JUMP, + OPEN_INVENTORY, + START_FALL_FLYING + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/flying/WrappedPacketInFlying.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/flying/WrappedPacketInFlying.java new file mode 100644 index 0000000..da54978 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/flying/WrappedPacketInFlying.java @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.flying; + + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketInFlying extends WrappedPacket { + public WrappedPacketInFlying(Object packet) { + super(packet); + } + + public double getX() { + return readDouble(0); + } + + public double getY() { + return readDouble(1); + } + + public double getZ() { + return readDouble(2); + } + + public float getYaw() { + return readFloat(0); + } + + public float getPitch() { + return readFloat(1); + } + + public boolean isOnGround() { + return readBoolean(0); + } + + public final boolean isPosition() { + return readBoolean(1); + } + + public final boolean isLook() { + return readBoolean(2); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/helditemslot/WrappedPacketInHeldItemSlot.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/helditemslot/WrappedPacketInHeldItemSlot.java new file mode 100644 index 0000000..0547b79 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/helditemslot/WrappedPacketInHeldItemSlot.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.helditemslot; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public final class WrappedPacketInHeldItemSlot extends WrappedPacket { + public WrappedPacketInHeldItemSlot(Object packet) { + super(packet); + } + + /** + * Get the index of the item we currently have in hand. + * @return Item in hand Index + */ + public int getCurrentSelectedSlot() { + return readInt(0); + } + +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/keepalive/WrappedPacketInKeepAlive.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/keepalive/WrappedPacketInKeepAlive.java new file mode 100644 index 0000000..7f5e83e --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/keepalive/WrappedPacketInKeepAlive.java @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.keepalive; + +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.reflection.Reflection; + +public final class WrappedPacketInKeepAlive extends WrappedPacket { + private static boolean integerPresentInIndex0; + + public WrappedPacketInKeepAlive(final Object packet) { + super(packet); + } + + public static void load() { + Class packetClass = PacketTypeClasses.Client.KEEP_ALIVE; + integerPresentInIndex0 = Reflection.getField(packetClass, int.class, 0) != null; + } + + /** + * Get the ID response from the client. + * + * You may cast this down to an int if you are on 1.7.10 - 1.12.2. + * On 1.13.2 - 1.16.3 a long is sent. + * @return response ID + */ + public long getId() { + if(!integerPresentInIndex0) { + return readLong(0); + } + else { + return readInt(0); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/settings/WrappedPacketInSettings.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/settings/WrappedPacketInSettings.java new file mode 100644 index 0000000..1634488 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/settings/WrappedPacketInSettings.java @@ -0,0 +1,170 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.settings; + +import io.github.retrooper.packetevents.annotations.Nullable; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; +import io.github.retrooper.packetevents.utils.server.ServerVersion; + +import java.util.HashMap; + +public class WrappedPacketInSettings extends WrappedPacket { + private static Class chatVisibilityEnumClass; + + private static boolean isLowerThan_v_1_8; + private Object chatVisibilityEnumObj; + private HashMap displayedSkinParts = new HashMap<>(); + + public WrappedPacketInSettings(final Object packet) { + super(packet); + } + + public static void load() { + + isLowerThan_v_1_8 = version.isLowerThan(ServerVersion.v_1_8); + + try { + chatVisibilityEnumClass = NMSUtils.getNMSClass("EnumChatVisibility"); + } catch (ClassNotFoundException e) { + Class entityHumanClass = NMSUtils.getNMSClassWithoutException("EntityHuman"); + //They are just on an outdated version + assert entityHumanClass != null; + chatVisibilityEnumClass = SubclassUtil.getSubClass(entityHumanClass, "EnumChatVisibility"); + } + } + + /** + * Get language client setting + * + * @return String Locale + */ + public String getLocale() { + return readString(0); + } + + /** + * Get client view distance. + * + * @return View Distance + */ + public int getViewDistance() { + return readInt(0); + } + + /** + * Get Chat Visibility + * + * @return Chat Visibility + */ + public ChatVisibility getChatVisibility() { + if (chatVisibilityEnumObj == null) { + chatVisibilityEnumObj = readObject(0, chatVisibilityEnumClass); + } + String enumValueAsString = chatVisibilityEnumObj.toString(); + if (enumValueAsString.equals("FULL")) { + return ChatVisibility.ENABLED; + } else if (enumValueAsString.equals("SYSTEM")) { + return ChatVisibility.COMMANDS_ONLY; + } else { + return ChatVisibility.HIDDEN; + } + } + + /** + * Is chat colors + * + * @return Chat Colors + */ + public boolean isChatColors() { + return readBoolean(0); + } + + /** + * Get Displayed skin parts. + *

+ * It is possible for some keys to not exist. + * If that is the case, the server version is 1.7.10. + * 1.7.10 only sends the cape skin part. + * + * @return A map with a Skin Parts as a key, and a boolean as a value. + */ + @Nullable + public HashMap getDisplayedSkinPartsMap() { + if (displayedSkinParts.isEmpty()) { + if (isLowerThan_v_1_8) { + //in 1.7.10 only the cape display skin part is sent + boolean capeEnabled = readBoolean(1); + displayedSkinParts.put(DisplayedSkinPart.CAPE, capeEnabled); + } else { + //in 1.8, all the skin parts are sent + int skinPartFlags = readInt(1); + displayedSkinParts.put(DisplayedSkinPart.CAPE, (skinPartFlags & 0x01) != 0); + displayedSkinParts.put(DisplayedSkinPart.JACKET, (skinPartFlags & 0x02) != 0); + displayedSkinParts.put(DisplayedSkinPart.LEFT_SLEEVE, (skinPartFlags & 0x04) != 0); + displayedSkinParts.put(DisplayedSkinPart.RIGHT_SLEEVE, (skinPartFlags & 0x08) != 0); + displayedSkinParts.put(DisplayedSkinPart.LEFT_PANTS, (skinPartFlags & 0x10) != 0); + displayedSkinParts.put(DisplayedSkinPart.RIGHT_PANTS, (skinPartFlags & 0x20) != 0); + displayedSkinParts.put(DisplayedSkinPart.HAT, (skinPartFlags & 0x40) != 0); + } + } + return displayedSkinParts; + } + + /** + * Is the skin part enabled. + *

+ * On 1.7.10, some skin parts will default to 'false' as 1.7.10 + * only sends the 'cape' skin part. + * + * @param part The skin part to check the status of. + * @return Is the skin part enabled + */ + public boolean isDisplaySkinPartEnabled(DisplayedSkinPart part) { + if(displayedSkinParts.isEmpty()) { + displayedSkinParts = getDisplayedSkinPartsMap(); + } + //1.7.10, we will default the other skin parts to return false. + if (!displayedSkinParts.containsKey(part)) { + return false; + } + return displayedSkinParts.get(part); + } + + /** + * Enum for the client chat visibility setting + */ + public enum ChatVisibility { + ENABLED, COMMANDS_ONLY, HIDDEN + } + + /** + * Enum for the client displayed skin parts settings + */ + public enum DisplayedSkinPart { + CAPE, JACKET, LEFT_SLEEVE, RIGHT_SLEEVE, LEFT_PANTS, RIGHT_PANTS, HAT + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/steervehicle/WrappedPacketInSteerVehicle.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/steervehicle/WrappedPacketInSteerVehicle.java new file mode 100644 index 0000000..900f87f --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/steervehicle/WrappedPacketInSteerVehicle.java @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.steervehicle; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketInSteerVehicle extends WrappedPacket { + public WrappedPacketInSteerVehicle(Object packet) { + super(packet); + } + + /** + * Get the side value. + *

+ * If positive, they are moving to the left, if negative, they are moving to the right. + * + * @return Side Value + */ + public float getSideValue() { + return readFloat(0); + } + + /** + * Get the forward value. + *

+ * If positive, they are moving forward, if negative, they are moving backwards. + * + * @return Forward Value + */ + public float getForwardValue() { + return readFloat(1); + } + + /** + * Is a Jump + * + * @return Is Jump + */ + public boolean isJump() { + return readBoolean(0); + } + + /** + * Is an unmount + * + * @return Is Unmounting + */ + public boolean isUnmount() { + return readBoolean(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/transaction/WrappedPacketInTransaction.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/transaction/WrappedPacketInTransaction.java new file mode 100644 index 0000000..6196101 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/transaction/WrappedPacketInTransaction.java @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.transaction; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public final class WrappedPacketInTransaction extends WrappedPacket { + public WrappedPacketInTransaction(final Object packet) { + super(packet); + } + + /** + * Get the window ID. + * + * @return Window ID + */ + public int getWindowId() { + return readInt(0); + } + + /** + * Get the action number. + * + * @return Action number + */ + public short getActionNumber() { + return readShort(0); + } + + /** + * Is transaction accepted. + * + * @return Transaction Accepted + */ + public boolean isAccepted() { + return readBoolean(0); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/updatesign/WrappedPacketInUpdateSign.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/updatesign/WrappedPacketInUpdateSign.java new file mode 100644 index 0000000..abcae15 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/updatesign/WrappedPacketInUpdateSign.java @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.updatesign; + +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.packetwrappers.out.chat.WrappedPacketOutChat; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; + +import java.lang.reflect.InvocationTargetException; + +public class WrappedPacketInUpdateSign extends WrappedPacket { + private static boolean v_1_7_mode, strArrayMode; + private static Class blockPosClass, iChatBaseComponentClass; + + private Object blockPosObj; + + public static void load() { + v_1_7_mode = Reflection.getField(PacketTypeClasses.Client.UPDATE_SIGN, int.class, 0) != null; + strArrayMode = Reflection.getField(PacketTypeClasses.Client.UPDATE_SIGN, String[].class, 0) != null; + + try { + blockPosClass = NMSUtils.getNMSClass("BlockPosition"); + } catch (ClassNotFoundException e) { + if (!v_1_7_mode) { + e.printStackTrace(); + } + } + try { + iChatBaseComponentClass = NMSUtils.getNMSClass("IChatBaseComponent"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + public WrappedPacketInUpdateSign(Object packet) { + super(packet); + } + + public int getX() { + if (v_1_7_mode) { + return readInt(0); + } else { + if (blockPosObj == null) { + blockPosObj = readObject(0, blockPosClass); + + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getX", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + public int getY() { + if (v_1_7_mode) { + return readInt(1); + } else { + if (blockPosObj == null) { + blockPosObj = readObject(0, blockPosClass); + + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getY", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + public int getZ() { + if (v_1_7_mode) { + return readInt(2); + } else { + if (blockPosObj == null) { + blockPosObj = readObject(0, blockPosClass); + + } + try { + return (int) Reflection.getMethod(blockPosObj.getClass().getSuperclass(), "getZ", 0).invoke(blockPosObj); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return -1; + } + } + + public String[] getTextLines() { + if (strArrayMode) { + //1.7.10 and the newest versions use this + return readStringArray(0); + } else { + //1.8 uses this for example + Object[] iChatComponents = (Object[]) readAnyObject(1); + String[] lines = new String[iChatComponents.length]; + for (int i = 0; i < iChatComponents.length; i++) { + lines[i] = WrappedPacketOutChat. + toStringFromIChatBaseComponent(iChatComponents[i]); + } + return lines; + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/useentity/WrappedPacketInUseEntity.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/useentity/WrappedPacketInUseEntity.java new file mode 100644 index 0000000..94079bc --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/useentity/WrappedPacketInUseEntity.java @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.useentity; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.player.ClientHand; +import io.github.retrooper.packetevents.utils.reflection.SubclassUtil; +import org.bukkit.entity.Entity; + +public final class WrappedPacketInUseEntity extends WrappedPacket { + private static Class enumEntityUseActionClass, enumHandClass; + private Entity entity; + private int entityID = -1; + public WrappedPacketInUseEntity(final Object packet) { + super(packet); + } + + public static void load() { + Class useEntityClass = NMSUtils.getNMSClassWithoutException("PacketPlayInUseEntity"); + enumHandClass = NMSUtils.getNMSClassWithoutException("EnumHand"); + try { + enumEntityUseActionClass = NMSUtils.getNMSClass("EnumEntityUseAction"); + } catch (ClassNotFoundException e) { + //That is fine, it is probably a subclass + assert useEntityClass != null; + enumEntityUseActionClass = SubclassUtil.getSubClass(useEntityClass, "EnumEntityUseAction"); + } + } + + + /** + * Lookup the associated entity by the ID that was sent in the packet. + * @return Entity + */ + public Entity getEntity() { + if(entity != null) { + return entity; + } + return entity = NMSUtils.getEntityById(getEntityID()); + } + + /** + * Get the ID of the entity. + * If you do not want to use {@link #getEntity()}, + * you lookup the entity by yourself with this entity ID. + * @return Entity ID + */ + public int getEntityID() { + if(entityID != -1) { + return entityID; + } + else { + return entityID = readInt(0); + } + } + + /** + * Get the associated action. + * @return Get EntityUseAction + */ + public EntityUseAction getAction() { + final Object useActionEnum = readObject(0, enumEntityUseActionClass); + return EntityUseAction.valueOf(useActionEnum.toString()); + } + + public enum EntityUseAction { + INTERACT, INTERACT_AT, ATTACK, INVALID + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/vehiclemove/WrappedPacketInVehicleMove.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/vehiclemove/WrappedPacketInVehicleMove.java new file mode 100644 index 0000000..c368075 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/vehiclemove/WrappedPacketInVehicleMove.java @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.vehiclemove; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketInVehicleMove extends WrappedPacket { + public WrappedPacketInVehicleMove(Object packet) { + super(packet); + } + + public double getX() { + return readDouble(0); + } + + public double getY() { + return readDouble(1); + } + + public double getZ() { + return readDouble(2); + } + + public float getYaw() { + return readFloat(0); + } + + public float getPitch() { + return readFloat(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/windowclick/WrappedPacketInWindowClick.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/windowclick/WrappedPacketInWindowClick.java new file mode 100644 index 0000000..03dce93 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/in/windowclick/WrappedPacketInWindowClick.java @@ -0,0 +1,222 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.in.windowclick; + +import io.github.retrooper.packetevents.packettype.PacketTypeClasses; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.reflection.Reflection; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class WrappedPacketInWindowClick extends WrappedPacket { + private static final HashMap invClickTypeMapCache = new HashMap<>(); + private static final HashMap> windowClickTypeCache = new HashMap<>(); + private static Class invClickTypeClass; + private static boolean isClickModePrimitive; + // TODO: All the fields below are useless cuz there is never a value assigned to them - change this + private int id; + private int slot; + private int button; + private short actionNumber; + private int mode; + private ItemStack clickedItem; + + public WrappedPacketInWindowClick(Object packet) { + super(packet); + } + + public static void load() { + Class packetClass = PacketTypeClasses.Client.WINDOW_CLICK; + invClickTypeClass = NMSUtils.getNMSClassWithoutException("InventoryClickType"); + + invClickTypeMapCache.put("PICKUP", 0); + invClickTypeMapCache.put("QUICK_MOVE", 1); + invClickTypeMapCache.put("SWAP", 2); + invClickTypeMapCache.put("CLONE", 3); + invClickTypeMapCache.put("THROW", 4); + invClickTypeMapCache.put("QUICK_CRAFT", 5); + invClickTypeMapCache.put("PICKUP_ALL", 6); + + //MODE 0 + windowClickTypeCache.put(0, getArrayListOfWindowClickTypes(WindowClickType.LEFT_MOUSE_CLICK, + WindowClickType.RIGHT_MOUSE_CLICK)); + + //MODE 1 + windowClickTypeCache.put(1, getArrayListOfWindowClickTypes(WindowClickType.SHIFT_LEFT_MOUSE_CLICK, + WindowClickType.SHIFT_RIGHT_MOUSE_CLICK)); + + //MODE 2 + windowClickTypeCache.put(2, getArrayListOfWindowClickTypes( + WindowClickType.KEY_NUMBER1, + WindowClickType.KEY_NUMBER2, + WindowClickType.KEY_NUMBER3, + WindowClickType.KEY_NUMBER4, + WindowClickType.KEY_NUMBER5, + WindowClickType.KEY_NUMBER6, + WindowClickType.KEY_NUMBER7, + WindowClickType.KEY_NUMBER8, + WindowClickType.KEY_NUMBER9)); + + //MODE 3 + windowClickTypeCache.put(3, getArrayListOfWindowClickTypes(WindowClickType.UNKNOWN, WindowClickType.UNKNOWN, WindowClickType.CREATIVE_MIDDLE_CLICK)); + + //MODE 4 + windowClickTypeCache.put(4, getArrayListOfWindowClickTypes(WindowClickType.KEY_DROP, + WindowClickType.KEY_DROP_STACK)); + + //MODE 5 + windowClickTypeCache.put(5, getArrayListOfWindowClickTypes( + WindowClickType.STARTING_LEFT_MOUSE_DRAG, + WindowClickType.ADD_SLOT_LEFT_MOUSE_DRAG, + WindowClickType.ENDING_LEFT_MOUSE_DRAG, + WindowClickType.UNKNOWN, + WindowClickType.STARTING_RIGHT_MOUSE_DRAG, + WindowClickType.ADD_SLOT_RIGHT_MOUSE_DRAG, + WindowClickType.CREATIVE_STARTING_MIDDLE_MOUSE_DRAG, + WindowClickType.ADD_SLOT_MIDDLE_MOUSE_DRAG, + WindowClickType.ENDING_MIDDLE_MOUSE_DRAG)); + + windowClickTypeCache.put(6, getArrayListOfWindowClickTypes(WindowClickType.DOUBLE_CLICK)); + isClickModePrimitive = Reflection.getField(packetClass, int.class, 3) != null; + invClickTypeClass = NMSUtils.getNMSClassWithoutException("InventoryClickType"); + } + + private static ArrayList getArrayListOfWindowClickTypes(WindowClickType... types) { + ArrayList arrayList = new ArrayList(types.length); + arrayList.addAll(Arrays.asList(types)); + return arrayList; + } + + /** + * Get the Window ID. + * @return Get Window ID + */ + public int getWindowID() { + return readInt(0); + } + + /** + * Get the Window slot. + * @return Get Window Slot + */ + public int getWindowSlot() { + return readInt(1); + } + + /** + * Get the Window button. + * @return Get Window Button + */ + public int getWindowButton() { + return readInt(2); + } + + /** + * Get the action number. + * @return Get Action Number + */ + public short getActionNumber() { + return readShort(0); + } + + /** + * Get the window click type. + * @return Get Window Click Type + */ + public WindowClickType getWindowClickType() { + if (windowClickTypeCache.get(mode) == null) { + return WindowClickType.UNKNOWN; + } + if (button + 1 > windowClickTypeCache.size()) { + return WindowClickType.UNKNOWN; + } + + // TODO: This has no use cuz the fields used are never getting assigned a value to them + if (mode == 4) { + if (slot == -999) { + if (button == 0) { + return WindowClickType.LEFT_CLICK_OUTSIDE_WINDOW_HOLDING_NOTHING; + } else if (button == 1) { + return WindowClickType.RIGHT_CLICK_OUTSIDE_WINDOW_HOLDING_NOTHING; + } + } + } + return windowClickTypeCache.get(mode).get(button); + } + + /** + * Get the Window mode. + * @return Get Window Mode. + */ + public int getMode() { + if (isClickModePrimitive) { + return readInt(3); + } else { + return invClickTypeMapCache.get(readObject(5, invClickTypeClass).toString()); + } + } + + /** + * Get the clicked item. + * @return Get Clicked ItemStack + */ + public ItemStack getClickedItem() { + Object nmsItemStack = readObject(0, NMSUtils.nmsItemStackClass); + return NMSUtils.toBukkitItemStack(nmsItemStack); + } + + public enum WindowClickType { + LEFT_MOUSE_CLICK, RIGHT_MOUSE_CLICK, + SHIFT_LEFT_MOUSE_CLICK, SHIFT_RIGHT_MOUSE_CLICK, + + CREATIVE_MIDDLE_CLICK, CREATIVE_STARTING_MIDDLE_MOUSE_DRAG, + + KEY_NUMBER1, KEY_NUMBER2, KEY_NUMBER3, KEY_NUMBER4, + KEY_NUMBER5, KEY_NUMBER6, KEY_NUMBER7, KEY_NUMBER8, + KEY_NUMBER9, KEY_DROP, KEY_DROP_STACK, + + LEFT_CLICK_OUTSIDE_WINDOW_HOLDING_NOTHING, + RIGHT_CLICK_OUTSIDE_WINDOW_HOLDING_NOTHING, + + STARTING_LEFT_MOUSE_DRAG, + STARTING_RIGHT_MOUSE_DRAG, + + ADD_SLOT_LEFT_MOUSE_DRAG, + ADD_SLOT_RIGHT_MOUSE_DRAG, + ADD_SLOT_MIDDLE_MOUSE_DRAG, + + ENDING_LEFT_MOUSE_DRAG, + ENDING_RIGHT_MOUSE_DRAG, + ENDING_MIDDLE_MOUSE_DRAG, + + DOUBLE_CLICK, + + UNKNOWN + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginHandshake.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginHandshake.java new file mode 100644 index 0000000..44a53c5 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginHandshake.java @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.login.in; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketLoginHandshake extends WrappedPacket { + public WrappedPacketLoginHandshake(Object packet) { + super(packet); + } + + public int getProtocolVersion() { + return readInt(0); + } + + public String getHostName() { + return readString(0); + } + + public int getPort() { + return readInt(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInCustomPayload.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInCustomPayload.java new file mode 100644 index 0000000..8f57786 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInCustomPayload.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.login.in; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketLoginInCustomPayload extends WrappedPacket { + public WrappedPacketLoginInCustomPayload(Object packet) { + super(packet); + } + + + /* TODO wrappers + * net.minecraft.server.v1_16_R2.PacketLoginInCustomPayload inCP; + * net.minecraft.server.v1_16_R2.PacketLoginOutCustomPayload outCP; + * net.minecraft.server.v1_16_R2.PacketPlayOutLogin outLogin; + * Find out about the Status Response packet + */ +} + diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInStart.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInStart.java new file mode 100644 index 0000000..15f9390 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrappedPacketLoginInStart.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.login.in; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.gameprofile.GameProfileUtil; +import io.github.retrooper.packetevents.utils.gameprofile.WrappedGameProfile; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; + +public class WrappedPacketLoginInStart extends WrappedPacket { + public WrappedPacketLoginInStart(Object packet) { + super(packet); + } + + public WrappedGameProfile getGameProfile() { + return GameProfileUtil.getWrappedGameProfile(readObject(0, NMSUtils.gameProfileClass)); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrapppedPacketLoginInEncryptionBegin.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrapppedPacketLoginInEncryptionBegin.java new file mode 100644 index 0000000..4b6817b --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/login/in/WrapppedPacketLoginInEncryptionBegin.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.login.in; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrapppedPacketLoginInEncryptionBegin extends WrappedPacket { + public WrapppedPacketLoginInEncryptionBegin(Object packet) { + super(packet); + } + + public byte[] getPublicKey() { + return readByteArray(0); + } + + public byte[] getVerifyToken() { + return readByteArray(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPing.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPing.java new file mode 100644 index 0000000..c506cb3 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPing.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.status; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketStatusPing extends WrappedPacket { + public WrappedPacketStatusPing(Object packet) { + super(packet); + } + + public long getPayload() { + return readLong(0); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPong.java b/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPong.java new file mode 100644 index 0000000..3338ae5 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/packetwrappers/status/WrappedPacketStatusPong.java @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.packetwrappers.status; + +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; + +public class WrappedPacketStatusPong extends WrappedPacket { + public WrappedPacketStatusPong(Object packet) { + super(packet); + } + + public long getPayload() { + return readLong(0); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/settings/PacketEventsSettings.java b/src/main/java/io/github/retrooper/packetevents/settings/PacketEventsSettings.java new file mode 100644 index 0000000..df383a6 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/settings/PacketEventsSettings.java @@ -0,0 +1,92 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.settings; + +import io.github.retrooper.packetevents.utils.server.ServerVersion; + +public class PacketEventsSettings { + private ServerVersion backupServerVersion = ServerVersion.v_1_7_10; + private boolean injectAsync = true; + private boolean ejectAsync = true; + private boolean checkForUpdates = true; + private boolean injectEarly = true; + private int packetHandlingThreadCount = 1; + + public PacketEventsSettings backupServerVersion(ServerVersion serverVersion) { + this.backupServerVersion = serverVersion; + return this; + } + + + public PacketEventsSettings injectAsync(boolean injectAsync) { + this.injectAsync = injectAsync; + return this; + } + + public PacketEventsSettings ejectAsync(boolean ejectAsync) { + this.ejectAsync = ejectAsync; + return this; + } + + public PacketEventsSettings checkForUpdates(boolean checkForUpdates) { + this.checkForUpdates = checkForUpdates; + return this; + } + + + public PacketEventsSettings injectEarly(boolean injectEarly) { + this.injectEarly = injectEarly; + return this; + } + + public PacketEventsSettings packetHandlingThreadCount(int threadCount) { + this.packetHandlingThreadCount = threadCount; + return this; + } + + public ServerVersion getBackupServerVersion() { + return backupServerVersion; + } + + public boolean shouldInjectAsync() { + return injectAsync; + } + + public boolean shouldEjectAsync() { + return ejectAsync; + } + + public boolean shouldCheckForUpdates() { + return checkForUpdates; + } + + public boolean shouldInjectEarly() { + return injectEarly; + } + + public int getPacketHandlingThreadCount() { + return packetHandlingThreadCount; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/updatechecker/UpdateChecker.java b/src/main/java/io/github/retrooper/packetevents/updatechecker/UpdateChecker.java new file mode 100644 index 0000000..71318b0 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/updatechecker/UpdateChecker.java @@ -0,0 +1,79 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.updatechecker; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.utils.server.PEVersion; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; + +public class UpdateChecker { + public void handleUpdate() { + PEVersion localVersion = PacketEvents.getVersion(); + inform("[PacketEvents] Checking for an update, please wait..."); + String line; + try { + line = readLatestVersion(); + } catch (IOException exception) { + report("[PacketEvents] We failed to find the latest released version of PacketEvents. Your build: (" + localVersion.toString() + ")"); + return; + } + PEVersion newVersion = new PEVersion(line); + if (localVersion.isOlder(newVersion)) { + inform("[PacketEvents] There is an update available for the PacketEvents API! Your build: (" + localVersion.toString() + ") | Latest released build: (" + newVersion.toString() + ")"); + } else if (localVersion.isNewer(newVersion)) { + inform("[PacketEvents] You are on a dev or pre released build of PacketEvents. Your build: (" + localVersion.toString() + ") | Latest released build: (" + newVersion.toString() + ")"); + } + else if (localVersion.equals(newVersion)) { + inform("[PacketEvents] You are on the latest released version of PacketEvents. (" + newVersion.toString() + ")"); + } + else { + report("[PacketEvents] Something went wrong while checking for an update. Your build: (" + localVersion.toString() + ") | Latest released build: (" + newVersion.toString() + ")"); + } + } + + private void inform(String message) { + Bukkit.getLogger().info(message); + } + + private void report(String message) { + Bukkit.getLogger().info(ChatColor.DARK_RED + message); + } + + private String readLatestVersion() throws IOException { + URLConnection connection = new URL("https://api.spigotmc.org/legacy/update.php?resource=80279").openConnection(); + connection.addRequestProperty("User-Agent", "Mozilla/4.0"); + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line = reader.readLine(); + reader.close(); + return line; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil.java b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil.java new file mode 100644 index 0000000..8713604 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.bytebuf; + +import io.github.retrooper.packetevents.packetmanager.netty.NettyPacketManager; + +public class ByteBufUtil { + public static Object copiedBuffer(byte[] bytes) { + if (NettyPacketManager.v1_7_nettyMode) { + return ByteBufUtil_7.copiedBuffer(bytes); + } else { + return ByteBufUtil_8.copiedBuffer(bytes); + } + } + + public static byte[] getBytes(Object byteBuf) { + if(NettyPacketManager.v1_7_nettyMode) { + return ByteBufUtil_7.getBytes(byteBuf); + } + else { + return ByteBufUtil_8.getBytes(byteBuf); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_7.java b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_7.java new file mode 100644 index 0000000..72b485b --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_7.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.bytebuf; + +import net.minecraft.util.io.netty.buffer.ByteBuf; +import net.minecraft.util.io.netty.buffer.Unpooled; + +class ByteBufUtil_7 { + public static Object copiedBuffer(byte[] bytes) { + return Unpooled.copiedBuffer(bytes); + } + + public static byte[] getBytes(Object byteBuf) { + ByteBuf bb = (ByteBuf)byteBuf; + byte[] bytes; + if (bb.hasArray()) { + bytes = bb.array(); + } else { + bytes = new byte[bb.readableBytes()]; + bb.getBytes(bb.readerIndex(), bytes); + } + return bytes; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_8.java b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_8.java new file mode 100644 index 0000000..ed159f2 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/bytebuf/ByteBufUtil_8.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.bytebuf; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; + +class ByteBufUtil_8 { + public static Object copiedBuffer(byte[] bytes) { + return Unpooled.copiedBuffer(bytes); + } + + public static byte[] getBytes(Object byteBuf) { + ByteBuf bb = (ByteBuf)byteBuf; + byte[] bytes; + if (bb.hasArray()) { + bytes = bb.array(); + } else { + bytes = new byte[bb.readableBytes()]; + bb.getBytes(bb.readerIndex(), bytes); + } + return bytes; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/entityfinder/EntityFinderUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/entityfinder/EntityFinderUtils.java new file mode 100644 index 0000000..5517bc6 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/entityfinder/EntityFinderUtils.java @@ -0,0 +1,121 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.entityfinder; + +import io.github.retrooper.packetevents.annotations.Nullable; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Entity; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public final class EntityFinderUtils { + public static ServerVersion version; + private static Class worldServerClass; + private static Class craftWorldClass; + private static Class entityClass; + private static Method getEntityByIdMethod; + private static Method craftWorldGetHandle; + private static Method getBukkitEntity; + + private static boolean isServerVersion_v_1_8_x; + + public static void load() { + try { + worldServerClass = NMSUtils.getNMSClass("WorldServer"); + craftWorldClass = NMSUtils.getOBCClass("CraftWorld"); + entityClass = NMSUtils.getNMSClass("Entity"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + try { + getEntityByIdMethod = worldServerClass.getMethod((version.getProtocolVersion() == (short) 47) + ? "a" : "getEntity", int.class); + craftWorldGetHandle = craftWorldClass.getMethod("getHandle"); + getBukkitEntity = entityClass.getMethod("getBukkitEntity"); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + /** + * Get an entity by their ID. + * @param id + * @return Entity + */ + public static Entity getEntityById(final int id) { + for (final World world : Bukkit.getWorlds()) { + final Entity entity = getEntityByIdWithWorld(world, id); + if (entity != null) { + return entity; + } + } + return null; + } + + /** + * Get an entity by their ID, guaranteed to be in the specified world. + * @param world + * @param id + * @return Entity + */ + @Nullable + public static Entity getEntityByIdWithWorld(final World world, final int id) { + if (world == null) { + return null; + } + else if(craftWorldClass == null) { + throw new IllegalStateException("PacketEvents failed to locate the CraftWorld class."); + } + Object craftWorld = craftWorldClass.cast(world); + + Object worldServer = null; + try { + worldServer = craftWorldGetHandle.invoke(craftWorld); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + + Object nmsEntity = null; + try { + nmsEntity = getEntityByIdMethod.invoke(worldServer, id); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + if (nmsEntity == null) { + return null; + } + try { + return (Entity) getBukkitEntity.invoke(nmsEntity); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil.java b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil.java new file mode 100644 index 0000000..6f50e7c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil.java @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.gameprofile; + +import io.github.retrooper.packetevents.packetmanager.netty.NettyPacketManager; + +import java.util.UUID; + +public class GameProfileUtil { + public static Object getGameProfile(UUID uuid, String username) { + if(NettyPacketManager.v1_7_nettyMode) { + return GameProfileUtil_7.getGameProfile(uuid, username); + } + else { + return GameProfileUtil_8.getGameProfile(uuid, username); + } + } + + public static WrappedGameProfile getWrappedGameProfile(Object gameProfile) { + if(NettyPacketManager.v1_7_nettyMode) { + return GameProfileUtil_7.getWrappedGameProfile(gameProfile); + } + else { + return GameProfileUtil_8.getWrappedGameProfile(gameProfile); + } + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_7.java b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_7.java new file mode 100644 index 0000000..0ff0341 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_7.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.gameprofile; + +import net.minecraft.util.com.mojang.authlib.GameProfile; + +import java.util.UUID; + +class GameProfileUtil_7 { + public static Object getGameProfile(UUID uuid, String username) { + return new GameProfile(uuid, username); + } + + public static WrappedGameProfile getWrappedGameProfile(Object gameProfile) { + GameProfile gp = (GameProfile) gameProfile; + return new WrappedGameProfile(gp.getId(), gp.getName(), gp.isLegacy()); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_8.java b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_8.java new file mode 100644 index 0000000..cd673bf --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/GameProfileUtil_8.java @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.gameprofile; + +import com.mojang.authlib.GameProfile; + +import java.util.UUID; + +class GameProfileUtil_8 { + public static Object getGameProfile(UUID uuid, String username) { + return new GameProfile(uuid, username); + } + + public static WrappedGameProfile getWrappedGameProfile(Object gameProfile) { + GameProfile gp = (GameProfile)gameProfile; + return new WrappedGameProfile(gp.getId(), gp.getName(), gp.isLegacy()); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/WrappedGameProfile.java b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/WrappedGameProfile.java new file mode 100644 index 0000000..bea7a65 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/gameprofile/WrappedGameProfile.java @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.gameprofile; + +import java.util.UUID; + +public class WrappedGameProfile { + public UUID id; + public String name; + public boolean legacy; + + public WrappedGameProfile(UUID id, String name, boolean legacy) { + this.id = id; + this.name = name; + this.legacy = legacy; + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public boolean isLegacy() { + return legacy; + } + + public boolean isComplete() { + return id != null && !isBlank(name); + } + + private boolean isBlank(CharSequence cs) { + int strLen; + if (cs != null && (strLen = cs.length()) != 0) { + for (int i = 0; i < strLen; ++i) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/nms/NMSUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/nms/NMSUtils.java new file mode 100644 index 0000000..3a57ef1 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/nms/NMSUtils.java @@ -0,0 +1,314 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.nms; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.annotations.Nullable; +import io.github.retrooper.packetevents.packetwrappers.WrappedPacket; +import io.github.retrooper.packetevents.utils.entityfinder.EntityFinderUtils; +import io.github.retrooper.packetevents.utils.reflection.Reflection; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.UUID; + +public final class NMSUtils { + public static ServerVersion version; + private static final String nmsDir = ServerVersion.getNMSDirectory(); + private static final String obcDir = ServerVersion.getOBCDirectory(); + public static String nettyPrefix = "io.netty"; + public static Class nmsEntityClass, minecraftServerClass, craftWorldClass, playerInteractManagerClass, entityPlayerClass, playerConnectionClass, craftServerClass, + craftPlayerClass, serverConnectionClass, craftEntityClass, + craftItemStack, nmsItemStackClass, networkManagerClass, nettyChannelClass, gameProfileClass, iChatBaseComponentClass, + blockPosClass, enumDirectionClass; + private static Method craftWorldGetHandle; + private static Method getCraftPlayerHandle; + private static Method getCraftEntityHandle; + private static Method asBukkitCopy; + private static Method getBukkitEntity; + private static Field entityPlayerPingField, playerConnectionField; + + public static final HashMap channelCache = new HashMap<>(); + + public static void load() { + try { + Class.forName(nettyPrefix + ".channel.Channel"); + } catch (ClassNotFoundException e) { + nettyPrefix = "net.minecraft.util.io.netty"; + try { + Class.forName(nettyPrefix + ".channel.Channel"); + } catch (ClassNotFoundException e2) { + throw new IllegalStateException("PacketEvents failed to locate Netty's location."); + } + } + try { + nettyChannelClass = getNettyClass("channel.Channel"); + nmsEntityClass = getNMSClass("Entity"); + minecraftServerClass = getNMSClass("MinecraftServer"); + craftWorldClass = getOBCClass("CraftWorld"); + craftPlayerClass = getOBCClass("entity.CraftPlayer"); + craftServerClass = getOBCClass("CraftServer"); + entityPlayerClass = getNMSClass("EntityPlayer"); + playerConnectionClass = getNMSClass("PlayerConnection"); + serverConnectionClass = getNMSClass("ServerConnection"); + craftEntityClass = getOBCClass("entity.CraftEntity"); + craftItemStack = getOBCClass("inventory.CraftItemStack"); + nmsItemStackClass = getNMSClass("ItemStack"); + networkManagerClass = getNMSClass("NetworkManager"); + playerInteractManagerClass = getNMSClass("PlayerInteractManager"); + try { + gameProfileClass = Class.forName("com.mojang.authlib.GameProfile"); + } catch (ClassNotFoundException e) { + gameProfileClass = Class.forName("net.minecraft.util.com.mojang.authlib.GameProfile"); + } + iChatBaseComponentClass = NMSUtils.getNMSClass("IChatBaseComponent"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + blockPosClass = NMSUtils.getNMSClassWithoutException("BlockPosition"); + enumDirectionClass = NMSUtils.getNMSClassWithoutException("EnumDirection"); + //METHODS + try { + getCraftPlayerHandle = craftPlayerClass.getMethod("getHandle"); + getCraftEntityHandle = craftEntityClass.getMethod("getHandle"); + asBukkitCopy = craftItemStack.getMethod("asBukkitCopy", nmsItemStackClass); + craftWorldGetHandle = craftWorldClass.getMethod("getHandle"); + getBukkitEntity = nmsEntityClass.getMethod("getBukkitEntity"); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + + + try { + entityPlayerPingField = entityPlayerClass.getField("ping"); + playerConnectionField = entityPlayerClass.getField("playerConnection"); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } + + public static Object getMinecraftServerInstance() throws InvocationTargetException, IllegalAccessException { + return Reflection.getMethod(minecraftServerClass, "getServer", 0).invoke(null); + } + + public static Object getMinecraftServerConnection() { + WrappedPacket wrapper = null; + try { + wrapper = new WrappedPacket(getMinecraftServerInstance()); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + } + if (wrapper == null) { + return null; + } + return wrapper.readObject(0, serverConnectionClass); + } + + public static double[] recentTPS() throws IllegalAccessException, InvocationTargetException { + final Object minecraftServerObj = getMinecraftServerInstance(); + return (double[]) Reflection.getField(minecraftServerClass, double[].class, 0).get(minecraftServerObj); + } + + public static Class getNMSClass(String name) throws ClassNotFoundException { + return Class.forName(nmsDir + "." + name); + } + + public static Class getNMSClassWithoutException(String name) { + try { + return getNMSClass(name); + } catch (ClassNotFoundException e) { + return null; + } + + } + + public static Class getOBCClass(String name) throws ClassNotFoundException { + return Class.forName(obcDir + "." + name); + } + + public static Class getNettyClass(String name) throws ClassNotFoundException { + return Class.forName(nettyPrefix + "." + name); + } + + public static String getChannelFutureListFieldName() { + if (version.isLowerThan(ServerVersion.v_1_8)) { + return "e"; + } + if (version.isLowerThan(ServerVersion.v_1_13)) { + return "g"; + } + return "f"; + } + + @Nullable + public static Entity getEntityById(final int id) { + return EntityFinderUtils.getEntityById(id); + } + + @Nullable + public static Entity getEntityByIdWithWorld(final World world, final int id) { + return EntityFinderUtils.getEntityByIdWithWorld(world, id); + } + + public static Object getNMSEntity(final Entity entity) { + final Object craftEntity = craftEntityClass.cast(entity); + try { + return getCraftEntityHandle.invoke(craftEntity); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static Entity getBukkitEntity(final Object nmsEntity) { + try { + return (Entity) getBukkitEntity.invoke(nmsEntity); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static Object getCraftPlayer(final Player player) { + return craftPlayerClass.cast(player); + } + + public static Object getEntityPlayer(final Player player) { + try { + return getCraftPlayerHandle.invoke(getCraftPlayer(player)); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static Object getPlayerConnection(final Player player) { + try { + return playerConnectionField.get(getEntityPlayer(player)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + public static Object getNetworkManager(final Player player) { + WrappedPacket wrapper = new WrappedPacket(getPlayerConnection(player)); + return wrapper.readObject(0, networkManagerClass); + } + + public static Object getChannel(final Player player) { + if (PacketEvents.getAPI().packetManager.tinyProtocol == null) { + UUID uuid = player.getUniqueId(); + Object channel = channelCache.get(uuid); + if (channel == null) { + Object newChannel = getChannelNoCache(player); + channelCache.put(uuid, newChannel); + return newChannel; + } + return channel; + } else { + return PacketEvents.getAPI().packetManager.tinyProtocol.getChannel(player); + } + } + + public static Object getChannelNoCache(final Player player) { + if (PacketEvents.getAPI().packetManager.tinyProtocol == null) { + WrappedPacket wrapper = new WrappedPacket(getNetworkManager(player)); + return wrapper.readObject(0, nettyChannelClass); + } else { + return PacketEvents.getAPI().packetManager.tinyProtocol.getChannel(player); + } + } + + public static int getPlayerPing(final Player player) { + Object entityPlayer = getEntityPlayer(player); + try { + return entityPlayerPingField.getInt(entityPlayer); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return -1; + } + + public static ItemStack toBukkitItemStack(final Object nmsItemstack) { + try { + return (ItemStack) asBukkitCopy.invoke(null, nmsItemstack); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static Object convertBukkitServerToNMSServer(Server server) { + Object craftServer = craftServerClass.cast(server); + WrappedPacket wrapper = new WrappedPacket(craftServer); + return wrapper.readObject(0, minecraftServerClass); + } + + public static Object convertBukkitWorldToNMSWorld(World world) { + Object craftWorld = craftWorldClass.cast(world); + + try { + return craftWorldGetHandle.invoke(craftWorld); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static Object createNewEntityPlayer(Server server, World world, Object gameProfile) { + Object nmsServer = convertBukkitServerToNMSServer(server); + Object nmsWorld = convertBukkitWorldToNMSWorld(world); + return privateCreateNewEntityPlayer(nmsServer, nmsWorld, gameProfile); + } + + private static Object privateCreateNewEntityPlayer(Object nmsServer, Object nmsWorld, Object gameProfile) { + Object playerInteractManager = null; + try { + playerInteractManager = playerInteractManagerClass.getConstructor(nmsWorld.getClass()).newInstance(nmsWorld); + } catch (InstantiationException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + + try { + return entityPlayerClass.getConstructor(nmsServer.getClass(), nmsWorld.getClass(), + gameProfile.getClass(), playerConnectionClass). + newInstance(nmsServer, nmsWorld, gameProfile, playerInteractManager); + } catch (InstantiationException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/player/ClientHand.java b/src/main/java/io/github/retrooper/packetevents/utils/player/ClientHand.java new file mode 100644 index 0000000..3a8b3b3 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/player/ClientHand.java @@ -0,0 +1,30 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.player; + +public enum ClientHand { + MAIN_HAND, + OFF_HAND; +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/player/ClientVersion.java b/src/main/java/io/github/retrooper/packetevents/utils/player/ClientVersion.java new file mode 100644 index 0000000..86d63ff --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/player/ClientVersion.java @@ -0,0 +1,117 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.player; + +public enum ClientVersion { + LOWER_THAN_SUPPORTED_VERSIONS(4), + v_1_7_10(5), + v_1_8(47), + v_1_9(107), + v_1_9_1(108), + v_1_9_2(109), + v_1_9_3(110), + v_1_10(210), + v_1_11(315), + v_1_11_1(316), + v_1_12(335), + v_1_12_1(338), + v_1_12_2(340), + v_1_13(393), + v_1_13_1(401), + v_1_13_2(404), + v_1_14(477), + v_1_14_1(480), + v_1_14_2(485), + v_1_14_3(490), + v_1_14_4(498), + v_1_15(573), + v_1_15_1(575), + v_1_15_2(578), + v_1_16(735), + v_1_16_1(736), + v_1_16_2(751), + v_1_16_3(753), + v_1_16_4(754), + HIGHER_THAN_SUPPORTED_VERSIONS(755), + /** + * Pre releases just aren't supported, we would end up with so many enum constants. + */ + ANY_PRE_RELEASE_VERSION(0), + + UNRESOLVED(-1); + + private final int protocolVersion; + + private static final int lowestSupportedProtocolVersion = LOWER_THAN_SUPPORTED_VERSIONS.protocolVersion; + private static final int highestSupportedProtocolVersion = HIGHER_THAN_SUPPORTED_VERSIONS.protocolVersion; + + ClientVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + } + + public int getProtocolVersion() { + return protocolVersion; + } + + public static boolean isHigherThan(ClientVersion a, ClientVersion b) { + return a.getProtocolVersion() > b.getProtocolVersion(); + } + + public static boolean isLowerThan(ClientVersion a, ClientVersion b) { + return a.getProtocolVersion() < b.getProtocolVersion(); + } + + public boolean isHigherThan(ClientVersion target) { + return protocolVersion > target.getProtocolVersion(); + } + + public boolean isLowerThan(ClientVersion target) { + return protocolVersion < target.getProtocolVersion(); + } + + public boolean equals(ClientVersion target) { + return protocolVersion == target.getProtocolVersion(); + } + + public static ClientVersion getClientVersion(int protocolVersion) { + if (protocolVersion == -1) { + return ClientVersion.UNRESOLVED; + } + for (ClientVersion version : ClientVersion.values()) { + if (version.protocolVersion > protocolVersion) { + break; + } else if (version.protocolVersion == protocolVersion) { + return version; + } + } + if (protocolVersion <= lowestSupportedProtocolVersion) { + return LOWER_THAN_SUPPORTED_VERSIONS; + } else if (protocolVersion >= highestSupportedProtocolVersion) { + return HIGHER_THAN_SUPPORTED_VERSIONS; + } else { + return ANY_PRE_RELEASE_VERSION; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/retrooper/packetevents/utils/player/PlayerUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/player/PlayerUtils.java new file mode 100644 index 0000000..9cf88dd --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/player/PlayerUtils.java @@ -0,0 +1,121 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.player; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.impl.PlayerEjectEvent; +import io.github.retrooper.packetevents.packetwrappers.SendableWrapper; +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.UUID; + +public final class PlayerUtils { + public final HashMap playerPingMap = new HashMap<>(); + public final HashMap playerSmoothedPingMap = new HashMap<>(); + + /** + * This map stores each player's client version. + */ + public final HashMap clientVersionsMap = new HashMap<>(); + + /** + * Use reflection to read the ping value NMS calculates for the player. + * NMS smooths the player ping. + * @param player + * @return Get NMS smoothed Player Ping + */ + public int getNMSPing(final Player player) { + return NMSUtils.getPlayerPing(player); + } + + /** + * Use the ping PacketEvents calculates for the player. (Updates every incoming Keep Alive packet) + * @param player Player + * @return Get Ping + */ + public short getPing(final Player player) { + return playerPingMap.getOrDefault(player.getUniqueId(), (short)0); + } + + public short getSmoothedPing(final Player player) { + return playerSmoothedPingMap.getOrDefault(player.getUniqueId(), (short)0); + } + + public short getPing(UUID uuid) { + return playerPingMap.getOrDefault(uuid, (short)0); + } + + public short getSmoothedPing(UUID uuid) { + return playerSmoothedPingMap.getOrDefault(uuid, (short)0); + } + + /** + * Get the client version by a player object. + * @param player + * @return Get Client Version + */ + public ClientVersion getClientVersion(final Player player) { + return clientVersionsMap.get(NMSUtils.getChannel(player)); + } + + /** + * Inject a player. + * + * (This also calls the {@link io.github.retrooper.packetevents.event.impl.PlayerInjectEvent}) + * @param player + */ + public void injectPlayer(final Player player) { + PacketEvents.getAPI().packetManager.injectPlayer(player); + } + + /** + * Eject a player. + * (This also calls the {@link PlayerEjectEvent}) + * @param player + */ + public void ejectPlayer(final Player player) { + PacketEvents.getAPI().packetManager.ejectPlayer(player); + } + + /** + * Send a {@link SendableWrapper} wrapper to a player. + * @param player + * @param sendableWrapper + */ + public void sendPacket(final Player player, final SendableWrapper sendableWrapper) { + PacketEvents.getAPI().packetManager.sendPacket(NMSUtils.getChannel(player), sendableWrapper.asNMSPacket()); + } + + /** + * Send a raw NMS packet to a player. + * @param player + * @param nmsPacket + */ + public void sendNMSPacket(final Player player, Object nmsPacket) { + PacketEvents.getAPI().packetManager.sendPacket(NMSUtils.getChannel(player), nmsPacket); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/reflection/ClassUtil.java b/src/main/java/io/github/retrooper/packetevents/utils/reflection/ClassUtil.java new file mode 100644 index 0000000..67f16f7 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/reflection/ClassUtil.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.reflection; + +import java.util.HashMap; +import java.util.Map; + +public class ClassUtil { + private static final Map classSimpleNameCache = new HashMap<>(); + + public static String getClassSimpleName(Class cls) { + final String className = cls.getName(); + final String simpleClassName; + if (!classSimpleNameCache.containsKey(className)) { + classSimpleNameCache.put(className, simpleClassName = cls.getSimpleName()); + } else { + simpleClassName = classSimpleNameCache.get(className); + } + return simpleClassName; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/reflection/Reflection.java b/src/main/java/io/github/retrooper/packetevents/utils/reflection/Reflection.java new file mode 100644 index 0000000..f49666c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/reflection/Reflection.java @@ -0,0 +1,138 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.reflection; + + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; + +public final class Reflection { + + //FIELDS + @Deprecated + public static Field[] getFields(Class cls) { + Field[] declaredFields = cls.getDeclaredFields(); + for (Field f : declaredFields) { + f.setAccessible(true); + } + return declaredFields; + } + + @Deprecated + public static Field getField(final Class cls, final String name) { + for (final Field f : getFields(cls)) { + if (f.getName().equals(name)) { + return f; + } + } + return null; + } + + @Deprecated + public static Field getField(final Class cls, final Class dataType, final int index) { + int currentIndex = 0; + for (final Field f : getFields(cls)) { + if (f.getType().equals(dataType)) { + if (currentIndex++ == index) { + return f; + } + } + } + return null; + } + + @Deprecated + public static Field getField(final Class cls, final int index) { + return getFields(cls)[index]; + } + + //METHODS + public static Method[] getMethods(final Class cls) { + Method[] declaredMethods = cls.getDeclaredMethods(); + for (Method m : declaredMethods) { + m.setAccessible(true); + } + return declaredMethods; + } + + public static Method getMethod(final Class cls, final int index, final Class... params) { + int currentIndex = 0; + for (final Method m : getMethods(cls)) { + if (Arrays.equals(m.getParameterTypes(), params) && index == currentIndex++) { + return m; + } + } + return null; + } + + public static Method getMethod(Class cls, Class returning, int index, Class... params) { + int currentIndex = 0; + for(Method m : getMethods(cls)) { + if(Arrays.equals(m.getParameterTypes(), params) && m.getReturnType().equals(returning) && index == currentIndex++) { + return m; + } + } + return null; + } + + public static Method getMethod(final Class cls, final String name, Class returning, Class... params) { + for (final Method m : getMethods(cls)) { + if (m.getName().equals(name) && Arrays.equals(m.getParameterTypes(), params) && m.getReturnType().equals(returning)) { + return m; + } + } + return null; + } + + public static Method getMethod(final Class cls, final String name, final int index) { + int currentIndex = 0; + for (final Method m : getMethods(cls)) { + if (m.getName().equals(name) && index == currentIndex++) { + return m; + } + } + return null; + } + + public static Method getMethod(final Class cls, final Class returning, final int index) { + int currentIndex = 0; + for (final Method m : getMethods(cls)) { + if (returning.equals(m.getReturnType()) && index == currentIndex++) { + return m; + } + } + return null; + } + + public static Method getMethod(final Class cls, final String name, final Class returning) { + for (final Method m : getMethods(cls)) { + if (m.getName().equals(name) && m.getReturnType().equals(returning)) { + return m; + } + } + return null; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/reflection/SubclassUtil.java b/src/main/java/io/github/retrooper/packetevents/utils/reflection/SubclassUtil.java new file mode 100644 index 0000000..596a7ec --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/reflection/SubclassUtil.java @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.reflection; + +import java.lang.annotation.Annotation; + +public class SubclassUtil { + public static Class getSubClass(Class cls, String name) { + for (Class subClass : cls.getDeclaredClasses()) { + if (ClassUtil.getClassSimpleName(subClass).equals(name)) { + return subClass; + } + } + return null; + } + + public static Class getSubClass(Class cls, int index) { + int currentIndex = 0; + for (Class subClass : cls.getDeclaredClasses()) { + if (index == currentIndex++) { + return subClass; + } + } + return null; + } + + public static Class getSubClass(Class cls, Annotation annotation, int index) { + int currentIndex = 0; + for(Class subClass : cls.getDeclaredClasses()) { + if(subClass.getAnnotation(annotation.getClass()) != null) { + if(index == currentIndex++) { + return subClass; + } + } + } + return null; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/server/PEVersion.java b/src/main/java/io/github/retrooper/packetevents/utils/server/PEVersion.java new file mode 100644 index 0000000..0fb36b6 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/server/PEVersion.java @@ -0,0 +1,142 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.server; + +import java.util.Arrays; +import java.util.List; + + +//I was lazy while making this class, I know +public class PEVersion { + private final int[] version = new int[4]; + + public PEVersion(int... version) throws IllegalArgumentException { + if (version.length > 4) { + throw new IllegalArgumentException("You are not allowed to have more than 4 arguments!"); + } else if (version.length < 2) { + throw new IllegalArgumentException("You need at least two arguments."); + } + System.arraycopy(version, 0, this.version, 0, version.length); + } + + public PEVersion(String text) { + this(stringConstructor(text)); + } + + private static int[] stringConstructor(String text) { + //1.2.3.4 + text += "."; + + char[] chars = text.toCharArray(); + + //1.2.3.4. + int arrayIndex = 0; + int[] version = new int[4]; + + // String t = "1.2.3.4"; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + + if (c == '.') { + version[arrayIndex++] = + Integer.parseInt(Character.toString(chars[i - 1])); + } + } + return version; + } + + public boolean isNewer(PEVersion other) { + //Our equals check is quite cheap. + //If they are equal, then they aren't older nor newer + if (equals(other)) { + return false; + } + //The version length if guaranteed to be 4. + for (int i = 0; i < version.length; i++) { + if (version[i] > other.getVersion()[i]) { + return true; + } else if (version[i] < other.getVersion()[i]) { + return false; + } + } + return false; + } + + public boolean equals(PEVersion version) { + return Arrays.equals(getVersion(), version.getVersion()); + } + + public boolean isOlder(PEVersion other) { + //Our equals check is quite cheap. + //If they are equal, then they aren't older nor newer + if (equals(other)) { + return false; + } + //The version length if guaranteed to be 4. + for (int i = 0; i < version.length; i++) { + if (version[i] < other.getVersion()[i]) { + return true; + } else if (version[i] > other.getVersion()[i]) { + return false; + } + } + return false; + } + + public final int[] getVersion() { + return version; + } + + public final int[] getVersionShortened() { + int length = 2; + if (version[2] != 0) { + length++; + } + if (version[3] != 0) { + length++; + } + int[] shortened = new int[length]; + System.arraycopy(version, 0, shortened, 0, length); + return shortened; + } + + private int[] convertIntListToIntArray(List list) { + int[] versionArray = new int[list.size()]; + for (int i = 0; i < versionArray.length; i++) { + versionArray[i] = list.get(i); + } + return versionArray; + } + + @Override + public String toString() { + int[] shortenedVersion = getVersionShortened(); + StringBuilder text = new StringBuilder(); + for (int v : shortenedVersion) { + text.append(".").append(v); + } + return text.substring(1); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/server/ServerUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/server/ServerUtils.java new file mode 100644 index 0000000..24eee0c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/server/ServerUtils.java @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.server; + +import io.github.retrooper.packetevents.utils.nms.NMSUtils; +import org.spigotmc.SpigotConfig; + +import java.lang.reflect.InvocationTargetException; + +public final class ServerUtils { + /** + * Get the server version. + * @return Get Server Version + */ + public ServerVersion getVersion() { + return ServerVersion.getVersion(); + } + + /** + * Get recent TPS array from NMS. + * @return Get Recent TPS + */ + public double[] getRecentTPS() { + try { + return NMSUtils.recentTPS(); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return new double[0]; + } + + /** + * Get the current TPS. + * @return Get Current TPS + */ + public double getTPS() { + return getRecentTPS()[0]; + } + + /** + * Get the operating system of the local machine + * @return Get Operating System + */ + public SystemOS getOS() { + return SystemOS.getOperatingSystem(); + } + + public boolean isBungeeCordEnabled() { + return SpigotConfig.bungee; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/server/ServerVersion.java b/src/main/java/io/github/retrooper/packetevents/utils/server/ServerVersion.java new file mode 100644 index 0000000..13a18f0 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/server/ServerVersion.java @@ -0,0 +1,158 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.server; + +import io.github.retrooper.packetevents.PacketEvents; +import org.bukkit.Bukkit; + +/** + * @author retrooper + */ +public enum ServerVersion { + + v_1_7_10((short) 5), v_1_8((short) 47), v_1_8_3((short) 47), v_1_8_4((short) 47), v_1_8_5((short) 47), + v_1_8_6((short) 47), v_1_8_7((short) 47), v_1_8_8((short) 47), v_1_9((short) 107), v_1_9_2((short) 109), + v_1_9_4((short) 110), v_1_10((short) 210), v_1_10_2((short) 210), v_1_11((short) 315), v_1_11_1((short) 316), + v_1_11_2((short) 316), v_1_12((short) 335), v_1_12_1((short) 338), v_1_12_2((short) 340), v_1_13((short) 393), + v_1_13_1((short) 401), v_1_13_2((short) 404), v_1_14((short) 477), v_1_14_1((short) 480), v_1_14_2((short) 485), + v_1_14_3((short) 490), v_1_14_4((short) 498), v_1_15((short) 573), v_1_15_1((short) 575), v_1_15_2((short) 578), + v_1_16((short) 735), v_1_16_1((short) 736), v_1_16_2((short) 751), v_1_16_3((short) 753), v_1_16_4((short)754),ERROR((short) -1); + + private static final String nmsVersionSuffix = Bukkit.getServer().getClass().getPackage().getName() + .replace(".", ",").split(",")[3]; + public static ServerVersion[] reversedValues = new ServerVersion[values().length]; + private static ServerVersion cachedVersion; + private final short protocolId; + + ServerVersion(short protocolId) { + this.protocolId = protocolId; + } + + public short getProtocolVersion() { + return protocolId; + } + + private static ServerVersion getVersionNoCache() { + if (reversedValues[0] == null) { + reversedValues = ServerVersion.reverse(values()); + } + if(reversedValues == null) { + throw new IllegalStateException("Failed to reverse the ServerVersion enum constant values."); + } + for (final ServerVersion val : reversedValues) { + String valName = val.name().substring(2).replace("_", "."); + if (Bukkit.getBukkitVersion().contains(valName)) { + return val; + } + } + if(PacketEvents.getSettings().getBackupServerVersion() != null) { + return PacketEvents.getSettings().getBackupServerVersion(); + } + return ERROR; + } + + public static ServerVersion getVersion() { + if (cachedVersion == null) { + cachedVersion = getVersionNoCache(); + } + return cachedVersion; + } + + public static ServerVersion[] reverse(final ServerVersion[] arr) { + ServerVersion[] array = arr.clone(); + if (array == null) { + return null; + } + int i = 0; + int j = array.length - 1; + ServerVersion tmp; + while (j > i) { + tmp = array[j]; + array[j--] = array[i]; + array[i++] = tmp; + } + return array; + } + + public static String getNmsSuffix() { + return nmsVersionSuffix; + } + + public static String getNMSDirectory() { + return "net.minecraft.server." + getNmsSuffix(); + } + + public static String getOBCDirectory() { + return "org.bukkit.craftbukkit." + getNmsSuffix(); + } + + /** + * Returns if the current version is more up to date than the argument passed + * version + * + * @param version The version to compare to the server's value. + * @return True if the supplied version is lower, false if it is equal or higher + * than the server's version. + */ + public boolean isHigherThan(final ServerVersion version) { + return protocolId > version.protocolId; + } + + /** + * Returns if the current version is more up to date than the argument passed + * version + * + * @param version The version to compare to the server's value. + * @return True if the supplied version is lower or equal, false if it is higher + * than the server's version. + */ + public boolean isHigherThanOrEquals(final ServerVersion version) { + return protocolId >= version.protocolId; + } + + /** + * Returns if the current version is more older than the argument passed + * version + * + * @param version The version to compare to the server's value. + * @return True if the supplied version is higher, false if it is equal or lower + * than the server's version. + */ + public boolean isLowerThan(final ServerVersion version) { + return version.protocolId > protocolId; + } + + /** + * Returns if the current version is more older than the argument passed + * version + * + * @param version The version to compare to the server's value. + * @return True if the supplied version is higher or equal, false if it is lower + * than the server's version. + */ + public boolean isLowerThanOrEquals(final ServerVersion version) { + return version.protocolId >= protocolId; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/server/SystemOS.java b/src/main/java/io/github/retrooper/packetevents/utils/server/SystemOS.java new file mode 100644 index 0000000..226fd2c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/server/SystemOS.java @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.server; + +public enum SystemOS { + WINDOWS, MACOS, LINUX, OTHER; + + private static SystemOS value; + + private static SystemOS getOS() { + final String os = System.getProperty("os.name"); + for (final String osName : getOperatingSystemNames()) { + if (os.toLowerCase().contains(osName.toLowerCase())) { + return SystemOS.valueOf(osName); + } + } + return OTHER; + } + + public static SystemOS getOperatingSystem() { + if (value == null) { + value = getOS(); + } + return value; + } + + public static String[] getOperatingSystemNames() { + final SystemOS[] values = values(); + final String[] arr = new String[values.length - 1]; + for (int i = 0; i < values.length - 1; i++) { + arr[i] = values[i].name(); + } + return arr; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/vector/Vector3i.java b/src/main/java/io/github/retrooper/packetevents/utils/vector/Vector3i.java new file mode 100644 index 0000000..2cd841e --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/vector/Vector3i.java @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.vector; + +public class Vector3i { + public int x, y, z; + public Vector3i() {} + + public Vector3i(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public Vector3i(int[] coordinates) { + x = coordinates[0]; + y = coordinates[1]; + z = coordinates[2]; + } + + public final int getX() { + return x; + } + + public final int getY() { + return y; + } + + public final int getZ() { + return z; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/VersionLookupUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/VersionLookupUtils.java new file mode 100644 index 0000000..d2a24a7 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/VersionLookupUtils.java @@ -0,0 +1,49 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.versionlookup; + +import io.github.retrooper.packetevents.utils.versionlookup.protocollib.ProtocolLibVersionLookupUtils; +import io.github.retrooper.packetevents.utils.versionlookup.protocolsupport.ProtocolSupportVersionLookupUtils; +import io.github.retrooper.packetevents.utils.versionlookup.viaversion.ViaVersionLookupUtils; +import org.bukkit.entity.Player; + +public class VersionLookupUtils { + public static boolean isDependencyAvailable() { + return ViaVersionLookupUtils.isAvailable() + || ProtocolLibVersionLookupUtils.isAvailable() + || ProtocolSupportVersionLookupUtils.isAvailable(); + } + + public static int getProtocolVersion(Player player) { + if (ViaVersionLookupUtils.isAvailable()) { + return ViaVersionLookupUtils.getProtocolVersion(player); + } else if (ProtocolLibVersionLookupUtils.isAvailable()) { + return ProtocolLibVersionLookupUtils.getProtocolVersion(player); + } else if (ProtocolSupportVersionLookupUtils.isAvailable()) { + return ProtocolSupportVersionLookupUtils.getProtocolVersion(player); + } + return -1; + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocollib/ProtocolLibVersionLookupUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocollib/ProtocolLibVersionLookupUtils.java new file mode 100644 index 0000000..9a39936 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocollib/ProtocolLibVersionLookupUtils.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.versionlookup.protocollib; + +import com.comphenix.protocol.ProtocolLibrary; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class ProtocolLibVersionLookupUtils { + public static boolean isAvailable() { + return Bukkit.getPluginManager().isPluginEnabled("ProtocolLib"); + } + + public static int getProtocolVersion(Player player) { + return ProtocolLibrary.getProtocolManager().getProtocolVersion(player); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocolsupport/ProtocolSupportVersionLookupUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocolsupport/ProtocolSupportVersionLookupUtils.java new file mode 100644 index 0000000..abba68c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/protocolsupport/ProtocolSupportVersionLookupUtils.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.versionlookup.protocolsupport; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import protocolsupport.api.ProtocolSupportAPI; + +public class ProtocolSupportVersionLookupUtils { + public static boolean isAvailable() { + return Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport"); + } + + public static int getProtocolVersion(Player player) { + return ProtocolSupportAPI.getProtocolVersion(player).getId(); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/v_1_7_10/ProtocolVersionAccessor_v_1_7.java b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/v_1_7_10/ProtocolVersionAccessor_v_1_7.java new file mode 100644 index 0000000..0b14fb5 --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/v_1_7_10/ProtocolVersionAccessor_v_1_7.java @@ -0,0 +1,34 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.versionlookup.v_1_7_10; + +import org.bukkit.entity.Player; + +public class ProtocolVersionAccessor_v_1_7 { + public static int getProtocolVersion(Player player) { + net.minecraft.server.v1_7_R4.EntityPlayer entityPlayer = ((org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer) player).getHandle(); + return entityPlayer.playerConnection.networkManager.getVersion(); + } +} diff --git a/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/viaversion/ViaVersionLookupUtils.java b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/viaversion/ViaVersionLookupUtils.java new file mode 100644 index 0000000..43dc63c --- /dev/null +++ b/src/main/java/io/github/retrooper/packetevents/utils/versionlookup/viaversion/ViaVersionLookupUtils.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2020 retrooper + * + * 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 io.github.retrooper.packetevents.utils.versionlookup.viaversion; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import us.myles.ViaVersion.api.Via; + +public class ViaVersionLookupUtils { + public static boolean isAvailable() { + return Bukkit.getPluginManager().isPluginEnabled("ViaVersion"); + } + + public static int getProtocolVersion(Player player) { + return Via.getAPI().getPlayerVersion(player.getUniqueId()); + } +} diff --git a/src/main/java/me/liwk/karhu/Karhu.java b/src/main/java/me/liwk/karhu/Karhu.java new file mode 100644 index 0000000..196b7b4 --- /dev/null +++ b/src/main/java/me/liwk/karhu/Karhu.java @@ -0,0 +1,191 @@ +package me.liwk.karhu; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.packetwrappers.out.transaction.WrappedPacketOutTransaction; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import lombok.Getter; +import me.liwk.karhu.commands.ACCommand; +import me.liwk.karhu.commands.sub.KarhuCommand; +import me.liwk.karhu.handler.MovementProcessor; +import me.liwk.karhu.handler.PacketProcessor; +import me.liwk.karhu.listener.PlayerListener; +import me.liwk.karhu.manager.AlertsManager; +import me.liwk.karhu.manager.PlayerDataManager; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.check.EnabledUtil; +import me.liwk.karhu.util.command.CommandFramework; +import me.liwk.karhu.util.file.FileManager; +import me.liwk.karhu.util.update.UpdateCheck; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.util.Date; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/* +PRIVILEGED's LICENSE KEY: 8POV-ISXY-XP8L-IPAN (i keep deleting it and spend 10m trying to find it) + */ + +public class Karhu extends JavaPlugin { + @Getter + private static Karhu instance; + @Getter + private PlayerDataManager playerDataManager; + @Getter + private AlertsManager alertsManager; + @Getter + private CommandFramework framework; + @Getter + private FileManager fileManager; + private double tps; + private long serverTick; + private long lastTickInMs; + @Getter + private Executor executorService; + @Getter private ScheduledExecutorService transactionThread; + @Getter public static ServerVersion SERVER_VERSION; + @Getter + private String build = "1.9.30"; + + final WrappedPacketOutTransaction transaction = new WrappedPacketOutTransaction(0, Short.MIN_VALUE, false); + + @Override + public void onEnable() { + instance = this; + this.fileManager = new FileManager(this); + PacketEvents.getSettings().backupServerVersion(EnabledUtil.checkDefault(Karhu.getInstance().getFileManager().getDefaultVersion())); + PacketEvents.getSettings().injectAsync(true); + PacketEvents.getSettings().ejectAsync(true); + PacketEvents.getSettings().checkForUpdates(false); + PacketEvents.init(this); + this.executorService = Executors.newSingleThreadExecutor(); + this.transactionThread = Executors.newSingleThreadScheduledExecutor(); + this.framework = new CommandFramework(this); + this.alertsManager = new AlertsManager(); + this.playerDataManager = new PlayerDataManager(this); + this.registerCommands(); + SERVER_VERSION = PacketEvents.getAPI().getServerUtils().getVersion(); + //this.DeleteOldFiles(); + + PacketEvents.getAPI().getEventManager().registerListener(new PacketProcessor()); + PacketEvents.getAPI().getEventManager().registerListener(new MovementProcessor()); + + Bukkit.getServer().getPluginManager().registerEvents(new PlayerListener(this), this); + + UpdateCheck.checkVersion(); + + handleTick(); + + runTasks(); + + } + @Override + public void onDisable() { + PacketEvents.stop(); + this.playerDataManager.getPlayerDataMap().clear(); + } + + public void DeleteOldFiles() { + File file = new File(this.getDataFolder() + "/logs"); + if (file.isDirectory()) { + for (File files : Objects.requireNonNull(file.listFiles())) { + long diff = new Date().getTime() - files.lastModified(); + if (diff > 24 * 24 * 60 * 60 * 1000) { + files.deleteOnExit(); + } + } + } + } + + private void registerCommands() { + new ACCommand(); + new KarhuCommand(); + } + + public void handleTick() { + if (!this.transactionThread.isShutdown()) { + this.transactionThread.scheduleAtFixedRate(() -> { + if (this.serverTick % 5 == 0) { + sendTransactions(); + } + }, 50L, 50L, TimeUnit.MILLISECONDS); + } + } + + public void sendTransactions() { + playerDataManager.getPlayerDataMap().values().forEach(data -> { + if (data != null) { + ++data.timerTransactionSent; + if (data.timerTransactionSent > 0) { + data.timerTransactionSent = Short.MIN_VALUE; + } + data.transactionTime.put(data.timerTransactionSent, System.currentTimeMillis()); + PacketEvents.getAPI().getPlayerUtils().sendPacket(data.getDataPlayer(), new WrappedPacketOutTransaction(0, data.timerTransactionSent, false)); + } + }); + } + public void runTasks() { + + new BukkitRunnable() { + + long sec; + long currentSec; + int ticks; + + public void run() { + + if (serverTick == Long.MAX_VALUE) { + serverTick = 0; + } + + ++serverTick; + long ms = System.currentTimeMillis(); + sec = (System.currentTimeMillis() / 1000L); + if (currentSec == sec) { + ticks++; + } else { + currentSec = sec; + tps = (tps == 0.0D ? ticks : (tps + ticks) / 2.0D); + ticks = 1; + } + + lastTickInMs = ms; + } + + }.runTaskTimer(this, 0L, 1L); + + } + + public double getTPS() { + return Math.min(tps + 1.0, 20.0); + } + + public double getLag() { + return MathUtil.round((1.0 - tps / 20.0) * 100.0); + } + + public boolean isServerLagging() { + return tps < 18 || (System.currentTimeMillis() - lastTickInMs) > 100; + } + + public static long getFreeMemory() { + Runtime r = Runtime.getRuntime(); + return r.freeMemory() / 1024L / 1024L; + } + + public static long getMaxMemory() { + Runtime r = Runtime.getRuntime(); + return r.maxMemory() / 1024L / 1024L; + } + + public static long getTotalMemory() { + Runtime r = Runtime.getRuntime(); + return r.totalMemory() / 1024L / 1024L; + } +} diff --git a/src/main/java/me/liwk/karhu/api/events/AlertEvent.java b/src/main/java/me/liwk/karhu/api/events/AlertEvent.java new file mode 100644 index 0000000..61bd8e7 --- /dev/null +++ b/src/main/java/me/liwk/karhu/api/events/AlertEvent.java @@ -0,0 +1,72 @@ +package me.liwk.karhu.api.events; + +import me.liwk.karhu.Karhu; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class AlertEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + private boolean cancelled; + + private Player player; + private Check check; + private int vl; + private String information; + private String data; + + public AlertEvent(Player player, Check check, String information, String data, Integer vl) { + this.player = player; + this.check = check; + this.vl = vl; + this.information = information; + this.data = data; + } + + public Player getPlayer() { + return player; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public Check getCheck() { + return check; + } + + public Integer getVl() { + return vl; + } + + public Integer setVl(Integer newVl) { + PlayerData playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(player); + playerData.setCheckVl(newVl, check); + this.vl = newVl; + return vl; + } + + public String getInformation() { + return information; + } + +} diff --git a/src/main/java/me/liwk/karhu/api/events/BanEvent.java b/src/main/java/me/liwk/karhu/api/events/BanEvent.java new file mode 100644 index 0000000..7e38639 --- /dev/null +++ b/src/main/java/me/liwk/karhu/api/events/BanEvent.java @@ -0,0 +1,51 @@ +package me.liwk.karhu.api.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BanEvent extends Event implements Cancellable { + private static final HandlerList HANDLER_LIST; + private final Player player; + private final String information; + private final String reason; + private boolean cancelled; + + public static HandlerList getHandlerList() { + return BanEvent.HANDLER_LIST; + } + + @Override + public HandlerList getHandlers() { + return BanEvent.HANDLER_LIST; + } + + @Override + public void setCancelled(final boolean cancelled) { + this.cancelled = cancelled; + } + + public Player getPlayer() { + return this.player; + } + + public String getReason() { + return this.reason; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + public BanEvent(final Player player, final String reason, final String information) { + this.player = player; + this.information = information; + this.reason = reason; + } + + static { + HANDLER_LIST = new HandlerList(); + } +} diff --git a/src/main/java/me/liwk/karhu/check/api/Category.java b/src/main/java/me/liwk/karhu/check/api/Category.java new file mode 100644 index 0000000..6fc9087 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/api/Category.java @@ -0,0 +1,5 @@ +package me.liwk.karhu.check.api; + +public enum Category { + COMBAT, MOVEMENT, PACKET; +} diff --git a/src/main/java/me/liwk/karhu/check/api/Check.java b/src/main/java/me/liwk/karhu/check/api/Check.java new file mode 100644 index 0000000..25894d9 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/api/Check.java @@ -0,0 +1,227 @@ +package me.liwk.karhu.check.api; + +import io.github.retrooper.packetevents.event.PacketEvent; +import lombok.Getter; +import lombok.Setter; +import me.liwk.karhu.Karhu; +import me.liwk.karhu.api.events.BanEvent; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.api.events.AlertEvent; +import me.liwk.karhu.util.UtilPlayer; +import me.liwk.karhu.util.check.EnabledUtil; +import me.liwk.karhu.util.file.LogUtil; +import me.liwk.karhu.util.task.Tasker; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public abstract class Check { + @Getter + private String rawName; + @Getter + @Setter + private Player player; + @Getter + private final String name; + @Getter + private final char type; + @Getter + private final Category category; + protected final PlayerData playerData; + + public boolean enabled, punishable, dev; + + public double getVl() { + return playerData.getCheckVl(this); + } + + public void setVl(double vl) { + playerData.setCheckVl(vl, this); + } + + public Check(final String name, final char type, final Category category, PlayerData playerData) { + this.name = name; + this.type = type; + this.category = category; + this.playerData = playerData; + } + + public Check(final Category category, final PlayerData playerData) { + this.playerData = playerData; + Class cls = getClass(); + this.name = cls.getSimpleName(); + this.type = cls.getSimpleName().charAt(cls.getSimpleName().length() - 1); + this.category = category; + } + + public int getBanVL(String name) { + String f = name.replace(" ", ""); + + if(!Karhu.getInstance().getFileManager().getSettings().isSet("checks." + f + ".ban-vl")) { + Karhu.getInstance().getFileManager().getSettings().set("checks." + f + ".ban-vl", 20); + } + + return Karhu.getInstance().getFileManager().getSettings().getInt("checks." + f + ".ban-vl"); + } + + protected void handleFlag(Player player, String information, String data, int maxvl, long time) { + Karhu.getInstance().getExecutorService().execute(() -> { + if (player == null) return; + if (Karhu.getInstance().isServerLagging()) return; + if (playerData.isBanned()) return; + if (playerData.isExempt()) return; + playerData.addViolation(this); + int violations = playerData.getViolations(this, time); + playerData.setCheckVl(violations, this); + this.rawName = information; + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("karhu.alerts")) { + if (Karhu.getInstance().getAlertsManager().hasAlertsToggled(online)) { + TextComponent message = new TextComponent(Karhu.getInstance().getFileManager().getPrefix() + Karhu.getInstance().getFileManager().getAlertMessage().replaceAll("%player%", player.getName()).replaceAll("%check%", information).replaceAll("%vl%", String.valueOf(violations))); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp " + player.getName())); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(Karhu.getInstance().getFileManager().getAlertHoverMessage().replaceAll("%info%", data).replaceAll("%player%", player.getName()).replaceAll("%ping%", String.valueOf(playerData.getTransPing())).replaceAll("%tps%", String.valueOf((int) Karhu.getInstance().getTPS()))).create())); + online.spigot().sendMessage(message); + } + } + } + if (Karhu.getInstance().getFileManager().isLogFileEnable()) { + LogUtil.TextFile logFile = new LogUtil.TextFile("" + player.getName().toLowerCase(), "/logs"); + try { + LogUtil.logToFile(logFile, "%time% | " + ChatColor.stripColor(information).replaceAll("\n", " ") + " [" + ChatColor.stripColor(data.replaceAll("\n", " ") + "]" + " [" + playerData.getTransPing() + "ms]" + "/[" + Karhu.getInstance().getTPS() + " TPS]" + " (x" + violations + ")")); + } catch (Exception ignored) { } + + } + if (category.equals(Category.MOVEMENT)) { + if (Karhu.getInstance().getFileManager().isPullback()) { + Tasker.run(() -> { + UtilPlayer.pullback(playerData.getLastLastLastLocation(), player.getLocation(), player); + }); + } + } + if (violations >= maxvl) { + if (playerData.isExempt()) { + return; + } + if(Karhu.getInstance().getFileManager().isBypass()) { + if(player.hasPermission("karhu.bypass")) return; + } + if (EnabledUtil.checkIfIsAutoban(information.replaceAll(" ", "").replaceAll("§4^", "")) && Karhu.getInstance().getFileManager().isAutoban()) { + if (!playerData.isBanned()) { + Tasker.run(() -> { + Bukkit.getPluginManager().callEvent(new BanEvent(player.getPlayer(), information, data)); + }); + playerData.setBanned(true); + if (Karhu.getInstance().getFileManager().isAutobanWait()) { + if (!EnabledUtil.playersToBeBanned.contains(player)) { + EnabledUtil.playersToBeBanned.add(player); + } + for (Player staff : Bukkit.getOnlinePlayers()) { + if (player.hasPermission("karhu.alerts")) { + TextComponent message = new TextComponent(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getCountDown().replaceAll("%player%", player.getName()))); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/karhu cancelban " + player.getName())); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( + ChatColor.translateAlternateColorCodes('&', "&b&lClick to cancel ban!")).create())); + staff.spigot().sendMessage(message); + } + } + Bukkit.getScheduler().runTaskLaterAsynchronously(Karhu.getInstance(), new Runnable() { + public void run() { + if (EnabledUtil.playersToBeBanned.contains(player)) { + EnabledUtil.playersToBeBanned.remove(player); + Tasker.run(() -> { + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("karhu.alerts")) { + if (Karhu.getInstance().getAlertsManager().hasAlertsToggled(online)) { + TextComponent message = new TextComponent(Karhu.getInstance().getFileManager().getAlertBanMessage().replaceAll("%player%", player.getName()).replaceAll("%check%", information)); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/unban " + player.getName())); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Click to UNBAN\n" + Karhu.getInstance().getFileManager().getAlertHoverMessage().replaceAll("%info%", data).replaceAll("%player%", player.getName()).replaceAll("%ping%", String.valueOf(playerData.getTransPing())).replaceAll("%tps%", String.valueOf((int) Karhu.getInstance().getTPS()))).create())); + online.spigot().sendMessage(message); + } + } + } + LogUtil.TextFile logFile = new LogUtil.TextFile("" + player.getName().toLowerCase(), "/logs"); + try { + LogUtil.logToFile(logFile, "PUNISHMENT APPLIED @ %time% |" + ChatColor.stripColor(information).replaceAll("\n", " ") + " [" + ChatColor.stripColor(data.replaceAll("\n", " ") + "]" + " [" + playerData.getTransPing() + "ms]" + "/[" + Karhu.getInstance().getTPS() + " TPS]" + " (x" + violations + ")")); + } catch (Exception ex) { + //IGNORED + } + Tasker.run(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), Karhu.getInstance().getFileManager().getBanCommand().replaceAll("%player%", player.getName()).replaceAll("%check%", information)); + }); + }); + } else { + playerData.setBanned(false); + } + } + }, 20 * 15); + } else { + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("karhu.alerts")) { + if (Karhu.getInstance().getAlertsManager().hasAlertsToggled(online)) { + TextComponent message = new TextComponent(Karhu.getInstance().getFileManager().getAlertBanMessage().replaceAll("%player%", player.getName()).replaceAll("%check%", information)); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/unban " + player.getName())); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Click to UNBAN\n" + Karhu.getInstance().getFileManager().getAlertHoverMessage().replaceAll("%info%", data).replaceAll("%player%", player.getName()).replaceAll("%ping%", String.valueOf(playerData.getTransPing())).replaceAll("%tps%", String.valueOf((int) Karhu.getInstance().getTPS()))).create())); + online.spigot().sendMessage(message); + } + } + } + LogUtil.TextFile logFile = new LogUtil.TextFile("" + player.getName().toLowerCase(), "/logs"); + try { + LogUtil.logToFile(logFile, "PUNISHMENT APPLIED @ %time% | " + ChatColor.stripColor(information).replaceAll("\n", " ") + " [" + ChatColor.stripColor(data.replaceAll("\n", " ") + "]" + " [" + playerData.getTransPing() + "ms]" + "/[" + Karhu.getInstance().getTPS() + " TPS]" + " (x" + violations + ")")); + } catch (Exception ex) { + //IGNORED + } + Tasker.run(() -> { + Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), Karhu.getInstance().getFileManager().getBanCommand().replaceAll("%player%", player.getName()).replaceAll("%check%", information)); + }); + } + } + } + } + Tasker.run(() -> { + Bukkit.getPluginManager().callEvent(new AlertEvent(player.getPlayer(), this, information, data, violations)); + }); + }); + } + + protected void debug(Player player, String name, String stuff, Boolean broadcast) { + if(broadcast) { + Bukkit.broadcastMessage("§7[§c§lDEBUG§7] §c" + player.getName() + " §7N: §c" + name + " §7D: §c" + stuff); + } else { + player.sendMessage("§7[§c§lDEBUG§7] §c" + player.getName() + " §7N: §c" + name + " §7D: §c" + stuff); + } + } + + protected void handleVerbose(Player player, String information, String data) { + Karhu.getInstance().getExecutorService().execute(() -> { + if (player == null) return; + if (playerData.isExempt()) { + return; + } + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("karhu.alerts")) { + if (Karhu.getInstance().getAlertsManager().hasDevAlertsToggled(online)) { + TextComponent message = new TextComponent(Karhu.getInstance().getFileManager().getVerbosePrefix() + Karhu.getInstance().getFileManager().getVerboseMessage().replaceAll("%player%", player.getName()).replaceAll("%check%", information)); + message.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp " + player.getName())); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(Karhu.getInstance().getFileManager().getAlertHoverMessage().replaceAll("%info%", data).replaceAll("%player%", player.getName()).replaceAll("%ping%", String.valueOf(playerData.getTransPing())).replaceAll("%tps%", String.valueOf((int) Karhu.getInstance().getTPS()))).create())); + online.spigot().sendMessage(message); + } + } + } + }); + } + + public abstract void handle(PacketEvent e, Player player); + + /*public void handle(final PacketEvent event) { + }*/ + + public void handleMovement(final PacketEvent event, Location to, Location from) { + } + +} diff --git a/src/main/java/me/liwk/karhu/check/api/CheckList.java b/src/main/java/me/liwk/karhu/check/api/CheckList.java new file mode 100644 index 0000000..0539cfb --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/api/CheckList.java @@ -0,0 +1,88 @@ +package me.liwk.karhu.check.api; + +public class CheckList { + /*private static int checkSize; + + public static int getChecksAmount() { + return checkSize; + } + + public static void register(Check check, List curChecks){ + curChecks.add(check); + } + + public static List setupPlayer(Player player) { + List playerChecks = new ArrayList<>(); + playerChecks.add(new KillauraA()); + playerChecks.add(new KillauraB()); + playerChecks.add(new KillauraC()); + playerChecks.add(new KillauraD()); + playerChecks.add(new KillauraE()); + playerChecks.add(new KillauraF()); + + playerChecks.add(new AimA()); + playerChecks.add(new AimB()); + playerChecks.add(new AimC()); + playerChecks.add(new AimD()); + playerChecks.add(new AimE()); + playerChecks.add(new AimF()); + playerChecks.add(new AimG()); + playerChecks.add(new AimH()); + playerChecks.add(new AimI()); + ////register(new AimJ()); + + playerChecks.add(new ReachA()); + playerChecks.add(new ReachB()); + + playerChecks.add(new HitboxA()); + + playerChecks.add(new AutoclickerA()); + playerChecks.add(new AutoclickerB()); + playerChecks.add(new AutoclickerC()); + + playerChecks.add(new VelocityA()); + playerChecks.add(new VelocityB()); + + //MOVEMENT + playerChecks.add(new FlyA()); + playerChecks.add(new FlyB()); + + //register(new BoatFlyA()); + + playerChecks.add(new NofallA()); + playerChecks.add(new NofallB()); + + playerChecks.add(new JesusA()); + + playerChecks.add(new NoSlowA()); + + playerChecks.add(new SpeedA()); + + playerChecks.add(new SpeedB()); + playerChecks.add(new SpeedC()); + playerChecks.add(new SpeedD()); + + playerChecks.add(new OmniSprintA()); + + playerChecks.add(new FastLadderA()); + playerChecks.add(new MotionA()); + playerChecks.add(new MotionB()); + + playerChecks.add(new StepA()); + playerChecks.add(new StepB()); + + //PACKET + playerChecks.add(new BadA()); + playerChecks.add(new BadB()); + playerChecks.add(new BadC()); + + playerChecks.add(new TimerA()); + playerChecks.add(new TimerB()); + + playerChecks.add(new PingSpoofA()); + playerChecks.add(new PingSpoofB()); + checkSize = playerChecks.size(); + return playerChecks; + }*/ + +} diff --git a/src/main/java/me/liwk/karhu/check/api/manager/CheckManager.java b/src/main/java/me/liwk/karhu/check/api/manager/CheckManager.java new file mode 100644 index 0000000..047b12a --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/api/manager/CheckManager.java @@ -0,0 +1,166 @@ +package me.liwk.karhu.check.api.manager; + +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.ImmutableClassToInstanceMap; +import lombok.Getter; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.check.impl.aimassist.*; +import me.liwk.karhu.check.impl.aimassist.smooth.Mouse; +import me.liwk.karhu.check.impl.aimassist.smooth.Optifine; +import me.liwk.karhu.check.impl.autoclicker.*; +import me.liwk.karhu.check.impl.badpackets.*; +import me.liwk.karhu.check.impl.boatfly.BoatFlyA; +import me.liwk.karhu.check.impl.fastladder.FastLadderA; +import me.liwk.karhu.check.impl.fly.FlyA; +import me.liwk.karhu.check.impl.fly.FlyB; +import me.liwk.karhu.check.impl.hitbox.HitboxA; +import me.liwk.karhu.check.impl.inventory.InventoryA; +import me.liwk.karhu.check.impl.inventory.InventoryB; +import me.liwk.karhu.check.impl.jesus.JesusA; +import me.liwk.karhu.check.impl.killaura.*; +import me.liwk.karhu.check.impl.motion.MotionA; +import me.liwk.karhu.check.impl.motion.MotionB; +import me.liwk.karhu.check.impl.motion.MotionC; +import me.liwk.karhu.check.impl.nofall.NofallA; +import me.liwk.karhu.check.impl.nofall.NofallB; +import me.liwk.karhu.check.impl.noslow.NoSlowA; +import me.liwk.karhu.check.impl.noslow.NoSlowB; +import me.liwk.karhu.check.impl.pingspoof.PingSpoofA; +import me.liwk.karhu.check.impl.pingspoof.PingSpoofB; +import me.liwk.karhu.check.impl.reach.ReachA; +import me.liwk.karhu.check.impl.reach.ReachB; +import me.liwk.karhu.check.impl.scaffold.ScaffoldA; +import me.liwk.karhu.check.impl.speed.SpeedA; +import me.liwk.karhu.check.impl.speed.SpeedB; +import me.liwk.karhu.check.impl.speed.SpeedC; +import me.liwk.karhu.check.impl.sprint.OmniSprintA; +import me.liwk.karhu.check.impl.step.StepA; +import me.liwk.karhu.check.impl.step.StepB; +import me.liwk.karhu.check.impl.timer.TimerA; +import me.liwk.karhu.check.impl.timer.TimerB; +import me.liwk.karhu.check.impl.velocity.VelocityA; +import me.liwk.karhu.check.impl.velocity.VelocityB; +import me.liwk.karhu.data.PlayerData; + +import java.util.Collection; + +//import me.liwk.karhu.check.impl.hitbox.HitboxA; + +@Getter +public final class CheckManager { + private final ClassToInstanceMap checks; + + public CheckManager(final PlayerData playerData) { + checks = new ImmutableClassToInstanceMap.Builder() + + .put(KillauraA.class, new KillauraA(playerData)) + .put(KillauraB.class, new KillauraB(playerData)) + .put(KillauraC.class, new KillauraC(playerData)) + .put(KillauraD.class, new KillauraD(playerData)) + .put(KillauraE.class, new KillauraE(playerData)) + .put(KillauraF.class, new KillauraF(playerData)) + .put(KillauraG.class, new KillauraG(playerData)) + .put(KillauraH.class, new KillauraH(playerData)) + .put(KillauraI.class, new KillauraI(playerData)) + .put(KillauraJ.class, new KillauraJ(playerData)) + + .put(HitboxA.class, new HitboxA(playerData)) + + .put(ReachA.class, new ReachA(playerData)) + .put(ReachB.class, new ReachB(playerData)) + + .put(VelocityA.class, new VelocityA(playerData)) + .put(VelocityB.class, new VelocityB(playerData)) + + .put(Optifine.class, new Optifine(playerData)) + .put(Mouse.class, new Mouse(playerData)) + + .put(AimA.class, new AimA(playerData)) + .put(AimB.class, new AimB(playerData)) + .put(AimC.class, new AimC(playerData)) + .put(AimD.class, new AimD(playerData)) + .put(AimE.class, new AimE(playerData)) + .put(AimF.class, new AimF(playerData)) + .put(AimG.class, new AimG(playerData)) + .put(AimH.class, new AimH(playerData)) + .put(AimI.class, new AimI(playerData)) + .put(AimJ.class, new AimJ(playerData)) + .put(AimK.class, new AimK(playerData)) + .put(AimL.class, new AimL(playerData)) + .put(AimM.class, new AimM(playerData)) + .put(AimO.class, new AimO(playerData)) + + .put(BadA.class, new BadA(playerData)) + .put(BadB.class, new BadB(playerData)) + .put(BadC.class, new BadC(playerData)) + .put(BadD.class, new BadD(playerData)) + .put(BadE.class, new BadE(playerData)) + + .put(FlyA.class, new FlyA(playerData)) + .put(FlyB.class, new FlyB(playerData)) + + .put(BoatFlyA.class, new BoatFlyA(playerData)) + + .put(StepA.class, new StepA(playerData)) + .put(StepB.class, new StepB(playerData)) + + //.put(PhaseA.class, new PhaseA(playerData)) + + .put(AutoclickerA.class, new AutoclickerA(playerData)) + .put(AutoclickerB.class, new AutoclickerB(playerData)) + .put(AutoclickerC.class, new AutoclickerC(playerData)) + .put(AutoclickerD.class, new AutoclickerD(playerData)) + .put(AutoclickerE.class, new AutoclickerE(playerData)) + .put(AutoclickerF.class, new AutoclickerF(playerData)) + + .put(TimerA.class, new TimerA(playerData)) + .put(TimerB.class, new TimerB(playerData)) + + .put(SpeedA.class, new SpeedA(playerData)) + .put(SpeedB.class, new SpeedB(playerData)) + .put(SpeedC.class, new SpeedC(playerData)) + + .put(NoSlowA.class, new NoSlowA(playerData)) + .put(NoSlowB.class, new NoSlowB(playerData)) + + .put(MotionA.class, new MotionA(playerData)) + .put(MotionB.class, new MotionB(playerData)) + .put(MotionC.class, new MotionC(playerData)) + + .put(InventoryA.class, new InventoryA(playerData)) + .put(InventoryB.class, new InventoryB(playerData)) + + .put(FastLadderA.class, new FastLadderA(playerData)) + + .put(ScaffoldA.class, new ScaffoldA(playerData)) + + .put(NofallA.class, new NofallA(playerData)) + .put(NofallB.class, new NofallB(playerData)) + + .put(OmniSprintA.class, new OmniSprintA(playerData)) + + .put(JesusA.class, new JesusA(playerData)) + + .put(PingSpoofA.class, new PingSpoofA(playerData)) + .put(PingSpoofB.class, new PingSpoofB(playerData)) + .build(); + } + + public Collection getChecks() { + if(checks != null) + return checks.values(); + return null; + } + + public int checkAmount() { + if(this.getChecks() != null) + return this.getChecks().size(); + return 0; + } + + public Check getCheck(final Class clazz) { + return checks.getInstance(clazz); + } + + +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimA.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimA.java new file mode 100644 index 0000000..a51c558 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimA.java @@ -0,0 +1,55 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimA extends Check { + + private float lastPitch, lastYaw; + private double buffer; + private double streak; + + public AimA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if(playerData.getLastAttackTick() > 10) return; + + float diffYaw = Math.abs(to.getYaw() - from.getYaw()); + float diffPitch = Math.abs(to.getPitch() - from.getPitch()); + + float yawAccel = Math.abs(diffYaw - lastYaw); + float pitchAccel = Math.abs(diffPitch - lastPitch); + + //Bukkit.broadcastMessage("§7[§c§lPRE-DEBUG§7] §fpA: §c" + pitchAccel + " §fyA: §c" + yawAccel); + + if(yawAccel > 1.5 && pitchAccel > 0.7 && diffPitch > 0.5 && diffPitch < 1.4 && pitchAccel < 2.25) { + if(++buffer > 3) { + buffer = 0; + if(++streak > 2) { + handleFlag(player, "AimAssist A", "§b* §fpA=§b" + pitchAccel + "\n§b* §fyA=§b" + yawAccel, getBanVL("AimA"), 30000L); + } + } + } else { + buffer = Math.max(buffer - 0.5, 0); + streak = Math.max(streak - 0.25, 0); + } + + lastYaw = diffYaw; + lastPitch = diffPitch; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimB.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimB.java new file mode 100644 index 0000000..1d00051 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimB.java @@ -0,0 +1,55 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimB extends Check { + + private int vl; + + public AimB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if(playerData.getLastAttackTick() > 5) return; + + if(to.getYaw() == from.getYaw()) return; + + float fromYaw = (from.getYaw() - 90) % 360F; + float toYaw = (to.getYaw() - 90) % 360F; + + + if (fromYaw < 0F) + fromYaw += 360F; + + if (toYaw < 0F) + toYaw += 360F; + + double diffYaw = Math.abs(toYaw - fromYaw); + + if (diffYaw > 0D && playerData.getLastServerPositionTick() > 60) { + if (diffYaw % 1 == 0D) { + if ((vl += 12) > 35) { + handleFlag(player, "AimAssist B", "§b* §fSmoothing incorrectly" + "\n§b* §fdiff=§b" + diffYaw + "\n§b* §fyaw=§b" + fromYaw, getBanVL("AimB"), 300000L); + } + handleVerbose(player, "AimAssist B", "§b* §fSmoothing incorrectly" + "\n§b* §fdiff=§b" + diffYaw + "\n§b* §fyaw=§b" + fromYaw); + } else vl -= 2; + } + } + + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimC.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimC.java new file mode 100644 index 0000000..409859c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimC.java @@ -0,0 +1,43 @@ +package me.liwk.karhu.check.impl.aimassist; + + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimC extends Check { + + private int buffer; + + public AimC(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + if (event.getTimestamp() - playerData.getLastAttackPacket() > 500L) { + return; + } + + float yawChange = MathUtil.getDistanceBetweenAngles(to.getYaw(), from.getYaw()); + if (yawChange > 0 && Math.abs(Math.floor(yawChange) - yawChange) < 1.0E-10) { + if (++buffer > 2) { + handleFlag(player, "AimAssist C", "§b* §fRound YAW" + "\n§b* §fyaw=§b" + yawChange, getBanVL("AimC"), 300000L); + } else { + buffer = 0; + } + } + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimD.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimD.java new file mode 100644 index 0000000..acb3d5c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimD.java @@ -0,0 +1,64 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimD extends Check { + + private float lastpitch, lastyaw; + private double buffer, buffer2; + + public AimD(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.getLastAttackTick() > 5) return; + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + final float yawAccel = Math.abs(this.lastyaw - deltaYaw); + final float pitchAccel = Math.abs(this.lastpitch - deltaPitch); + + double range = 100; + if(playerData.getLastTarget() != null) { + Player target = playerData.getLastTarget(); + if (target != null) { + range = player.getEyeLocation().clone().toVector().setY(0.0D).distance(target.getEyeLocation().clone().toVector().setY(0.0D)); + } + } + + if (yawAccel > 0 && deltaYaw > 1 && deltaPitch > 0.278 && deltaPitch < 0.29) { + if (++buffer2 > 3) { + handleFlag(player, "AimAssist D", "§b* §fP=§b" + deltaPitch + "\n§b* §fY=§b" + deltaYaw, getBanVL("AimD"), 300000L); + } + } else { + buffer2 = Math.max(buffer2 - 1, 0); + } + + if (yawAccel > 1.5 && (pitchAccel < 0.05 && pitchAccel > 0.0 || pitchAccel == 0) && range > 0.5) { + if (++buffer > 5) { + handleFlag(player, "AimAssist D", "§b* §fpA=§b" + pitchAccel + "\n§b* §fyA=§b" + yawAccel, getBanVL("AimD"), 300000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + this.lastyaw = deltaYaw; + this.lastpitch = deltaPitch; + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimE.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimE.java new file mode 100644 index 0000000..fb59904 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimE.java @@ -0,0 +1,60 @@ +package me.liwk.karhu.check.impl.aimassist; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class AimE extends Check { + + private final Deque samples = Lists.newLinkedList(); + private double buffer; + + public AimE(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaYaw > 0.0 && deltaPitch > 0.0 && deltaYaw < 30.f && deltaPitch < 30.f) { + samples.add(deltaPitch); + } + + if (samples.size() == 120) { + final int distinct = (int) (samples.stream().distinct().count()); + final int duplicates = samples.size() - distinct; + + final double average = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + + if (duplicates <= 16 && duplicates > 6 && average > 7.5f && average < 25.f) { + if (++buffer > 1) { + handleFlag(player, "AimAssist E", "§7Help from Elevated\n\n§b* §fRandomized aim\n§b* §faverage=§b" + average, getBanVL("AimE"), 300000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + + samples.clear(); + } + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimF.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimF.java new file mode 100644 index 0000000..dfd5123 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimF.java @@ -0,0 +1,59 @@ +package me.liwk.karhu.check.impl.aimassist; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class AimF extends Check { + + private final Deque samples = Lists.newLinkedList(); + private double buffer; + + public AimF(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaYaw > 0.0 && deltaPitch > 0.0 && deltaYaw < 30.f && deltaPitch < 30.f) { + samples.add(deltaPitch); + } + + if (samples.size() == 120) { + final int distinct = (int) (samples.stream().distinct().count()); + final int duplicates = samples.size() - distinct; + + final double average = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + + if (duplicates >= 10 && average > 4.1f && average < 12.f) { + if (++buffer > 5.25) { + handleFlag(player, "AimAssist F", "§b* §fShaky randomized aim\n§b* §faverage=§b" + average, getBanVL("AimF"), 300000L); + } + } else { + buffer = Math.max(buffer - 1.25, 0); + } + + samples.clear(); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimG.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimG.java new file mode 100644 index 0000000..1253913 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimG.java @@ -0,0 +1,46 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimG extends Check { + + private int buffer; + + public AimG(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 2) { + return; + } + + if(playerData.isCinematic()) return; + + float diffYaw = Math.abs(to.getYaw() - from.getYaw()); + float diffPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (diffYaw <= 3.0 && diffYaw > 1 && diffPitch > 4.035 && diffPitch < 7) { + if (++buffer > 4) { + handleFlag(player, "AimAssist G", "§b* §fpitch=§b" + diffPitch + "\n§b* §fyaw=§b" + diffYaw, getBanVL("AimG"), 300000L); + } + } else { + buffer = 0; + } + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimH.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimH.java new file mode 100644 index 0000000..536430b --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimH.java @@ -0,0 +1,57 @@ +package me.liwk.karhu.check.impl.aimassist; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class AimH extends Check { + + private final Deque samples = Lists.newLinkedList(); + private double buffer; + + public AimH(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaPitch > 0.0 && deltaPitch < 40.f) { + samples.add(deltaPitch); + } + + if (samples.size() == 40) { + final int distinct = (int) (samples.stream().distinct().count()); + final int duplicates = samples.size() - distinct; + + final double average = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + + if (average > 9.7f && average < 26.5f && duplicates >= 2) { + if (++buffer > 2) { + handleFlag(player, "AimAssist H", "§b* §fDuplicated pattern\n§b* §faverage=§b" + average + "\n§b* §fduplicates=§b" + duplicates, getBanVL("AimH"), 300000L); + } + } else buffer = Math.max(buffer - 0.5, 0); + + samples.clear(); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimI.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimI.java new file mode 100644 index 0000000..ef22f77 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimI.java @@ -0,0 +1,73 @@ +package me.liwk.karhu.check.impl.aimassist; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class AimI extends Check { + + public AimI(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + + private final Deque samplesP = Lists.newLinkedList(); + private final Deque samplesY = Lists.newLinkedList(); + private double buffer; + + private double lastAveragePitch; + private double lastAverageYaw; + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaYaw > 0.0 && deltaPitch > 0.0 && deltaYaw < 30.f && deltaPitch < 30.f) { + samplesP.add(deltaPitch); + samplesY.add(deltaYaw); + } + + if (samplesP.size() == 20 && samplesY.size() == 20) { + + final double averagePitch = samplesP.stream().mapToDouble(d -> d).average().orElse(0.0); + final double averageYaw = samplesY.stream().mapToDouble(d -> d).average().orElse(0.0); + + final double finalPitch = averagePitch - lastAveragePitch; + final double finalYaw = averageYaw - lastAverageYaw; + + + if (finalPitch > 0.65f && finalPitch < 1.2f && finalYaw > 3F && finalYaw < 5.2f) { + if (++buffer> 3) { + handleFlag(player, "AimAssist I", "§b* §fSmooth aim\n§b* §faveragePitch=§b" + averagePitch + "\n§b* §faverageYaw=§b" + averageYaw, getBanVL("AimI"), 300000L); + } + } else { + buffer = Math.max(buffer- 1.25, 0); + } + + samplesP.clear(); + samplesY.clear(); + + lastAverageYaw = averageYaw; + lastAveragePitch = averagePitch; + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimJ.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimJ.java new file mode 100644 index 0000000..01fee14 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimJ.java @@ -0,0 +1,77 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimJ extends Check { + + private final double multiplier = Math.pow(2, 24); + private float previous; + private double vl, streak; + + public AimJ(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if((playerData.isCinematic() || System.currentTimeMillis() - playerData.getLastCinematic() < 2500L)) return; + + if ((System.currentTimeMillis() - playerData.getLastAttackPacket() >= 2000)) { + this.setVl(0); + vl = 0; + streak = 0; + return; + } + + if (playerData.lastServerPositionTick < 55) { + vl = 0; + return; + } + + //if(PacketEvents.getAPI().getPlayerUtils().getClientVersion(player).isHigherThan(ClientVersion.v_1_8)) return; DOESN'T WORK ATM + + float pitchChange = MathUtil.getDistanceBetweenAngles(to.getPitch(), from.getPitch()); + + long a = (long) (pitchChange * multiplier); + long b = (long) (previous * multiplier); + + long gcd = MathUtil.gcd(a, b); + + float magicVal = pitchChange * 100 / previous; + + if (magicVal > 60) { + vl = Math.max(0, vl - 1); + streak = Math.max(0, streak - 0.125); + } + + if (pitchChange > 0.5 && pitchChange <= 20 && gcd < 131072) { + if (++vl > 1) { + ++streak; + } + if (streak > 10) { + streak -= 2; + handleFlag(player, "AimAssist J", "§b* §fChecks for GCD" + "\n§b* §fmagicv=§b" + magicVal + "\n§b* §fgcd=§b" + gcd, getBanVL("AimJ"), 300000L); + } + if(streak > 3) { + handleVerbose(player, "AimAssist J", "§b* §fChecks for GCD" + "\n§b* §fmagicv=§b" + magicVal + "\n§b* §fgcd=§b" + gcd); + } + } else { + vl = Math.max(0, vl - 1); + } + this.previous = pitchChange; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimK.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimK.java new file mode 100644 index 0000000..f07e336 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimK.java @@ -0,0 +1,43 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimK extends Check { + + private double buffer; + + public AimK(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 3) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + + if(deltaYaw % 0.5 == 0.0 && deltaYaw > 0) { + if(++buffer > 4) { + handleFlag(player, "AimAssist K", "§b* §fTick aim" + "\n§b* §fdeltaYaw=§b" + deltaYaw, getBanVL("AimK"), 500000L); + buffer = 0; + } + } else { + buffer = Math.max(buffer - 1, 0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimL.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimL.java new file mode 100644 index 0000000..d4788ad --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimL.java @@ -0,0 +1,48 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimL extends Check { + + private double buffer; + + public AimL(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 4) { + return; + } + + final float angleYaw = MathUtil.getDistanceBetweenAngles(to.getYaw(), from.getYaw()); + final float anglePitch = MathUtil.getDistanceBetweenAngles(to.getPitch(), from.getPitch()); + + final double shit = anglePitch % angleYaw; + + //Bukkit.broadcastMessage("A: " + angleYaw + " NaN: " + Double.isNaN(shit)); + + if(angleYaw > 0.1 && Double.isNaN(shit)) { + if(++buffer > 10) { + handleFlag(player, "AimAssist L", "§b* §fInvalid rotation" + "\n§b* §fdeltaYaw=§b" + angleYaw + "\n§b* §fP % Y=§b" + shit, getBanVL("AimL"), 500000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimM.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimM.java new file mode 100644 index 0000000..4709c11 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimM.java @@ -0,0 +1,84 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimM extends Check { + + private double buffer, vl; + private double ticks; + private double lastModuloShit, lastRModuloShit; + private double lastDifference, lastRDifference; + + public AimM(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 4) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + final float rDeltaYaw = Math.abs(deltaYaw - Math.round(deltaYaw)); + final float rDeltaPitch = Math.abs(deltaPitch - Math.round(deltaPitch)); + + final double moduloShit = deltaYaw % deltaPitch; + final double difference = Math.abs(moduloShit - lastModuloShit); + final double deltaDifference = Math.abs(difference - lastDifference); + + final double rModuloShit = rDeltaYaw % rDeltaPitch; + final double rDifference = Math.abs(rModuloShit - lastRModuloShit); + final double rDeltaDifference = Math.abs(rDifference - lastRDifference); + + if (rDeltaDifference < 0.05 && rDeltaDifference > 0.016 && (!playerData.isCinematic() || System.currentTimeMillis() - playerData.getLastCinematic() > 2000L)) { + ++buffer; + + if (Double.isNaN(rDeltaDifference)) { + buffer = Math.max(buffer - 0.8, 0); + } + + if (buffer > 30) { + ++ticks; + if (buffer > 35) + buffer -= 10; + } else { + if (ticks > 0) { + ticks -= 1; + } + } + + //Bukkit.broadcastMessage("§frD: §b" + rDeltaDifference + " §ft b: §b" + ticks + " " + buffer + " §fc: §b" + (System.currentTimeMillis() - playerData.getLastCinematic() < 2000L)); + if (ticks > 20) { + handleFlag(player, "AimAssist M", "§b* §f% change is too low" + "\n§b* §fdiff=§b" + rDeltaDifference + "\n§b* §fticks=§b" + ticks, getBanVL("AimM"), 500000L); + if (ticks >= 25) + ticks -= 10; + buffer -= 10; + if (buffer > 35) { + buffer /= 2; + } + } + } + lastModuloShit = moduloShit; + lastRModuloShit = rModuloShit; + lastDifference = difference; + lastRDifference = rDifference; + } else { + buffer = Math.max(buffer - 0.75, 0); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/AimO.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimO.java new file mode 100644 index 0000000..79b7a61 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/AimO.java @@ -0,0 +1,67 @@ +package me.liwk.karhu.check.impl.aimassist; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class AimO extends Check { + + private int buffer; + + public AimO(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 3) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if(deltaYaw == 0) return; + + double rP = reversedPitch(playerData.getSensitivity(), deltaPitch); + double rY = reversedYaw(playerData.getSensitivity(), deltaYaw); + + if(String.valueOf(deltaPitch).contains("E") && deltaYaw > 1) { + if(++buffer > 10) { + handleFlag(player, "AimAssist O", "§b* §fNearly impossible rotation" + "\n§b* §fpitch §b" + deltaPitch, getBanVL("AimO"), 500000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + + if(rY <= 0.000001 || rP <= 0.000001 && rP != 0 && rY != 0) { + //handleFlag(player, "AimAssist O", "§b* §fClient GCD patch", getBanVL("AimO"), 500000L); + } + + + } + } + } + + + private double reversedPitch(double sens, double pitch) { + float f = (float)(sens * 0.6 + 0.2); + float gcd = f * f * f * 1.2F; + return pitch % gcd; + } + + private double reversedYaw(double sens, double yaw) { + float f = (float)(sens * 0.6 + 0.2); + float gcd = f * f * f * 1.2F; + return yaw % gcd; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Mouse.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Mouse.java new file mode 100644 index 0000000..0ee67f0 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Mouse.java @@ -0,0 +1,92 @@ +package me.liwk.karhu.check.impl.aimassist.smooth; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.evictinglist.EvictingList; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class Mouse extends Check { + + public Mouse(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + private static Map sensitivityMap = new HashMap<>(); + + static { + for(double d = 50.9; d < 204.8; d += 0.7725) { + sensitivityMap.put(d, sensitivityMap.size() * 0.005); + } + } + + private EvictingList samples = new EvictingList<>(50); + private double lastDeltaPitch, lastMode, lastConstant, recordedConstant; + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + double deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + long expandedPitch = (long) Math.abs(deltaPitch * Math.pow(2, 24)); + long lastExpandedPitch = (long) Math.abs(lastDeltaPitch * Math.pow(2, 24)); + + double pitchGcd = MathUtil.getGcd(expandedPitch, lastExpandedPitch) / Math.pow(2, 24); + + samples.add(pitchGcd); + if (samples.size() == 50) { + double mode = MathUtil.getMode(samples); + + long expandedMode = (long) (mode * Math.pow(2, 24)); + long lastExpandedMode = (long) (lastMode * Math.pow(2, 24)); + + double modeGcd = MathUtil.getGcd(expandedMode, lastExpandedMode); + double constant = Math.round((Math.cbrt(modeGcd / .15 / 8) - .2 / .6) * 1000.0) / 1000.0; + + + if (Math.abs(constant - lastConstant) < 0.01) { + playerData.setVerifyingSensitivity(false); + recordedConstant = constant; + } else { + playerData.setVerifyingSensitivity(true); + } + + double sensitivity = getSensitivity(recordedConstant); + if (sensitivity > -1) { + playerData.setSensitivity(sensitivity); + } + + /*if(!playerData.isVerifyingSensitivity()) { + Bukkit.broadcastMessage(ChatColor.RED + "" + player.getName() + "'s Sensitivity = " + ChatColor.GREEN + Math.round(sensitivity * 200.0) + "%"); + }*/ + + lastConstant = constant; + lastMode = mode; + samples.clear(); + } + + lastDeltaPitch = deltaPitch; + } + } + } + + public double getSensitivity(double constant) { + for(double val : sensitivityMap.keySet()) { + if(Math.abs(val - constant) <= 0.4) { + return sensitivityMap.get(val); + } + } + return -1; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Optifine.java b/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Optifine.java new file mode 100644 index 0000000..3372fdd --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/aimassist/smooth/Optifine.java @@ -0,0 +1,95 @@ +package me.liwk.karhu.check.impl.aimassist.smooth; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MouseFilter; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class Optifine extends Check { + + private float lastPitch; + private int ticks; + + /** Smooth cam filter X */ + private float smoothCamFilterX; + + /** Smooth cam filter Y */ + private float smoothCamFilterY; + + /** Smooth cam yaw */ + private float smoothCamYaw; + + /** Smooth cam pitch */ + private float smoothCamPitch; + + private MouseFilter mouseFilterXAxis = new MouseFilter(); + private MouseFilter mouseFilterYAxis = new MouseFilter(); + + public Optifine(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + /*@Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + } + } + }*/ + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + float diffPitch = Math.abs(to.getPitch() - from.getPitch()); + + if(isNearlySame(diffPitch, lastPitch)) { + if(++ticks >= 3) { + //Bukkit.broadcastMessage("doge=" + (System.currentTimeMillis() - playerData.getLastCinematic()) / 1000); + playerData.setCinematic(true); + playerData.setLastCinematic(System.currentTimeMillis()); + } + } else { + ticks = Math.max(ticks - 1, 0); + if(ticks <= 1) { + playerData.setCinematic(false); + } + } + lastPitch = diffPitch; + } + } + } + + public boolean isNearlySame(double d1, double d2) { + return Math.abs(d1 - d2) < 0.0275 && Math.abs(d1 - d2) > 0.0015; + } + + public void isOptifine() { + double sensitivity = playerData.getSensitivity(); + float f = (float) (sensitivity * 0.6F + 0.2F); + float f1 = f * f * f * 8.0F; + this.smoothCamFilterX = this.mouseFilterXAxis.smooth(this.smoothCamYaw, 0.05F * f1); + this.smoothCamFilterY = this.mouseFilterYAxis.smooth(this.smoothCamPitch, 0.05F * f1); + this.smoothCamYaw = 0.0F; + this.smoothCamPitch = 0.0F; + } + + private int getDeltaX(double yawDelta) { + double f2 = yawDelta / 0.15; + return (int) Math.floor(f2 / (playerData.getSensitivity() * 0.6F + 0.2F)); + } + + private int getDeltaY(double pitchDelta) { + double f3 = pitchDelta / 0.15; + return (int) Math.floor(f3 / (playerData.getSensitivity() * 0.6F + 0.2F)); + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerA.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerA.java new file mode 100644 index 0000000..8dd2b32 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerA.java @@ -0,0 +1,63 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import org.bukkit.entity.Player; + +import java.util.LinkedList; +import java.util.Queue; + +public class AutoclickerA extends Check { + + public boolean AAswing; + public int AAmoves; + private double buffer; + public final Queue AAMoveIntervals = new LinkedList<>(); + + public AutoclickerA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (AAswing && !playerData.isPlacing() && !playerData.isDigging()) { + if (AAmoves < 8) { + AAMoveIntervals.add(AAmoves); + if (AAMoveIntervals.size() == 100) { + + double average = AAMoveIntervals.stream().mapToDouble(d -> d).average().orElse(0.0); + double stdDeviation = 0.0; + + for (Integer i : AAMoveIntervals) + stdDeviation += Math.pow(i.doubleValue() - average, 2.0); + + stdDeviation /= AAMoveIntervals.size(); + + if (stdDeviation <= 0.28) { + if (++buffer > 5) { + handleFlag(player, "Autoclicker A", "§b* §fstd=§b" + stdDeviation, getBanVL("AutoclickerA"), 300000L); + } + } else { + buffer = Math.max(buffer - 1.5, 0.0); + } + + AAMoveIntervals.clear(); + } + + AAmoves = 0; + } + } + + AAswing = false; + ++AAmoves; + } else if(event instanceof SwingEvent) { + AAswing = true; + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerB.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerB.java new file mode 100644 index 0000000..0d3a5d9 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerB.java @@ -0,0 +1,76 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.DigEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import org.bukkit.entity.Player; + +import java.util.LinkedList; + + +public class AutoclickerB extends Check { + + public final LinkedList recentCounts = new LinkedList<>(); + public int flyingCount; + public boolean release; + private double buffer; + + public AutoclickerB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + ++flyingCount; + } + + if (event instanceof DigEvent) { + if (((DigEvent) event).getDigType() == WrappedPacketInBlockDig.PlayerDigType.RELEASE_USE_ITEM) { + release = true; + } + } + + if (event instanceof SwingEvent) { + if (playerData.isLagging(System.currentTimeMillis(), 150L)) return; + if (!playerData.isPlacing() && !playerData.isDigging()) { + if (flyingCount < 8) { + if (release) { + release = false; + flyingCount = 0; + return; + } + recentCounts.add(flyingCount); + if (recentCounts.size() == 100) { + double average = 0.0; + for (final int i : recentCounts) { + average += i; + } + average /= recentCounts.size(); + double stdDev = 0.0; + for (final int j : recentCounts) { + stdDev += Math.pow(j - average, 2.0); + } + + stdDev /= recentCounts.size(); + stdDev = Math.sqrt(stdDev); + if (stdDev < 0.4) { + if ((buffer += 1.4) >= 5.0) { + handleFlag(player, "Autoclicker B", "§b* §fBasic std\n§b* §fstd§b=" + stdDev + "\n§b* §fvl§b=" + buffer, getBanVL("AutoclickerB"), 300000L); + } + } else { + buffer = Math.max(buffer - 0.8, 0.0); + } + recentCounts.clear(); + } + } + flyingCount = 0; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerC.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerC.java new file mode 100644 index 0000000..320ac22 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerC.java @@ -0,0 +1,65 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.DigEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import org.bukkit.entity.Player; + +public class AutoclickerC extends Check { + + private int clicks, outliers, flyings; + private boolean release; + private double buffer; + + + public AutoclickerC(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + ++flyings; + } + + if (event instanceof DigEvent) { + if (((DigEvent) event).getDigType() == WrappedPacketInBlockDig.PlayerDigType.RELEASE_USE_ITEM) { + release = true; + } + } + + if (event instanceof SwingEvent) { + if (flyings < 10 && flyings != 0) { + if (release) { + release = false; + flyings = 0; + return; + } + + if (flyings > 3 && !playerData.isDigging() && !playerData.isPlacing()) { + ++outliers; + } + + if (++clicks == 80) { + if (outliers == 0) { + if ((buffer += 1.4) >= 6.8) { + handleFlag(player, "Autoclicker C", "§b* §fChecks for impossible consistency\n§b* §foutliers§b=" + outliers + "\n§b* §fvl§b=" + buffer, getBanVL("AutoclickerC"), 300000L); + } + } else { + buffer = Math.max(buffer - 0.8, 0); + } + outliers = 0; + clicks = 0; + } + } + } + flyings = 0; + } +} + + diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerD.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerD.java new file mode 100644 index 0000000..05ce15f --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerD.java @@ -0,0 +1,51 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.entity.Player; + +import java.util.LinkedList; + +public class AutoclickerD extends Check { + + public final LinkedList clicksList = new LinkedList<>(); + public int flyingCount; + private double lastStd; + private double PreVL; + + public AutoclickerD(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof SwingEvent) { + if (flyingCount < 10 && !playerData.isPlacing() && !playerData.isDigging()) { + this.clicksList.add(flyingCount); + + double standardDeviation = MathUtil.getStandardDeviation(clicksList); + + if (clicksList.size() == 50) { + if (Math.abs(standardDeviation - lastStd) < 0.04) { + if (++PreVL > 2) { + handleFlag(player, "Autoclicker D", "§b* §fPoor randomization\n§b* §fdiff§b=" + Math.abs(standardDeviation - lastStd), getBanVL("AutoclickerD"), 300000L); + } + } else { + PreVL = Math.max(PreVL - 1.25, 0.0); + } + clicksList.clear(); + lastStd = standardDeviation; + } + } + flyingCount = 0; + + } else if (event instanceof FlyingEvent) { + ++flyingCount; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerE.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerE.java new file mode 100644 index 0000000..b42a53d --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerE.java @@ -0,0 +1,56 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.entity.Player; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class AutoclickerE extends Check { + + private final Deque delays = new ArrayDeque<>(); + private int delay; + private long lastTimestamp; + + public AutoclickerE(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof SwingEvent) { + boolean valid = !playerData.isPlacing() && !playerData.isDigging() && (playerData.getTotalTicks() - playerData.getLastDig()) > 10; + + long delay2 = playerData.getClientVersion().isLowerThan(ClientVersion.v_1_9) ? delay * 50L : ((SwingEvent) event).getTimeStamp() - lastTimestamp; + + if (delay2 <= 500 && valid) { + delays.add(delay2); + } + + if (delays.size() > 5 && valid) { + double CPS = MathUtil.getCPS1000(delays); + + + if (CPS > 20) { + handleFlag(player, "Autoclicker E", "§b* §fCPS=§b" + CPS, getBanVL("AutoclickerE"), 300000L); + } + + if(delays.size() >= 10) { + delays.clear(); + } + + } + lastTimestamp = ((SwingEvent) event).getTimeStamp(); + delay = 0; + } else if (event instanceof FlyingEvent) { + ++delay; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerF.java b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerF.java new file mode 100644 index 0000000..8f8a863 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/autoclicker/AutoclickerF.java @@ -0,0 +1,51 @@ +package me.liwk.karhu.check.impl.autoclicker; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.entity.Player; + +import java.util.LinkedList; + +public class AutoclickerF extends Check { + + public final LinkedList clicksList = new LinkedList<>(); + public int flyingCount; + private double lastStd; + private double PreVL; + + public AutoclickerF(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof SwingEvent) { + if (flyingCount < 10 && !playerData.isPlacing() && !playerData.isDigging()) { + this.clicksList.add(flyingCount); + + double standardDeviation = MathUtil.getStandardDeviation(clicksList); + + if (clicksList.size() == 100) { + if (Math.abs(standardDeviation - lastStd) < 0.1) { + if (++PreVL > 3) { + handleFlag(player, "Autoclicker F", "§b* §fPoor randomization\n§b* §fdiff§b=" + Math.abs(standardDeviation - lastStd), getBanVL("AutoclickerF"), 300000L); + } + } else { + PreVL = Math.max(PreVL - 1.25, 0.0); + } + clicksList.clear(); + lastStd = standardDeviation; + } + } + flyingCount = 0; + + } else if (event instanceof FlyingEvent) { + ++flyingCount; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/badpackets/BadA.java b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadA.java new file mode 100644 index 0000000..7974b9d --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadA.java @@ -0,0 +1,27 @@ +package me.liwk.karhu.check.impl.badpackets; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class BadA extends Check { + + public BadA(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked() || ((FlyingEvent) event).hasMoved()) { + if(Math.abs(((FlyingEvent) event).getPitch()) > 90 && playerData.getTeleportLocation() == null) { + handleFlag(player, "BadPackets A", "§b* §fInvalid pitch", getBanVL("BadPacketsA"), 200000L); + } + } + } + } + +} diff --git a/src/main/java/me/liwk/karhu/check/impl/badpackets/BadB.java b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadB.java new file mode 100644 index 0000000..35ffa7f --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadB.java @@ -0,0 +1,24 @@ +package me.liwk.karhu.check.impl.badpackets; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.HeldItemSlotEvent; +import org.bukkit.entity.Player; + +public class BadB extends Check { + + public BadB(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof HeldItemSlotEvent) { + if(playerData.isPlacing()) { + handleFlag(player, "BadPackets B", "§b* §fPlacing while changing slot", getBanVL("BadPacketsB"), 200000L); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/badpackets/BadC.java b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadC.java new file mode 100644 index 0000000..7545e90 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadC.java @@ -0,0 +1,25 @@ +package me.liwk.karhu.check.impl.badpackets; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AbilityEvent; +import org.bukkit.entity.Player; + +public class BadC extends Check { + + public BadC(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof AbilityEvent) { + if(!player.getAllowFlight()) { + handleFlag(player, "BadPackets C", "§b* §fSent ability packet without flying", getBanVL("BadPacketsC"), 6000L); + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/badpackets/BadD.java b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadD.java new file mode 100644 index 0000000..383e8e1 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadD.java @@ -0,0 +1,35 @@ +package me.liwk.karhu.check.impl.badpackets; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class BadD extends Check { + + private int buffer; + + public BadD(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(playerData.getTotalTicks() < 300) { + return; + } + + if(playerData.getPing() == 0 && playerData.getTransPing() > 1) { + if(buffer > 5) { + handleFlag(player, "BadPackets D", "§b* §fNull ping\n§b* §fKPing=§b" + playerData.getPing() + "\n§b* §fTPing=§b" + playerData.getTransPing(), getBanVL("BadPacketsD"), 6000L); + } + } else { + buffer = 0; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/badpackets/BadE.java b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadE.java new file mode 100644 index 0000000..4873e3a --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/badpackets/BadE.java @@ -0,0 +1,30 @@ +package me.liwk.karhu.check.impl.badpackets; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.HeldItemSlotEvent; +import org.bukkit.entity.Player; + +public class BadE extends Check { + + private int lastSlot = 69; + + public BadE(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof HeldItemSlotEvent) { + if (lastSlot == 69) { + return; + } + if (((HeldItemSlotEvent) event).getSlot() == lastSlot) { + handleFlag(player, "BadPackets E", "§b* §fsent 2 same held slot packets", getBanVL("BadPacketsE"), 6000L); + } + lastSlot = ((HeldItemSlotEvent) event).getSlot(); + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/boatfly/BoatFlyA.java b/src/main/java/me/liwk/karhu/check/impl/boatfly/BoatFlyA.java new file mode 100644 index 0000000..deee8c8 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/boatfly/BoatFlyA.java @@ -0,0 +1,36 @@ +package me.liwk.karhu.check.impl.boatfly; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Horse; +import org.bukkit.entity.Player; + +public class BoatFlyA extends Check { + + private double buffer; + + public BoatFlyA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + if (player.getVehicle() != null && !(player.getVehicle() instanceof Horse)) { + if((playerData.getDeltaY() - playerData.getLastDeltaY()) > 0.11 && playerData.getSlimeTicks() < 1 && playerData.getTotalTicks() - playerData.getLastVehicleTicks() > 5) { + if(++buffer > 4) { + handleFlag(player, "BoatFly A", "§b* §fdeltaY=§b" + (playerData.getDeltaY() - playerData.getLastDeltaY()), getBanVL("BoatFlyA"), 60000L); + } + } else { + buffer = Math.max(buffer - 0.5, 0); + } + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/fastladder/FastLadderA.java b/src/main/java/me/liwk/karhu/check/impl/fastladder/FastLadderA.java new file mode 100644 index 0000000..100af60 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/fastladder/FastLadderA.java @@ -0,0 +1,33 @@ +package me.liwk.karhu.check.impl.fastladder; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; + +public class FastLadderA extends Check { + + public FastLadderA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + if(playerData.getClimbableTicks() <= 15 + || playerData.getTotalTicks() - playerData.getLastFlyTick() < 40 + || UtilPlayer.isNearGround(player.getLocation()) + || playerData.getVelocityV() > 0 + || playerData.getVelocityH() > 0) return; + if(playerData.deltaY >= 0.15) { + handleFlag(player, "FastLadder A", "§b* §fdeltaY=§b" + playerData.deltaY + "\n§b* §fmaxSpeed=§b0.15", getBanVL("FastLadderA"), 60000L); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/fly/FlyA.java b/src/main/java/me/liwk/karhu/check/impl/fly/FlyA.java new file mode 100644 index 0000000..02011f3 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/fly/FlyA.java @@ -0,0 +1,110 @@ +package me.liwk.karhu.check.impl.fly; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.BlockPlaceEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class FlyA extends Check { + + private boolean lastOnGround, lastLastOnGround; + private int lastPlace; + private int lastSlimePlace; + private double buffer = 0.0d; + private int ticks; + + public FlyA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved() && (playerData.getDeltaY() != 0.0D || playerData.getDeltaXZ() != 0.0D)) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + double deltaY = playerData.deltaY; + double accel = Math.abs(deltaY - playerData.lastDeltaY); + + boolean onGround = UtilPlayer.isOnGroundBB(player); + + boolean lastOnGround = this.lastOnGround; + this.lastOnGround = onGround; + + boolean lastLastOnGround = this.lastLastOnGround; + this.lastLastOnGround = lastOnGround; + + if(playerData.getTotalTicks() - playerData.getLastFlyTick() < 40 + || playerData.isInLiquid() + || playerData.getLiquidTicks() > 0 + || playerData.getTotalTicks() - lastPlace <= 5 + || playerData.getTotalTicks() - lastSlimePlace <= 100 + || onGround + || lastOnGround + || lastLastOnGround + || UtilPlayer.isNearHoney(player) + || playerData.getTotalTicks() - playerData.getLastPacketDrop() < 5 + || (playerData.lastServerPositionTick < 55 + Math.min(MathUtil.getPingInTicks(playerData.getTransPing()), 5)) + || playerData.isWasInLiquid() + || event.getTimestamp() - playerData.getLastVehicle() < 2000L + || playerData.getTotalTicks() < 80 + || playerData.getTotalTicks() - playerData.getLastPacketDrop() < 10 + || System.currentTimeMillis() - playerData.getLastGlide() < 4250L + || UtilPlayer.isNearScaffolding(player) + || UtilPlayer.isClimbableBlock(player) + || playerData.isLagging(System.currentTimeMillis(), 100L) + || playerData.getVelocityV() > 0 + || playerData.getVelocityH() > 0 + || UtilPlayer.isOnFlyABad(player)) { + return; + } + + if (from.getX() == to.getX() && from.getY() == to.getY() && from.getZ() == to.getZ()) { + return; + } + + if(PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_7_10)) { + if (!player.getWorld().isChunkLoaded(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4)) { + return; + } + } + + float limit = playerData.airTicks <= 20 ? 0.0f + MovementUtils.getPotionEffectLevel(player, PotionEffectType.JUMP) * 0.2f : 0.0f; + + limit += System.currentTimeMillis() - playerData.getLastGlide() < 4400L ? 4.2 : 0; + limit += System.currentTimeMillis() - playerData.getLastRiptide() < 4400L ? 4.2 : 0; + limit += playerData.getSlimeHeight(); + + if ((playerData.getTeleportLocation() == null && System.currentTimeMillis() - playerData.getLastTeleport() > 2250L)) { + if(from.getX() != to.getX() || from.getZ() != to.getZ() || from.getY() != to.getY()) { + + if (playerData.getAirTicks() > 14) { + if ((deltaY >= limit || accel == 0.0) && !UtilPlayer.isNearWeb(player)) { + handleFlag(player, "Fly A", "§b* §fdeltaY=§b" + deltaY + "\n§b* §faccel=§b" + accel + "\n§b* §fairTicks=§b" + playerData.getAirTicks() + "\n§b* §fdeltaH=§b" + playerData.getDeltaXZ(), getBanVL("FlyA"), 60000L); + } + } + } + } + } + } else if(event instanceof BlockPlaceEvent) { + if (((BlockPlaceEvent) event).getItemStack().getType().isSolid()) { + this.lastPlace = playerData.getTotalTicks(); + } + if(((BlockPlaceEvent) event).getItemStack().toString().contains("SLIME")) { + this.lastSlimePlace = playerData.getTotalTicks(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/fly/FlyB.java b/src/main/java/me/liwk/karhu/check/impl/fly/FlyB.java new file mode 100644 index 0000000..11bae07 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/fly/FlyB.java @@ -0,0 +1,53 @@ +package me.liwk.karhu.check.impl.fly; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.playerhandler.Handler1_8; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class FlyB extends Check { + + private double buffer; + private double lastDeltaY; + + public FlyB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + double deltaY = to.getY() - from.getY(); + + double estimation = (lastDeltaY - 0.08) * 0.9800000190734863D; + + double diff = Math.abs(deltaY - estimation); + + if(Math.abs(deltaY + 0.098) <= 1E-4) { + return; + } + + boolean valid = playerData.getVelocityV() <= 0 + && playerData.getVelocityH() <= 0 && !player.getAllowFlight() && !Handler1_8.isSpectating(player); + + if (playerData.getAirTicks() > 6 && !playerData.isTeleporting() && diff >= 0.005D && Math.abs(estimation) >= 0.005D && !player.isInsideVehicle()) { + if(++buffer > 3) { + handleFlag(player, "Fly B", "§b* §fInvalid y-axis movement" + "\n§b* §fpred=§b" + estimation + "\n§b* §fticks=§b" + playerData.getAirTicks(), getBanVL("FlyB"), 60000L); + } + } else { + buffer = Math.max(buffer - 0.5, 0); + } + + + this.lastDeltaY = deltaY; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/hitbox/HitboxA.java b/src/main/java/me/liwk/karhu/check/impl/hitbox/HitboxA.java new file mode 100644 index 0000000..901206c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/hitbox/HitboxA.java @@ -0,0 +1,99 @@ +package me.liwk.karhu.check.impl.hitbox; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.entity.EntityLocationHandler; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.AxisAlignedBB; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.Vec3; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +public class HitboxA extends Check { + + private double buffer; + private Vec3 eyeLocation; + private Vec3 look, lookMouseDelayFix; + + public HitboxA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (!player.getGameMode().equals(GameMode.CREATIVE) + && playerData.getLastTarget() != null + && !playerData.isLagging(System.currentTimeMillis(), 200L) + && playerData.getLastAttackTick() <= 1 + && playerData.getPastLocsHitBox().size() >= 10 + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 5 + && System.currentTimeMillis() - playerData.getLastDelayedPacket() > 160L + && playerData.lastServerPositionTick > 100 + Math.min(MathUtil.getPingInTicks(playerData.getTransPing()), 5)) { + + float sneakAmount1_8 = playerData.isSneaking() ? 1.54F : 1.62F; + float sneakAmount1_13 = playerData.isSneaking() ? 1.27F : 1.62F; + + if (playerData.getLocation() != null) { + eyeLocation = MathUtil.getPositionEyes(playerData.attackerX2, playerData.attackerY2, playerData.attackerZ2, playerData.getClientVersion().isLowerThan(ClientVersion.v_1_13) ? sneakAmount1_8 : sneakAmount1_13); + } + + if (((FlyingEvent) event).hasLooked()) { + lookMouseDelayFix = MathUtil.getVectorForRotation(playerData.attackerPitch2, ((FlyingEvent) event).getYaw()); + look = MathUtil.getVectorForRotation(playerData.attackerPitch2, playerData.attackerYaw2); + } else { + lookMouseDelayFix = MathUtil.getVectorForRotation(playerData.attackerPitch2, playerData.attackerYaw2); + look = lookMouseDelayFix; + } + + Vec3 vec3 = eyeLocation; + Vec3 vec31 = look; + Vec3 vec311 = lookMouseDelayFix; + + Vec3 vec32 = vec3.addVector(vec31.xCoord * 6.0D, vec31.yCoord * 6.0D, vec31.zCoord * 6.0D); + Vec3 vec322 = vec3.addVector(vec311.xCoord * 6.0D, vec311.yCoord * 6.0D, vec311.zCoord * 6.0D); + + AxisAlignedBB axisalignedbb = new AxisAlignedBB(0, 0, 0, 0, 0, 0); + boolean a = false; + int missed = 0; + + final int nowTicks = playerData.getTotalTicks(); + final int pingTicks = MathUtil.getPingInTicks(playerData.getTransPing()) + 3; + + /*for(Pair pair : playerData.getPastLocsHitBox()) { + if(Math.abs(nowTicks - pair.getY() - pingTicks) < 2) { + if (!a) { + axisalignedbb = pair.getX(); + a = true; + } else { + axisalignedbb.minX = Math.min(axisalignedbb.minX, pair.getX().minX); + axisalignedbb.maxX = Math.max(axisalignedbb.maxX, pair.getX().maxX); + axisalignedbb.minY = Math.min(axisalignedbb.minY, pair.getX().minY); + axisalignedbb.maxY = Math.max(axisalignedbb.maxY, pair.getX().maxY); + axisalignedbb.minZ = Math.min(axisalignedbb.minZ, pair.getX().minZ); + axisalignedbb.maxZ = Math.max(axisalignedbb.maxZ, pair.getX().maxZ); + } + MovingObjectPosition movingobjectposition = axisalignedbb.calculateIntercept(vec3, vec32); + MovingObjectPosition movingobjectposition2 = axisalignedbb.calculateIntercept(vec3, vec322); + + if (movingobjectposition == null && movingobjectposition2 == null && !axisalignedbb.isVecInside(vec3)) { + ++missed; + } + } + }*/ + + + /*if (missed >= 3 && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 10) { + if((buffer += 0.75) > 8.5) { + //handleFlag(player, "Hitbox A", "§b* §fHit outside of players box\n§b* §fmisses=§b" + missed, getBanVL("HitboxA"), 60000L); + } + } else buffer = Math.max(buffer - 4.5, 0.0);*/ + } + EntityLocationHandler.updateFlyingLocations2(playerData, (FlyingEvent) event); + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryA.java b/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryA.java new file mode 100644 index 0000000..9e5de3d --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryA.java @@ -0,0 +1,36 @@ +package me.liwk.karhu.check.impl.inventory; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.WindowEvent; +import org.bukkit.entity.Player; + +public class InventoryA extends Check { + + private int buffer; + + public InventoryA(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof WindowEvent) { + if ( + playerData.getDeltaXZ() > 0.12 + && playerData.liquidTicks == 0 + && playerData.getVelocityH() <= 0 + && playerData.getIceTicks() <= 0 + && playerData.getGroundTicks() > 2 + ) { + if (++buffer > 6) { + handleFlag(player, "Inventory A", "§b* §fMoving while clicking" + "\n§b* §fdeltaXZ=§b" + playerData.getDeltaXZ(), getBanVL("InventoryA"), 60000L); + } + } else { + buffer = Math.max(buffer - 2, 0); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryB.java b/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryB.java new file mode 100644 index 0000000..677e256 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/inventory/InventoryB.java @@ -0,0 +1,40 @@ +package me.liwk.karhu.check.impl.inventory; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.WindowEvent; +import org.bukkit.entity.Player; + +public class InventoryB extends Check { + + private double buffer; + + public InventoryB(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof WindowEvent) { + if ( + !playerData.isInventoryOpen() + && playerData.deltaXZ > 0.1 + && !playerData.droppedPackets + && !playerData.isLagging(System.currentTimeMillis(), 100L) + && playerData.liquidTicks == 0 + && (playerData.getTotalTicks() - playerData.getInvStamp()) > 250 + && playerData.getVelocityH() <= 0 + && playerData.getIceTicks() <= 0 + && !playerData.hasFast() + ) { + if (++buffer > 4) { + handleFlag(player, "Inventory B", "§b* §fClicking inventory without it being open\n§b* §fticks=§b" + (playerData.getTotalTicks() - playerData.getInvStamp()), getBanVL("InventoryB"), 50000L); + } + } else { + buffer = Math.max(buffer - 2, 0); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/jesus/JesusA.java b/src/main/java/me/liwk/karhu/check/impl/jesus/JesusA.java new file mode 100644 index 0000000..e797ed6 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/jesus/JesusA.java @@ -0,0 +1,69 @@ +package me.liwk.karhu.check.impl.jesus; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import me.liwk.karhu.util.task.Tasker; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class JesusA extends Check { + + private double buffer, buffer2; + + public JesusA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + + if(player.isFlying() || UtilPlayer.isOnSlab(player) || UtilPlayer.isOnBadJesusBlock(player) || UtilPlayer.isOnLily(player)) return; + + //Bukkit.broadcastMessage("§7[§c§lPRE-DEBUG§7] §fl: §c" + UtilPlayer.isOnLily(player) + " §fb: §c" + UtilPlayer.isOnBadJesusBlock(player)); + + if(player.isOnGround() && UtilPlayer.isAboveLiquid(player)) { + if (++buffer2 > 3) { + handleFlag(player, "Jesus A", "§b* §fdeltaY=§b" + playerData.deltaY + "\n§b* §fdeltaXZ=§b" + playerData.getDeltaXZ(), getBanVL("JesusA"), 60000L); + } + } else { + buffer2 = Math.max(buffer2 - 1, 0); + if(UtilPlayer.isAboveLiquid(player) && Math.abs(playerData.deltaY) < 1.0E-4 && playerData.getDeltaXZ() > 0.07D && System.currentTimeMillis() - playerData.getLastVehicle() > 2250L && player.getVehicle() == null) { + if(PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + Tasker.run(() -> { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + }); + } else { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + } + if (++buffer > 4) { + handleFlag(player, "Jesus A", "§b* §fdeltaY=§b" + playerData.deltaY + "\n§b* §fdeltaXZ=§b" + playerData.getDeltaXZ(), getBanVL("JesusA"), 60000L); + } + handleVerbose(player, "Jesus A", "§b* §fdeltaY=§b" + playerData.deltaY + "\n§b* §fdeltaXZ=§b" + playerData.getDeltaXZ()); + + } else { + buffer = Math.max(buffer - 1, 0); + } + } + + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraA.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraA.java new file mode 100644 index 0000000..4267fef --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraA.java @@ -0,0 +1,44 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class KillauraA extends Check { + + public Long lastUseEntity; + private int buffer; + + public KillauraA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (lastUseEntity != null) { + double delay = System.currentTimeMillis() - lastUseEntity; + if (delay < 100 && delay > 40 && !playerData.hasFast() && !playerData.droppedPackets && !playerData.isLagging(System.currentTimeMillis(), 250L)) { + if (++buffer > 3) { + handleFlag(player, "Killaura A", "§b* §fPost killaura\n§b* §fdelay=§b" + delay, getBanVL("KillauraA"), 60000L); + lastUseEntity = null; + } + //handleVerbose(player, "Killaura A", "§b* §fPost killaura\n§b* §fdelay=§b" + delay); + } else { + buffer = Math.max(buffer - 1, 0); + } + } + } else if (event instanceof AttackEvent) { + + if (System.currentTimeMillis() - playerData.getLastFlying() < 10L) { + lastUseEntity = playerData.getLastFlying(); + } else { + buffer = Math.max(buffer - 1, 0); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraB.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraB.java new file mode 100644 index 0000000..55001a0 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraB.java @@ -0,0 +1,37 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.BlockPlaceEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.InteractEvent; +import org.bukkit.entity.Player; + +public class KillauraB extends Check { + + private boolean sentInteract; + private boolean sentAttack; + + public KillauraB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + sentInteract = false; + sentAttack = false; + } else if(event instanceof AttackEvent) { + sentAttack = true; + } else if(event instanceof InteractEvent) { + sentInteract = true; + } else if(event instanceof BlockPlaceEvent) { + if (sentAttack &! sentInteract) { + handleFlag(player, "Killaura B", "§b* §fAutoblock", getBanVL("KillauraB"), 60000L); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraC.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraC.java new file mode 100644 index 0000000..93af665 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraC.java @@ -0,0 +1,71 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class KillauraC extends Check { + + private double buffer; + private int ticksSinceHit; + private double lastDist; + + public KillauraC(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + + if(player == null) return; + + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + double distX = to.getX() - from.getX(); + double distZ = to.getZ() - from.getZ(); + double distXZ = Math.sqrt((distX * distX) + (distZ * distZ)); + + double lastDist = this.lastDist; + this.lastDist = distXZ; + + if(playerData.getLastTarget() == null) return; + + if (++ticksSinceHit <= 2 && !PacketEvents.getAPI().getPlayerUtils().getClientVersion(player).isHigherThan(ClientVersion.v_1_15_2)) { + + if(playerData.getVelocityH() > 0 || playerData.getVelocityV() > 0) return; + + if (playerData.isSprinting() && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 10 && !playerData.isLagging(System.currentTimeMillis(), 100L) && !UtilPlayer.isOnSoulSand(player) && player.getVehicle() == null) { + double acceleration = Math.abs(distXZ - lastDist); + float speedLevel = MovementUtils.getPotionEffectLevel(player, PotionEffectType.SPEED); + acceleration += playerData.getGroundTicks() < 5 ? speedLevel * 0.07f : speedLevel * 0.0573f; + if (acceleration <= 0.05 && distXZ > 0.23 && !UtilPlayer.blockNearHead(player)) { + if (++buffer > 8) { + handleFlag(player, "Killaura C", "§b* §fKeepsprint\n§b* §faccel=§b" + acceleration, getBanVL("KillauraC"), 50000L); + } + } else { + buffer = 0; + } + } else { + buffer = 0; + } + } + } + } else if(event instanceof AttackEvent) { + ticksSinceHit = 0; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraD.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraD.java new file mode 100644 index 0000000..5e3db7c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraD.java @@ -0,0 +1,42 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.DigEvent; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +public class KillauraD extends Check { + + private boolean sentDig; + private int buffer; + + public KillauraD(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof AttackEvent) { + if (sentDig) { + if (player.getItemInHand().getType() != Material.BOW || player.getItemInHand().getType() != Material.FISHING_ROD) { + if (++buffer > 6) { + handleFlag(player, "Killaura D", "§b* §fDigging while attacking", getBanVL("KillauraD"), 40000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + } else if (event instanceof FlyingEvent) { + sentDig = false; + } else if (event instanceof DigEvent) { + sentDig = true; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraE.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraE.java new file mode 100644 index 0000000..6e4ebc3 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraE.java @@ -0,0 +1,50 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +public class KillauraE extends Check { + + private int buffer; + + public KillauraE(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (playerData.getLastTarget() != null && player.getVehicle() == null && playerData.getLastAttackTick() <= 1 && playerData.lastServerPositionTick > 60) { + + Player target = playerData.getLastTarget(); + + final double range = player.getEyeLocation().clone().toVector().setY(0.0D).distance(target.getEyeLocation().clone().toVector().setY(0.0D)); + final Vector vec = target.getLocation().clone().toVector().setY(0.0).subtract(player.getEyeLocation().clone().toVector().setY(0.0)); + final float angle = player.getEyeLocation().getDirection().angle(vec); + + final double direction = MathUtil.getDirection(player.getEyeLocation(), target.getEyeLocation()); + final double dist = MathUtil.getDistanceBetweenAngles360(player.getEyeLocation().getYaw(), direction); + + double maxAngle = 35.0D; + + maxAngle += MathUtil.pingFormula(playerData.getTransPing()) + 5; + + if ((dist > maxAngle || angle > 2) && range > 1.5 && System.currentTimeMillis() - playerData.lastFlag > 50L) { + playerData.lastFlag = System.currentTimeMillis(); + if (++buffer > 5) { + buffer = 0; + handleFlag(player, "Killaura E", "* Hitbox aura\n* dist=" + dist + "\n* expected=" + maxAngle, getBanVL("KillauraE"), 60000L); + } + } else if(dist < maxAngle && angle < 2) { + buffer = Math.max(buffer - 1, 0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraF.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraF.java new file mode 100644 index 0000000..8c265dd --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraF.java @@ -0,0 +1,38 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.SwingEvent; +import org.bukkit.entity.Player; + +public class KillauraF extends Check { + + private int sentAnimation; + private int sentAttack; + + public KillauraF(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (sentAttack == 10 && sentAnimation < 5) { + handleFlag(player, "Killaura F", "§b* §fNoSwing", getBanVL("KillauraF"), 60000L); + } + sentAnimation = 0; + sentAttack = 0; + } else if(event instanceof AttackEvent) { + ++sentAttack; + } else if(event instanceof SwingEvent) { + ++sentAnimation; + } + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraG.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraG.java new file mode 100644 index 0000000..ea9ee1d --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraG.java @@ -0,0 +1,41 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.AttackEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.PositionEvent; +import org.bukkit.entity.Player; + +public class KillauraG extends Check { + + private long lastFlyingTime = -1; + private double buffer; + + public KillauraG(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + this.lastFlyingTime = System.currentTimeMillis(); + } else if (event instanceof AttackEvent) { + if ((System.currentTimeMillis() - playerData.getLastAttackPacket()) < 1000L && playerData.lastServerPositionTick > 60 && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 20 && !playerData.isLagging(System.currentTimeMillis(), 200L)) { + long timeDelta = System.currentTimeMillis() - this.lastFlyingTime; + // calculate delta so that we can check if they're sending attack packets too frequently + if (timeDelta < 5) { + if(++buffer > 10) { + handleFlag(player, "Killaura G", "§b* §fToo small timedelta\n§b* §ftimeDelta=§b" + timeDelta, getBanVL("KillauraG"), 60000L); + } + } else { + buffer = 0; + } + } + } else if (event instanceof PositionEvent) { + buffer = 0; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraH.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraH.java new file mode 100644 index 0000000..308c902 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraH.java @@ -0,0 +1,78 @@ +package me.liwk.karhu.check.impl.killaura; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class KillauraH extends Check { + + private final Deque samples = Lists.newLinkedList(); + private double buffer, buffer2; + + public KillauraH(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaYaw > 0.0 && deltaPitch > 0.0) { + samples.add(deltaYaw % deltaPitch); + } + + if (samples.size() == 40) { + final int distinct = (int) (samples.stream().distinct().count()); + final int duplicates = samples.size() - distinct; + + final double average = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + + final double std = MathUtil.getStandardDeviation(samples); + + //Bukkit.broadcastMessage("§7[§c§lPRE-DEBUG§7] §fD: §c" + duplicates + " §fA: §c" + average + " §fN: §c" + Double.isNaN(std) + " §fS: §c" + std); + + if (duplicates >= 6 && average > 0.125f && std > 0.165 && average < 0.55 && std < 0.325) { + //Bukkit.broadcastMessage("§7[§c§lPOST-DEBUG§7] §fD: §c" + duplicates + " §fA: §c" + average + " §fN: §c" + Double.isNaN(std) + " §fS: §c" + std); + if (++buffer > 2.25) { + handleFlag(player, "Killaura H", "§b* §fInvalid rotation\n§b* §fA=§b" + average + "\n§b* §fD=§b" + duplicates + "\n§b* §fS=§b" + std, getBanVL("KillauraH"), 600000L); + } + } else { + buffer = Math.max(buffer - 0.25, 0); + } + + if (duplicates >= 4 && average > 0.3d && std > 0.3 && average < 0.6 && std < 0.45 || isNearlySame(std, average) && duplicates > 2) { + //Bukkit.broadcastMessage("§7[§c§lPOST-DEBUG§7] §fD: §c" + duplicates + " §fA: §c" + average + " §fN: §c" + Double.isNaN(std) + " §fS: §c" + std); + if (++buffer2 > 2.5) { + handleFlag(player, "Killaura H", "§b* §fInvalid rotation\n§b* §fA=§b" + average + "\n§b* §fD=§b" + duplicates + "\n§b* §fS=§b" + std, getBanVL("KillauraH"), 600000L); + } + } else { + buffer2 = Math.max(buffer2 - 0.25, 0); + } + + samples.clear(); + } + } + } + } + public boolean isNearlySame(double d1, double d2) { + return Math.abs(d1 - d2) < 0.011; + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraI.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraI.java new file mode 100644 index 0000000..f4353cc --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraI.java @@ -0,0 +1,65 @@ +package me.liwk.karhu.check.impl.killaura; + +import com.google.common.collect.Lists; +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.Deque; + +public class KillauraI extends Check { + + private final Deque samples = Lists.newLinkedList(); + private double buffer; + + public KillauraI(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 2) { + return; + } + + final float deltaYaw = Math.abs(to.getYaw() - from.getYaw()); + final float deltaPitch = Math.abs(to.getPitch() - from.getPitch()); + + if (deltaYaw > 0.0 && deltaPitch > 0.0 && !Double.isNaN(deltaYaw % deltaPitch)) { + samples.add(deltaYaw % deltaPitch); + } + + if (samples.size() == 20) { + final int distinct = (int) (samples.stream().distinct().count()); + final int duplicates = samples.size() - distinct; + + final double average = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + + final double std = MathUtil.getStandardDeviation(samples); + + //Bukkit.broadcastMessage("§7[§c§lPRE-DEBUG§7] §fD: §c" + duplicates + " §fA: §c" + average + " §fN: §c" + Double.isNaN(std) + " §fS: §c" + std); + + if (duplicates < 2 && average > 0.45d && average < 0.61d && std > 3d && std < 4d) { + //Bukkit.broadcastMessage("§7[§c§lPOST-DEBUG§7] §fD: §c" + duplicates + " §fA: §c" + average + " §fN: §c" + Double.isNaN(std) + " §fS: §c" + std); + if (++buffer > 3.5) { + handleFlag(player, "Killaura I", "§b* §fLB like rotation\n§b* §fA=§b" + average + "\n§b* §fD=§b" + duplicates + "\n§b* §fS=§b" + std, getBanVL("KillauraH"), 300000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + samples.clear(); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraJ.java b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraJ.java new file mode 100644 index 0000000..2f2ed08 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/killaura/KillauraJ.java @@ -0,0 +1,63 @@ +package me.liwk.karhu.check.impl.killaura; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class KillauraJ extends Check { + + private float lastDeltaPitch; + private double buffer; + + public KillauraJ(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (((FlyingEvent) event).hasLooked()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.lastAttackTick > 5) { + return; + } + + float deltaPitch = MathUtil.getDistanceBetweenAngles(to.getPitch(), from.getPitch()); + float deltaYaw = MathUtil.getDistanceBetweenAngles(to.getYaw(), from.getYaw()); + + double range = 100; + if(playerData.getLastTarget() != null) { + Player target = playerData.getLastTarget(); + if (target != null) { + range = player.getEyeLocation().clone().toVector().setY(0.0D).distance(target.getEyeLocation().clone().toVector().setY(0.0D)); + } + } + + if(deltaYaw > 0.0 && deltaPitch > 0.0 && range > 0.6 && (!playerData.isCinematic() || System.currentTimeMillis() - playerData.getLastCinematic() > 2500L)) { + long dogPitch = (long) (deltaPitch * Math.pow(2, 24)); + long dogPitch2 = (long) (lastDeltaPitch * Math.pow(2, 24)); + if (MathUtil.getGcd(dogPitch, dogPitch2) <= 0b100000000000000000) { + if(++buffer > 13) { + handleFlag(player, "Killaura J", "§b* §fgcd modulo rotation\n§b* §fGCD=§b" + MathUtil.getGcd(dogPitch, dogPitch2), getBanVL("KillauraJ"), 300000L); + } + } else { + buffer = Math.max(buffer - 2.5, 0); + } + } else { + buffer = 0; + } + lastDeltaPitch = deltaPitch; + } + } + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/motion/MotionA.java b/src/main/java/me/liwk/karhu/check/impl/motion/MotionA.java new file mode 100644 index 0000000..2aa7bc9 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/motion/MotionA.java @@ -0,0 +1,54 @@ +package me.liwk.karhu.check.impl.motion; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; + +public class MotionA extends Check { + + private double buffer; + + public MotionA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + + if(UtilPlayer.isNearLiquid(player) + || UtilPlayer.isNearHoney(player) + || player.isInsideVehicle() + || UtilPlayer.isNearAnvil(player) + || UtilPlayer.isNearScaffolding(player) + || UtilPlayer.isNearClimbable(player) + || System.currentTimeMillis() - playerData.getLastMovePkt() > 2250L + || playerData.getVelocityV() > 0 + || player.getAllowFlight() + || playerData.getDeltaY() == 0 + || playerData.lastServerPositionTick < 60 + MathUtil.getPingInTicks(playerData.getTransPing()) + || playerData.slimeTicks > 0) return; + + if(UtilPlayer.isNearTrapdoor(player.getLocation()) || UtilPlayer.isNearIce(player)) { + buffer = 0; + return; + } + + if (playerData.getDeltaY() == -playerData.getLastDeltaY() && (playerData.getTeleportLocation() == null && System.currentTimeMillis() - playerData.getLastTeleport() > 3250L)) { + if(++buffer > 3) { + handleFlag(player, "Motion A", "§b* §fNot following MCP jump laws\n§b* §fMotion=§b" + playerData.getDeltaY(), getBanVL("MotionA"), 60000L); + } + } else { + buffer = Math.max(buffer - 1, 0.0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/motion/MotionB.java b/src/main/java/me/liwk/karhu/check/impl/motion/MotionB.java new file mode 100644 index 0000000..2acd951 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/motion/MotionB.java @@ -0,0 +1,58 @@ +package me.liwk.karhu.check.impl.motion; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class MotionB extends Check { + + private double buffer; + + public MotionB(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + float deltaY = playerData.getDeltaY(); + float expected = UtilPlayer.blockNearHead(player) ? 0.200000047f : 0.42f; + + if (UtilPlayer.isNearLiquid(player) + || UtilPlayer.isNearHoney(player) + || UtilPlayer.isNearScaffolding(player) + || UtilPlayer.isNearClimbable(player) + || player.isInsideVehicle() + || player.getAllowFlight() + || playerData.lastServerPositionTick < 80 + || playerData.getVelocityV() > 0 + || playerData.getVelocityH() > 0 + || playerData.droppedPackets + || playerData.halfTicks > 0 + || playerData.potTicks > 0 + || UtilPlayer.isOnLily(player) + || UtilPlayer.isOnCarpet(player) + || playerData.slimeTicks > 0) return; + + if (to.getY() % 1.0 > 0.0 && from.getY() % 1.0 == 0.0 && deltaY > 0 && (playerData.getTeleportLocation() == null && System.currentTimeMillis() - playerData.getLastTeleport() > 3250L)) { + if (deltaY < expected) { + if (++buffer > 12) { + handleFlag(player, "Motion B", "§b* §fNot following MCP jump laws\n§b* §fy=§b" + deltaY, getBanVL("MotionB"), 60000L); + } + } else { + buffer = Math.max(buffer - 2, 0.0); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/check/impl/motion/MotionC.java b/src/main/java/me/liwk/karhu/check/impl/motion/MotionC.java new file mode 100644 index 0000000..00f6356 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/motion/MotionC.java @@ -0,0 +1,105 @@ +package me.liwk.karhu.check.impl.motion; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +public class MotionC extends Check { + + private int buffer, buffer2; + private int ticks; + private int lastPlace; + + public MotionC(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + + if(UtilPlayer.isNearLiquid(player) + || UtilPlayer.isNearHoney(player) + || player.isInsideVehicle() + || UtilPlayer.isNearAnvil(player) + || UtilPlayer.isNearScaffolding(player) + || UtilPlayer.isNearClimbable(player) + || UtilPlayer.isClimbableBlock(player) + || playerData.getClimbableTicks() != 0 + || playerData.halfTicks > 0 + || playerData.getVelocityV() > 0 + || playerData.getTotalTicks() - lastPlace < 10 + || player.getAllowFlight() + || UtilPlayer.isNearWeb(player) + || playerData.liquidTicks > 0 + || playerData.getDeltaY() == 0 + || playerData.lastServerPositionTick < 120 + || playerData.getTotalTicks() - playerData.getLastTeleportReset() < 40 + || playerData.getTotalTicks() < 100 + || playerData.slimeTicks > 0) return; + + if(UtilPlayer.isNearTrapdoor(player.getLocation()) || UtilPlayer.isNearIce(player)) { + buffer = 0; + return; + } + + if (player.getLocation().getWorld().isChunkLoaded(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4)) { + buffer = Math.max(buffer - 1, 0); + return; + } + + if (playerData.getDeltaY() == playerData.getLastDeltaY() && (playerData.getTeleportLocation() == null && System.currentTimeMillis() - playerData.getLastTeleport() > 2250L)) { + if(++buffer > 2) { + handleFlag(player, "Motion C", "§b* §fNot following MCP jump laws\n§b* §fMotion=§b" + playerData.getDeltaY(), getBanVL("MotionA"), 60000L); + } + } else { + buffer = Math.max(buffer - 1, 0); + } + + if(!UtilPlayer.isNearGround(player.getLocation()) && isAgainstBlock(player.getLocation())) { + if(++ticks > 7) { + if(playerData.getDeltaY() > 0.1) { + if(++buffer2 > 3) { + handleFlag(player, "Motion C", "§b* §fClimbing a wall\n§b* §fMotion=§b" + playerData.getDeltaY(), getBanVL("MotionA"), 60000L); + } + } else { + buffer2 = Math.max(buffer2 - 1, 0); + } + } + } else { + ticks = 0; + } + } + } + } + private boolean isAgainstBlock(Location loc) { + double expand = 0.3; + + if (!loc.getWorld().isChunkLoaded(loc.getBlockX() >> 4, loc.getBlockZ() >> 4)) { + return false; + } + + for (double x = -expand; x <= expand; x += expand) { + for (double z = -expand; z <= expand; z += expand) { + if (loc.clone().add(x, 0.0001, z).getBlock().getType().isSolid()) { + return true; + } + } + } + for (double x = -expand; x <= expand; x += expand) { + for (double z = -expand; z <= expand; z += expand) { + if (loc.clone().add(x, 1.0001, z).getBlock().getType().isSolid()) { + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/nofall/NofallA.java b/src/main/java/me/liwk/karhu/check/impl/nofall/NofallA.java new file mode 100644 index 0000000..3a375cc --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/nofall/NofallA.java @@ -0,0 +1,67 @@ +package me.liwk.karhu.check.impl.nofall; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import me.liwk.karhu.util.task.Tasker; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class NofallA extends Check { + + private double buffer; + + public NofallA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + boolean onGround = UtilPlayer.isOnGroundBB(player); + + boolean lastOnGround = playerData.lastOnGround; + playerData.lastOnGround = onGround; + + boolean lastLastOnGround = playerData.lastLastOnGround; + playerData.lastLastOnGround = lastOnGround; + + if (player.getLocation().getWorld().isChunkLoaded(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4)) { + buffer = Math.max(buffer - 0.75, 0); + return; + } + + if(((FlyingEvent) event).isOnGround() && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 10 && !UtilPlayer.isOnBadJesusBlock(player) && !onGround && !lastOnGround && !lastLastOnGround) { + if(PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + Tasker.run(() -> { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + }); + } else { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + } + if(++buffer > 5) { + handleFlag(player, "Nofall A", "§b* §fSpoofed ground state", getBanVL("NofallA"), 60000L); + } + } else { + buffer = Math.max(buffer - 0.75, 0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/nofall/NofallB.java b/src/main/java/me/liwk/karhu/check/impl/nofall/NofallB.java new file mode 100644 index 0000000..dcbfb7a --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/nofall/NofallB.java @@ -0,0 +1,84 @@ +package me.liwk.karhu.check.impl.nofall; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.BlockPlaceEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.UtilPlayer; +import me.liwk.karhu.util.task.Tasker; +import org.bukkit.Location; +import org.bukkit.entity.Boat; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class NofallB extends Check { + + private int lastPlace; + private double buffer; + + public NofallB(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + final boolean serverGround = MathUtil.onGround(to.getY()); + + if(!player.getAllowFlight() + && playerData.lastServerPositionTick > 100 + && playerData.getTotalTicks() > 400 + && playerData.getVelocityV() <= 0 + && playerData.getVelocityH() <= 0 + && player.getVehicle() == null + && !UtilPlayer.isClimbableBlock(player) + && playerData.getClimbableTicks() == 0 + && !UtilPlayer.isOnStair2(player) + && !UtilPlayer.isOnSlab(player) + && !UtilPlayer.isNearSlime(player.getLocation()) + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 5 + && !playerData.hasFast() + && playerData.getTotalTicks() - lastPlace > 15 + && playerData.getLiquidTicks() == 0 + && !playerData.isLagging(System.currentTimeMillis(), 250L)) { + if (serverGround != ((FlyingEvent) event).isOnGround()) { + if(PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + Tasker.run(() -> { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + }); + } else { + for (Entity ent : player.getNearbyEntities(3.0, 3.0, 3.0)) { + if (ent instanceof Boat) { + return; + } + } + } + if (++buffer > 7) { + handleFlag(player, "Nofall B", "§b* §fserverGround=§b" + MathUtil.onGround(to.getY()) + "\n§b* §fclientGround=§b" + playerData.isOnGroundPacket(), getBanVL("NofallB"), 60000L); + } + + } else buffer = Math.max(buffer - 0.75, 0); + + } else buffer = Math.max(buffer - 0.5, 0); + } + } else if(event instanceof BlockPlaceEvent) { + if (((BlockPlaceEvent) event).getItemStack().getType().isSolid()) { + this.lastPlace = playerData.getTotalTicks(); + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowA.java b/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowA.java new file mode 100644 index 0000000..cc07d2f --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowA.java @@ -0,0 +1,71 @@ +package me.liwk.karhu.check.impl.noslow; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.BlockPlaceEvent; +import me.liwk.karhu.event.DigEvent; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +import static me.liwk.karhu.util.MovementUtils.getPotionEffectLevel; + +public class NoSlowA extends Check { + + private double buffer; + private boolean blocking; + + public NoSlowA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + + float maxSpeed = ((FlyingEvent) event).isOnGround() ? 0.21f : 0.32400000005960464f; + + float speedLevel = getPotionEffectLevel(player, PotionEffectType.SPEED); + + maxSpeed += (player.getWalkSpeed() - 0.2) * 0.02; + maxSpeed += (player.getFlySpeed() - 0.1) * 0.01; + maxSpeed += playerData.getVelocityHorizontal(); + maxSpeed *= playerData.iceTicks > 0 ? 6.8f : 1.0; + maxSpeed += playerData.getGroundTicks() < 5 ? speedLevel * 0.07f : speedLevel * 0.0573f; + + if(playerData.iceTicks == 0) { + if(UtilPlayer.isNearIce(player)) { + maxSpeed *= 6.8f; + } + } + + if (player.isBlocking() && blocking) { + if (playerData.deltaXZ > maxSpeed) { + if (++buffer > 15) { + buffer -= 5; + handleFlag(player, "NoSlow A", "§b* §fdeltaXZ=§b" + playerData.deltaXZ + "\n§b* §fmaxSpeed=§b" + maxSpeed, getBanVL("NoSlowA"), 40000L); + } + } else { + buffer = 0; + } + } else { + buffer = 0; + } + } + } else if (event instanceof DigEvent) { + if (((DigEvent) event).getDigType().equals(WrappedPacketInBlockDig.PlayerDigType.RELEASE_USE_ITEM)) { + blocking = false; + } + } else if (event instanceof BlockPlaceEvent) { + if (((BlockPlaceEvent) event).getItemStack().toString().toUpperCase().contains("SWORD")) { + blocking = true; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowB.java b/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowB.java new file mode 100644 index 0000000..c3ef1b6 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/noslow/NoSlowB.java @@ -0,0 +1,31 @@ +package me.liwk.karhu.check.impl.noslow; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.DigEvent; +import org.bukkit.entity.Player; + +public class NoSlowB extends Check { + + private int buffer; + + public NoSlowB(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof DigEvent) { + if(((DigEvent) event).getDigType().equals(WrappedPacketInBlockDig.PlayerDigType.RELEASE_USE_ITEM) && playerData.isPlacing() && playerData.getDeltaXZ() > 0.125) { + if(++buffer > 3) { + handleFlag(player, "NoSlow B", "§b* §fdeltaXZ=§b" + playerData.deltaXZ + "\n§b* §fSending bad block packets", getBanVL("NoSlowB"), 40000L); + } + } else { + buffer = 0; + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/phase/PhaseA.java b/src/main/java/me/liwk/karhu/check/impl/phase/PhaseA.java new file mode 100644 index 0000000..7ce0401 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/phase/PhaseA.java @@ -0,0 +1,42 @@ +package me.liwk.karhu.check.impl.phase; + +public class PhaseA /*extends Check*/ { + + /*private int pullbackTicks; + private double buffer; + + public PhaseA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasLooked() || ((FlyingEvent) event).hasMoved()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (playerData.getDeltaY() < -0.0979D && playerData.getDeltaY() > -0.0981D && playerData.getDeltaXZ() < 0.5D && (player.getGameMode().equals(GameMode.SURVIVAL) || player.getGameMode().equals(GameMode.ADVENTURE)) && playerData.getTotalTicks() - pullbackTicks < 2 + MathUtil.getPingInTicks(playerData.getTransPing())) return; + + if(isInBlock(to.clone().subtract(0.175, 0, 0.175)) && !UtilPlayer.isNearPiston(player.getLocation()) && !UtilPlayer.isNearTrapdoor(player.getLocation())) { + if(playerData.getTotalTicks() - playerData.getLastOpeningInteract() < 15) return; + //player.teleport(new Location(player.getWorld(), from.getX() - 0.55, from.getY(), from.getZ() - 0.35, playerData.getLastLastLocation().getPitch(), playerData.getLastLastLocation().getYaw())); + this.pullbackTicks = playerData.getTotalTicks(); + if(++buffer > 6) { + handleFlag(player, "Phase A §4^" , "§f* Attempting to go trough blocks\n§f* dXZ=§b" + playerData.getDeltaXZ() + "\n§f* dY=§b" + playerData.getDeltaY(), getBanVL("PhaseA"), 30000L); + } + } else { + buffer = Math.max(buffer - 0.5, 0); + } + } + } + } + public static boolean isInBlock(Location location) { + if (location.getWorld().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)) { + return true; + } + Block block = location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + 1, location.getBlockZ()); + return block.getType().isSolid() && !block.getType().toString().contains("SIGN") && !block.getType().toString().contains("TRAPDOOR") && !block.getType().toString().contains("DOOR") && !block.getType().toString().contains("VINE") && !block.getType().toString().contains("LADDER") && !block.getType().toString().contains("OBSIDIAN"); + }*/ +} diff --git a/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofA.java b/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofA.java new file mode 100644 index 0000000..bcee658 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofA.java @@ -0,0 +1,34 @@ +package me.liwk.karhu.check.impl.pingspoof; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class PingSpoofA extends Check { + + private int ticks; + + public PingSpoofA(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if(playerData.getPing() > playerData.getTransPing() + 100 + && Math.abs(playerData.getPing() - playerData.getTransPing()) > 300L + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 10 + && !playerData.hasFast() + && playerData.lastServerPositionTick > 150 + && !playerData.isLagging(System.currentTimeMillis(), 400L) + && playerData.getTotalTicks() > 400) { + if(++ticks > 1000) { + handleFlag(player, "PingSpoof A §4^", "§b* §fKeepAlive=§b" + playerData.getPing() + "\n§b* §fTransaction=§b" + playerData.getTransPing(), getBanVL("PingSpoofA"), 40000L); + } + } else ticks = 0; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofB.java b/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofB.java new file mode 100644 index 0000000..dc9232c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/pingspoof/PingSpoofB.java @@ -0,0 +1,34 @@ +package me.liwk.karhu.check.impl.pingspoof; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class PingSpoofB extends Check { + + private int ticks; + + public PingSpoofB(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if(playerData.getTransPing() > playerData.getPing() + 100 + && Math.abs(playerData.getTransPing() - playerData.getPing()) > 250L + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 10 + && !playerData.hasFast() + && playerData.lastServerPositionTick > 150 + && !playerData.isLagging(System.currentTimeMillis(), 400L) + && playerData.getTotalTicks() > 400) { + if(++ticks > 1000) { + handleFlag(player, "PingSpoof B §4^", "§b* §fTransaction=§b" + playerData.getTransPing() + "\n§b* §fKeepAlive=§b" + playerData.getPing(), getBanVL("PingSpoofB"), 40000L); + } + } else ticks = 0; + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/reach/ReachA.java b/src/main/java/me/liwk/karhu/check/impl/reach/ReachA.java new file mode 100644 index 0000000..a10b85a --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/reach/ReachA.java @@ -0,0 +1,88 @@ +package me.liwk.karhu.check.impl.reach; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.Karhu; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.AxisAlignedBB; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.pair.Pair; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + +public class ReachA extends Check { + + private double buffer; + + public ReachA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + Location to = ((FlyingEvent) event).toLocation(); + if (!player.getGameMode().equals(GameMode.CREATIVE) + && playerData.getLastTarget() != null + && playerData.getLastAttackTick() <= 1 + && !playerData.isLagging(System.currentTimeMillis(), 250L) + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 20 + && playerData.getPastLocations().size() >= 20 + && playerData.getTeleportLocation() == null + && player.getVehicle() == null + && System.currentTimeMillis() - playerData.getLastTeleport() > 5000L + && System.currentTimeMillis() - playerData.getLastLag() > 100L + && System.currentTimeMillis() - playerData.getLastDelayedPacket() > 160L) { + + Player target = playerData.getLastTarget(); + + if(Karhu.getInstance().getPlayerDataManager().getPlayerData(target).lastServerPositionTick < 100 || Karhu.getInstance().getPlayerDataManager().getPlayerData(target).getTotalTicks() - Karhu.getInstance().getPlayerDataManager().getPlayerData(target).getLastPacketDrop() < 15) return; + + AxisAlignedBB axisalignedbb = new AxisAlignedBB(0, 0, 0, 0, 0, 0); + boolean a = false; + + final int nowTicks = playerData.getTotalTicks(); + final int pingTicks = MathUtil.getPingInTicks(playerData.getPing()) + 3; + + final Vector origin = to.toVector(); + + double distance = -1; + int collided = 0; + + for (Pair pair : playerData.getPastLocations()) { + if (Math.abs(nowTicks - pair.getY() - pingTicks) < 2) { + if (!a) { + axisalignedbb = pair.getX(); + a = true; + } else { + axisalignedbb.minX = Math.min(axisalignedbb.minX, pair.getX().minX); + axisalignedbb.maxX = Math.max(axisalignedbb.maxX, pair.getX().maxX); + axisalignedbb.minY = Math.min(axisalignedbb.minY, pair.getX().minY); + axisalignedbb.maxY = Math.max(axisalignedbb.maxY, pair.getX().maxY); + axisalignedbb.minZ = Math.min(axisalignedbb.minZ, pair.getX().minZ); + axisalignedbb.maxZ = Math.max(axisalignedbb.maxZ, pair.getX().maxZ); + } + double boxX = Math.abs(axisalignedbb.minX - axisalignedbb.maxX) / 2, boxZ = Math.abs(axisalignedbb.minZ - axisalignedbb.maxZ) / 2; + Vector loc = new Vector(axisalignedbb.minX + boxX, 0, axisalignedbb.minZ + boxZ); + + distance = (origin.setY(0).distance(loc) - Math.hypot(boxX, boxZ) - 0.1f) - 0.05f; + if(distance > 3.05) + ++collided; + } + } + + if(distance > 3.05 && collided > 2) { + if ((buffer += 1.5) > 3.5) { + handleFlag(player, "Reach A", "§b* §fR: §b" + distance + "\n§b* §fC: §b" + collided + "\n§b* §fE: §b3.0", getBanVL("ReachA"), 30000L); + } + } else { + buffer = Math.max(buffer - 0.75, 0); + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/reach/ReachB.java b/src/main/java/me/liwk/karhu/check/impl/reach/ReachB.java new file mode 100644 index 0000000..1dc4cf1 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/reach/ReachB.java @@ -0,0 +1,120 @@ +package me.liwk.karhu.check.impl.reach; + +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.entity.EntityLocationHandler; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.AxisAlignedBB; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.MovingObjectPosition; +import me.liwk.karhu.util.Vec3; +import me.liwk.karhu.util.pair.Pair; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +public class ReachB extends Check { + + private double buffer, bufferBox; + private Vec3 eyeLocation; + private Vec3 look, lookMouseDelayFix; + + public ReachB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (!player.getGameMode().equals(GameMode.CREATIVE) + && playerData.getLastTarget() != null + && !playerData.isLagging(System.currentTimeMillis(), 200L) + && playerData.getPastLocs().size() >= 10 + && playerData.getLastAttackTick() <= 1 + && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 5 + && System.currentTimeMillis() - playerData.getLastDelayedPacket() > 160L + && playerData.lastServerPositionTick > 100 + Math.min(MathUtil.getPingInTicks(playerData.getTransPing()), 5)) { + + + float sneakAmount1_8 = playerData.isSneaking() ? 1.54F : 1.62F; + float sneakAmount1_13 = playerData.isSneaking() ? 1.27F : 1.62F; + + if (playerData.getLocation() != null) { + eyeLocation = MathUtil.getPositionEyes(playerData.attackerX, playerData.attackerY, playerData.attackerZ, playerData.getClientVersion().isLowerThan(ClientVersion.v_1_13) ? sneakAmount1_8 : sneakAmount1_13); + } + + if (((FlyingEvent) event).hasLooked()) { + lookMouseDelayFix = MathUtil.getVectorForRotation(playerData.attackerPitch, ((FlyingEvent) event).getYaw()); + look = MathUtil.getVectorForRotation(playerData.attackerPitch, playerData.attackerYaw); + } else { + lookMouseDelayFix = MathUtil.getVectorForRotation(playerData.attackerPitch, playerData.attackerYaw); + look = lookMouseDelayFix; + } + + Vec3 vec3 = eyeLocation; + Vec3 vec31 = look; + Vec3 vec311 = lookMouseDelayFix; + + Vec3 vec32 = vec3.addVector(vec31.xCoord * 6.0D, vec31.yCoord * 6.0D, vec31.zCoord * 6.0D); + Vec3 vec322 = vec3.addVector(vec311.xCoord * 6.0D, vec311.yCoord * 6.0D, vec311.zCoord * 6.0D); + + AxisAlignedBB axisalignedbb = new AxisAlignedBB(0, 0, 0, 0, 0, 0); + boolean a = false; + + int nowTicks = playerData.getTotalTicks(); + int pingTicks = MathUtil.getPingInTicks(playerData.getTransPing()) + 3; + + double distance = -1; + int collided = 0, missed = 0; + + for (Pair pair : playerData.getPastLocs()) { + if (Math.abs(nowTicks - pair.getY() - pingTicks) < 2) { + if (!a) { + axisalignedbb = pair.getX(); + a = true; + } else { + axisalignedbb.minX = Math.min(axisalignedbb.minX, pair.getX().minX); + axisalignedbb.maxX = Math.max(axisalignedbb.maxX, pair.getX().maxX); + axisalignedbb.minY = Math.min(axisalignedbb.minY, pair.getX().minY); + axisalignedbb.maxY = Math.max(axisalignedbb.maxY, pair.getX().maxY); + axisalignedbb.minZ = Math.min(axisalignedbb.minZ, pair.getX().minZ); + axisalignedbb.maxZ = Math.max(axisalignedbb.maxZ, pair.getX().maxZ); + } + + MovingObjectPosition movingobjectposition = axisalignedbb.calculateIntercept(vec3, vec32); + MovingObjectPosition movingobjectposition2 = axisalignedbb.calculateIntercept(vec3, vec322); + + if (movingobjectposition != null && movingobjectposition2 != null && !axisalignedbb.isVecInside(vec3)) { + double d3 = vec3.distanceTo(movingobjectposition.hitVec); + double d33 = vec3.distanceTo(movingobjectposition2.hitVec); + distance = Math.min(d3, d33); + if (distance > 3.03D) { + ++collided; + } + } else if (movingobjectposition == null && movingobjectposition2 == null && !axisalignedbb.isVecInside(vec3)) { + ++missed; + } + } + } + + double maxDist = 3.0D; + //Bukkit.broadcastMessage("P: " + player.getName() + "R: " + distance + " C: " + collided); + if (distance > 3.03D && collided > 2) { + + handleVerbose(player, "Reach B", "§b* §fRange=§b" + distance + "\n§b* §fCollided=§b" + collided + "\n§b* §fPredict=§b" + maxDist + "\n§b* §fvl=§b" + buffer); + + if ((buffer += 1.5) > 3.5) { + + handleFlag(player, "Reach B", "§b* §fRange=§b" + distance + "\n§b* §fCollided=§b" + collided + "\n§b* §fPredict=§b" + maxDist + "\n§b* §fvl=§b" + buffer, getBanVL("ReachB"), 60000L); + } + } else { + buffer = Math.max(buffer - 0.5, 0); + } + } + EntityLocationHandler.updateFlyingLocations(playerData, (FlyingEvent) event); + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/scaffold/ScaffoldA.java b/src/main/java/me/liwk/karhu/check/impl/scaffold/ScaffoldA.java new file mode 100644 index 0000000..fb72f6c --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/scaffold/ScaffoldA.java @@ -0,0 +1,36 @@ +package me.liwk.karhu.check.impl.scaffold; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.BlockPlaceEvent; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class ScaffoldA extends Check { + + private long lastFlying = -1; + private int buffer; + + public ScaffoldA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + lastFlying = System.currentTimeMillis(); + } else if (event instanceof BlockPlaceEvent) { + if(((BlockPlaceEvent) event).getItemStack().getType().isSolid()) { + if ((System.currentTimeMillis() - lastFlying) < 10L && !playerData.isLagging(System.currentTimeMillis(), 200L) && playerData.lastServerPositionTick > 20 && playerData.getTotalTicks() - playerData.getLastPacketDrop() > 20) { + if (++buffer > 8) { + handleFlag(player, "Scaffold A", "§b* §fPost scaffold\n§b* §ftime=§b" + (System.currentTimeMillis() - lastFlying), getBanVL("ScaffoldA"), 60000L); + } + } else { + buffer = 0; + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/speed/SpeedA.java b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedA.java new file mode 100644 index 0000000..e5ce1d6 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedA.java @@ -0,0 +1,118 @@ +package me.liwk.karhu.check.impl.speed; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.playerhandler.Handler1_13; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class SpeedA extends Check { + + private double buffer, buffer2; + + public SpeedA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + + float maxSpeed = (((FlyingEvent) event).isOnGround() && !UtilPlayer.isOnLily(player)) ? 0.3125f : 0.3585f; + float maxSpeed2 = (((FlyingEvent) event).isOnGround() && !UtilPlayer.isOnLily(player)) ? 0.6f : 1.0f; + + float speedLevel = MovementUtils.getPotionEffectLevel(player, PotionEffectType.SPEED); + float depthStriderLevel = MovementUtils.getDepthStriderLevel(player); + float dolphinLevel = 0; + + if(PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + dolphinLevel = Handler1_13.getDolphinLevel(player); + } + + if (player.isFlying() + || player.getAllowFlight() + || playerData.getTeleportLocation() != null + || player.getGameMode().equals(GameMode.CREATIVE) + || player.isInsideVehicle() + || event.getTimestamp() - playerData.getLastVehicle() < 2000L + || (playerData.getTotalTicks() - playerData.lastTeleportReset) < 25 + || playerData.getDeltaXZ() <= 0.01) return; + + + maxSpeed2 += playerData.getGroundTicks() < 5 ? speedLevel * 0.07f : speedLevel * 0.0573f; + maxSpeed2 *= playerData.iceTicks > 0 ? 4.4f : 1.0; + + maxSpeed += playerData.getGroundTicks() < 5 ? speedLevel * 0.07f : speedLevel * 0.0573f; + maxSpeed *= playerData.iceTicks > 0 ? 4.4f : 1.0; + + if(playerData.iceTicks == 0) { + if(UtilPlayer.isNearIce(player)) { + maxSpeed *= 4.4f; + maxSpeed2 *= 4.4f; + } + } + + maxSpeed *= playerData.slimeTicks > 0 ? 1.25f : 1.0; + maxSpeed += UtilPlayer.blockNearHead(player) ? 0.25 : 0.0; + + if(playerData.getTotalTicks() - playerData.getLastFlyTick() < 40) { + maxSpeed += 0.3; + maxSpeed2 += 0.3; + } + + if(playerData.getLiquidTicks() > 0) { + maxSpeed += dolphinLevel * 0.15f; + maxSpeed += (depthStriderLevel * 0.45f); + } + + maxSpeed += (player.getWalkSpeed() - 0.2) * 2.5; + maxSpeed += (player.getFlySpeed() - 0.1) * 2.5; + + maxSpeed2 += (player.getWalkSpeed() - 0.2) * 2.5; + maxSpeed2 += (player.getFlySpeed() - 0.1) * 2.5; + + maxSpeed *= (UtilPlayer.isOnStair(player) || UtilPlayer.isOnStair2(player)) || UtilPlayer.isOnSlab(player) ? 1.5f : 1.0; + + maxSpeed += playerData.getVelocityHorizontal(); + maxSpeed2 += playerData.getVelocityHorizontal(); + + if(playerData.isUnderBlock()) + maxSpeed += 0.26; + + maxSpeed += System.currentTimeMillis() - playerData.getLastGlide() < 4400L ? 2 : 0; + maxSpeed2 += System.currentTimeMillis() - playerData.getLastGlide() < 4400L ? 1.7 : 0; + maxSpeed += System.currentTimeMillis() - playerData.getLastRiptide() < 4400L ? 2 : 0; + maxSpeed2 += System.currentTimeMillis() - playerData.getLastRiptide() < 4400L ? 1.7 : 0; + + //Bukkit.broadcastMessage("§7[§c§lPOST-DEBUG§7] §fS: §c" + playerData.getDeltaXZ() + " §fM: §c" + maxSpeed); + + if (playerData.deltaXZ > maxSpeed) { + if(++buffer > 15) { + buffer -= 2; + handleFlag(player, "Speed A", "§b* §fdeltaXZ=§b" + playerData.deltaXZ + "\n§b* §fmaxSpeed=§b" + maxSpeed, getBanVL("SpeedA"), 60000L); + } + } else { + buffer -= buffer > 0 ? 1 : 0; + } + + if(playerData.deltaXZ > maxSpeed2 && !playerData.isLagging2(System.currentTimeMillis(), 100L)) { + if((buffer2 += 10) > 30) { + handleFlag(player, "Speed A", "§b* §fdeltaXZ=§b" + playerData.deltaXZ + "\n§b* §fmaxSpeed=§b" + maxSpeed2, getBanVL("SpeedA"), 60000L); + } + } else { + buffer2 -= buffer2 > 0 ? 1 : 0; + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/speed/SpeedB.java b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedB.java new file mode 100644 index 0000000..a0b438b --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedB.java @@ -0,0 +1,62 @@ +package me.liwk.karhu.check.impl.speed; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; + +public class SpeedB extends Check { + + private int buffer; + private double lastDeltaH; + + public SpeedB(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + + if (player.getAllowFlight() || playerData.isInLiquid() || UtilPlayer.isOnGroundBB(player) + || ((FlyingEvent) event).isOnGround() + || UtilPlayer.isClimbableBlock(player) + || playerData.lastServerPositionTick < 120 + || playerData.getVelocityH() > 0 + || player.getWalkSpeed() != 0.2 + || System.currentTimeMillis() - playerData.getLastRiptide() < 4250L + || System.currentTimeMillis() - playerData.getLastGlide() < 4250L + || playerData.isInWeb()) { + return; + } + + + if (playerData.getDeltaXZ() <= 0.005) { return; } // dont wanna flag if they arent moving + + double deltaH = Math.hypot(playerData.getDeltaX(), playerData.getDeltaZ()); + + double diff = lastDeltaH * 0.91F + 0.02; // calculate difference between movements + + if(playerData.isSprinting()) diff += 0.0063; + + double shit = deltaH - diff; + + if (shit > 0.000000000001 && diff > 0.08 && deltaH > 0.15) { // catches alot of shitty hops that edit friction even a bit + if (++buffer > 8) { + this.handleFlag(player, "Speed B", "§b* §fIgnoring friction\n§b* §fdiff=§b" + shit, getBanVL("SpeedB"), 60000L); + buffer /= 2; + } + } else { + buffer = 0; + } + lastDeltaH = deltaH; + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/speed/SpeedC.java b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedC.java new file mode 100644 index 0000000..54790fe --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/speed/SpeedC.java @@ -0,0 +1,152 @@ +package me.liwk.karhu.check.impl.speed; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.playerhandler.Handler1_13; +import me.liwk.karhu.playerhandler.Handler1_8; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class SpeedC extends Check { + + private double lastOffsetH, lastDeltaH; + private double friction = 0.91; + private double buffer; + + public SpeedC(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (((FlyingEvent) event).hasMoved()) { + + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + + if (player.getAllowFlight() + || UtilPlayer.isClimbableBlock(player) + || player.getVehicle() != null + || player.getGameMode().equals(GameMode.CREATIVE) + || Handler1_8.isSpectating(player) + || System.currentTimeMillis() - playerData.getLastRiptide() < 4250L + || System.currentTimeMillis() - playerData.getLastGlide() < 4250L + || playerData.isRiptiding() + || playerData.isGliding()) { + return; + } + + float speedLevel = MovementUtils.getPotionEffectLevel(player, PotionEffectType.SPEED); + float soulSpeedEnchant = Handler1_13.getSoulSpeedEnchant(player); + + + double dx = to.getX() - from.getX(); + double dz = to.getZ() - from.getZ(); + + //double offsetH = MathUtil.hypot(dx, dz); //Calculating the offset + double deltaH = Math.sqrt(dx * dx + dz * dz); //Calculating the offset + double offsetY = to.getY() - from.getY(); //Calculating the offset + + double jumpHeight = 0.42 + (MovementUtils.getPotionEffectLevel(player, PotionEffectType.JUMP) * 0.2f); //Accounting for jumps like Baldr + + double movementSpeed; + + if (playerData.isLastOnGroundPacket()) { + + if (PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + movementSpeed = playerData.isSprinting() ? (0.0699999988079071D + 0.026F + 0.005999999865889549D) * 1.3 : 0.1; + } else { + movementSpeed = playerData.isSprinting() ? (0.0699999988079071D + 0.030000001192092896) * 1.3 : 0.1; + } + + movementSpeed *= 0.16277136 / (friction * friction * friction); + + if (offsetY > 0.0000001 && offsetY < jumpHeight && playerData.isSprinting()) { + movementSpeed += 0.2; //Jump shit + } + + if (playerData.halfTicks > 0) { + movementSpeed += 0.2; + } + + if (playerData.iceTicks > 0) { + movementSpeed += 0.11; + } + + } else { + + movementSpeed = 0.026; // Base speed when sprinting = 0.26 else 0.2 + + friction = 0.91; + + if (PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_12_2)) { + if (playerData.getMovementsSinceUnderBlock() < 4) { + movementSpeed += 0.0435; + } + } else { + if (playerData.getMovementsSinceUnderBlock() < 4) { + movementSpeed = movementSpeed * 2; + } + } + + if (playerData.halfTicks > 0) { + movementSpeed += 0.2; + } + + if (offsetY > 0.4199 && playerData.isSprinting()) { + movementSpeed += 0.4199; //Fix for 1 false + } + } + + //Bukkit.broadcastMessage("f: " + friction + " d: " + (friction - blockFriction)); + + movementSpeed += (speedLevel * 0.2) * movementSpeed; //EntityLiving.class + + movementSpeed += (soulSpeedEnchant * 0.35F) * movementSpeed; //EntityLiving.class + + movementSpeed += playerData.getVelocityHorizontal(); //Velocity + + movementSpeed += (player.getWalkSpeed() - 0.2) * 12; + + if (playerData.isInLiquid()) { + movementSpeed += 0.2; + movementSpeed *= 8; + } + + + double diff = deltaH - lastDeltaH; + double speedup = diff - movementSpeed; + + if (speedup > 0.0012 && deltaH > 0.25 && (playerData.getTeleportLocation() == null && System.currentTimeMillis() - playerData.getLastTeleport() > 2250L)) { + + buffer = Math.min(500, buffer + 10 + speedup); //We do this to prevent integer overflow. + + handleVerbose(player, "Speed C", "§b* §fNot following mc speed laws\n§b* §fspeed=§b" + speedup + "\n§b* §fvl=§b" + buffer + " oY: " + offsetY + " g: " + player.isOnGround() + " gt: " + playerData.getGroundTicks()); + + if (buffer > 25) { + this.handleFlag(player, "Speed C", "§b* §fNot following mc speed laws\n§b* §fspeed=§b" + speedup + "\n§b* §fvl=§b" + buffer, getBanVL("SpeedC"), 60000L); + buffer -= 10; + } + } else { + buffer = Math.max(buffer - 0.5, -5); + } + + lastOffsetH = deltaH * friction; + lastDeltaH = deltaH * friction; + friction = UtilPlayer.getBlockFriction(player) * 0.91; + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/sprint/OmniSprintA.java b/src/main/java/me/liwk/karhu/check/impl/sprint/OmniSprintA.java new file mode 100644 index 0000000..05f6d40 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/sprint/OmniSprintA.java @@ -0,0 +1,72 @@ +package me.liwk.karhu.check.impl.sprint; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.playerhandler.Handler1_8; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class OmniSprintA extends Check { + + private double buffer; + + public OmniSprintA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + Location to = ((FlyingEvent) event).toLocation(); + Location from = playerData.getLastLocation(); + if(!playerData.isSprinting() + || playerData.isInLiquid() + || playerData.getVelocityH() > 0 + || playerData.getLastServerPositionTick() < 100 + MathUtil.getPingInTicks(playerData.getTransPing()) + || !MathUtil.onGround(to.getY()) + || playerData.droppedPackets + || player.isFlying() + || playerData.getTotalTicks() - playerData.getLastFlyTick() < 40 + || player.getGameMode().equals(GameMode.CREATIVE) + || Handler1_8.isSpectating(player) + || playerData.isLagging(System.currentTimeMillis(), 100L) + || !UtilPlayer.isOnGroundBB(player) + || playerData.getDeltaXZ() < 0.11) { + buffer = Math.max(buffer - 1, 0); + return; + } + + double speedDelta = MathUtil.getVectorSpeed(from, to).distanceSquared(MathUtil.getDirection(player)); + double maxDelta = 0.221; + float speedLevel = MovementUtils.getPotionEffectLevel(player, PotionEffectType.SPEED); + + maxDelta += playerData.getVelocityH() > 0 ? playerData.getVelocityHorizontal() : 0.0; + maxDelta += playerData.getGroundTicks() < 5 ? speedLevel * 0.07f : speedLevel * 0.0573f; + maxDelta *= playerData.iceTicks > 0 ? 1.5f : 1.0; + maxDelta *= UtilPlayer.isOnStair(player) || UtilPlayer.isOnSlab(player) ? 1.5f : 1.0; + maxDelta += (player.getWalkSpeed() - 0.2) * 5; + + if(speedDelta > maxDelta) { + if((buffer += 0.75) > 8) { + handleFlag(player, "OmniSprint A", "§b* §fspeedDelta=§b" + speedDelta + "\n§b* §fmaxDelta=§b" + maxDelta, getBanVL("OmniSprintA"), 30000L); + } + } else { + buffer = Math.max(buffer - 0.5, 0); + } + } + } + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/step/StepA.java b/src/main/java/me/liwk/karhu/check/impl/step/StepA.java new file mode 100644 index 0000000..74b3d30 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/step/StepA.java @@ -0,0 +1,57 @@ +package me.liwk.karhu.check.impl.step; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.MovementUtils; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class StepA extends Check { + + private double buffer; + + public StepA(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + + //Bukkit.broadcastMessage("dY: " + playerData.getDeltaY() + " gLL: " + MathUtil.onGround(playerData.getLastLocation().getY()) + " gLLL: " + MathUtil.onGround(playerData.getLastLastLastLocation().getY())); + + if(player.getAllowFlight() + || playerData.getVelocityV() > 0 + || event.getTimestamp() - playerData.getLastVehicle() < 2000L + || playerData.getSlimeTicks() > 0 + || System.currentTimeMillis() - playerData.lastOnSlime < 4250L + || playerData.getLastServerPositionTick() < 65 + || System.currentTimeMillis() - playerData.getLastTeleport() < 2250L + || playerData.getTotalTicks() < 50 + || playerData.liquidTicks > 0) return; + + float limit = 0.5625f + ((MovementUtils.getPotionEffectLevel(player, PotionEffectType.JUMP) * 0.2f) + playerData.getVelocityV()); + + limit += System.currentTimeMillis() - playerData.getLastGlide() < 4400L ? 4.2 : 0; + limit += System.currentTimeMillis() - playerData.getLastRiptide() < 4400L ? 4.2 : 0; + limit += playerData.getSlimeHeight(); + + if (playerData.deltaY > limit && (MathUtil.onGround(playerData.getLastLocation().getY()) || MathUtil.onGround(playerData.getLastLastLastLocation().getY()))) { + if (++buffer > 2) { + handleFlag(player, "Step A", "§b* §fMotionY=§b" + playerData.deltaY + "\n§b* §fpredict=§b" + limit, getBanVL("StepA"), 800000L); + } + } else { + if(playerData.deltaY > 0) { + buffer = Math.max(buffer - 1, 0); + } + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/step/StepB.java b/src/main/java/me/liwk/karhu/check/impl/step/StepB.java new file mode 100644 index 0000000..046872a --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/step/StepB.java @@ -0,0 +1,57 @@ +package me.liwk.karhu.check.impl.step; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.MovementUtils; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffectType; + +public class StepB extends Check { + + private double buffer; + + public StepB(final PlayerData playerData) { + super(Category.MOVEMENT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if(((FlyingEvent) event).hasMoved()) { + + if(player.getAllowFlight() + || playerData.getVelocityV() > 0.0 + || event.getTimestamp() - playerData.getLastVehicle() < 2000L + || playerData.getTotalTicks() < 50 + || System.currentTimeMillis() - playerData.getLastTeleport() < 2250L + || playerData.getLastServerPositionTick() < 65) return; + + float limit = 0.8f + ((MovementUtils.getPotionEffectLevel(player, PotionEffectType.JUMP)) * 0.2f); + + limit += System.currentTimeMillis() - playerData.getLastGlide() < 4400L ? 5.2 : 0; + limit += System.currentTimeMillis() - playerData.getLastRiptide() < 4400L ? 5.2 : 0; + limit += playerData.getSlimeHeight(); + + if (playerData.deltaY >= limit) { + if (MathUtil.onGround(playerData.getLastLocation().getY()) || MathUtil.onGround(playerData.getLastLastLastLocation().getY()) && !UtilPlayer.isLandingSoonOnSlime(player.getLocation())) { + handleFlag(player, "Step B", "§b* §fMotionY=§b" + playerData.deltaY + "\n§b* §fpredict=§b" + limit, getBanVL("StepB"), 800000L); + } else { + if(++buffer > 1) { + handleFlag(player, "Step B", "§b* §fMotionY=§b" + playerData.deltaY + "\n§b* §fpredict=§b" + limit, getBanVL("StepB"), 800000L); + } + } + } else { + if(playerData.deltaY > 0) { + buffer = Math.max(buffer - 0.5, 0); + } + } + } + } + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/timer/TimerA.java b/src/main/java/me/liwk/karhu/check/impl/timer/TimerA.java new file mode 100644 index 0000000..3911242 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/timer/TimerA.java @@ -0,0 +1,61 @@ +package me.liwk.karhu.check.impl.timer; + + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.Karhu; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.event.PositionEvent; +import me.liwk.karhu.util.MathUtil; +import org.bukkit.entity.Player; + +import java.util.Deque; +import java.util.LinkedList; + +public class TimerA extends Check { + + private long lastTime, lastFlag; + private double buffer; + + private final Deque samples = new LinkedList(); + + + public TimerA(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent && !Karhu.getInstance().isServerLagging()) { + long now = System.currentTimeMillis(); + + if (playerData.getTotalTicks() < 300 && playerData.isLagging2(System.currentTimeMillis(), 100L) && playerData.getTotalTicks() - playerData.getLastDroppedPackets() < 5) return; + + samples.add(now - lastTime); + + if (samples.size() >= 40) { + final double avg = samples.stream().mapToDouble(d -> d).average().orElse(0.0); + final double speed = 50 / avg; + + if (speed > (1.009 + MathUtil.getPingToTimer(playerData.getTransPing() + 50))) { + if ((buffer += 0.75) > 3.5) { + handleFlag(player, "Timer A", "§b* §fTimer=§b" + speed + "\n§b* §fAVG=§b" + avg, getBanVL("TimerA"), 100000L); + } + } else { + buffer = Math.max(buffer - 1.25, 0.0); + } + + samples.clear(); + + } + lastTime = System.currentTimeMillis(); + + + } else if (event instanceof PositionEvent) { + samples.add(150L); + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/timer/TimerB.java b/src/main/java/me/liwk/karhu/check/impl/timer/TimerB.java new file mode 100644 index 0000000..690f9e2 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/timer/TimerB.java @@ -0,0 +1,60 @@ +package me.liwk.karhu.check.impl.timer; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import io.github.retrooper.packetevents.utils.server.ServerVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import org.bukkit.entity.Player; + +public class TimerB extends Check { + + public long lastPacket; + private double count, buffer; + + public TimerB(final PlayerData playerData) { + super(Category.PACKET, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + if (playerData.getClientVersion().isLowerThan(ClientVersion.v_1_9)) { + long delay = System.currentTimeMillis() - playerData.lastFlying; + if (delay > 66L) { + if (count++ > 10) { + count -= 2; + if (++buffer > 3) { + buffer -= 2; + handleFlag(player, "Timer B", "§b* §fNegative timer§b\n§b* §fdelay=§b" + delay, getBanVL("TimerB"), 100000L); + } + } + } else { + count = Math.max(buffer - 1, 0); + buffer = Math.max(buffer - 0.25, 0); + } + } else if (playerData.getClientVersion().isHigherThan(ClientVersion.v_1_8) && PacketEvents.getAPI().getServerUtils().getVersion().isHigherThan(ServerVersion.v_1_8)) { + if (playerData.lastServerPositionTick > 35 && System.currentTimeMillis() - playerData.getLastMovement() < 400L) { + long delay = System.currentTimeMillis() - playerData.lastFlying; + if (delay > 75L) { + if (count++ > 15) { + count -= 5; + if (++buffer > 5) { + buffer = 0; + handleFlag(player, "Timer B", "§b* §fNegative timer§b\n§b* §fdelay=§b" + delay, getBanVL("TimerB"), 100000L); + } + } + } else { + count = Math.max(buffer - 1, 0); + buffer = Math.max(buffer - 0.25, 0); + } + lastPacket = System.currentTimeMillis(); + } + } + } + } +} + diff --git a/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityA.java b/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityA.java new file mode 100644 index 0000000..bcf11e0 --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityA.java @@ -0,0 +1,49 @@ +package me.liwk.karhu.check.impl.velocity; + +import io.github.retrooper.packetevents.event.PacketEvent; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.entity.Player; + +public class VelocityA extends Check { + + private int buffer; + + public VelocityA(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + + if (playerData.getVelocityY() > 0.2 && MathUtil.onGround(playerData.getLastLastLocation().getY())) { + int velTicks = playerData.getTotalTicks() - playerData.getVelocityTicks(); + int maxPreVL = MathUtil.getPingInTicks(playerData.getTransPing()) + 5; + + final double p = (playerData.getDeltaY() / playerData.getVelocityY()) * 100.0D + 0.01; + + if (velTicks <= MathUtil.getPingInTicks(playerData.getTransPing()) + 2 && playerData.getLastServerPositionTick() > 55) { + if (playerData.getLiquidTicks() < 1 && !UtilPlayer.blockNearHead(player) && !UtilPlayer.isInWeb(player) && !UtilPlayer.isClimbableBlock(player) && player.getVehicle() == null) { + if (p < 99 || p > 105.0D) { + if (++buffer >= maxPreVL) { + handleFlag(player, "Velocity A", "§b* §fVertical velocity§b\n§b* §fapprox ptc=§b" + (int) (p + 1) + "§f%", getBanVL("VelocityA"), 100000L); + } + } else { + buffer = 0; + } + } else { + buffer = 0; + } + } + } + } + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityB.java b/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityB.java new file mode 100644 index 0000000..c60a23d --- /dev/null +++ b/src/main/java/me/liwk/karhu/check/impl/velocity/VelocityB.java @@ -0,0 +1,82 @@ +package me.liwk.karhu.check.impl.velocity; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.event.PacketEvent; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import me.liwk.karhu.check.api.Category; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; +import me.liwk.karhu.util.MathUtil; +import me.liwk.karhu.util.UtilPlayer; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +public class VelocityB extends Check { + + private double buffer; + + public VelocityB(final PlayerData playerData) { + super(Category.COMBAT, playerData); + } + + @Override + public void handle(PacketEvent event, Player player) { + if (event instanceof FlyingEvent) { + Location to = ((FlyingEvent) event).toLocation(); + int velTicks = playerData.getTotalTicks() - playerData.getVelocityTicks(); + + double lastVelX = playerData.getLastVelocityX(), lastVelZ = playerData.getLastVelocityZ(); + + if (playerData.getLastLocation() != null && playerData.getLastLastLocation() != null) { + if (!MathUtil.onGround(to.getY()) && isTouchingAir(player.getLocation())) { + if (MathUtil.onGround(playerData.getLastLocation().getY()) && playerData.isLastLastOnGroundPacket() && !UtilPlayer.blockNearHead(player) && !UtilPlayer.isNearWall(player) && !UtilPlayer.isNextToWall(player) && !UtilPlayer.isInWeb(player)) { + if (velTicks == 1 && playerData.lastServerPositionTick > 120) { + double velFXZ = MathUtil.hypot(lastVelX, lastVelZ); + double velFTXZ = MathUtil.hypot(to.getX() - playerData.getLastLocation().getX(), to.getZ() - playerData.getLastLocation().getZ()); + double velFFTXZ = MathUtil.hypot(playerData.getLastLastLocation().getX() - playerData.getLastLocation().getX(), playerData.getLastLastLocation().getZ() - playerData.getLastLocation().getZ()); + double shit = Math.max(velFTXZ, velFFTXZ) / velFXZ; // With this you can calculate the AVG % of the velocity modification + double maxShit = PacketEvents.getAPI().getPlayerUtils().getClientVersion(player).isHigherThan(ClientVersion.v_1_8) ? 0.65 : 0.99; + if (playerData.getDeltaXZ() <= playerData.getVelocityHorizontal() * maxShit && shit < maxShit && velFXZ > 0.2) { + if ((buffer += 1.1 - shit) > 3.5) { + handleFlag(player, "Velocity B", "§b* §fHorizontal velocity§b\n§b* §fest ptc=§b" + (shit * 100) + "§f%", getBanVL("VelocityB"), 100000L); + buffer = 0; + } + } else { + buffer -= Math.min(buffer, 1.1); + } + } + } + } else { + if (MathUtil.onGround(playerData.getLastLocation().getY()) && !UtilPlayer.blockNearHead(player) && !UtilPlayer.isNearWall(player) && !UtilPlayer.isNextToWall(player)) { + if (velTicks == 1 && playerData.lastServerPositionTick > 120) { + double velFXZ = MathUtil.hypot(lastVelX, lastVelZ); + double velFTXZ = MathUtil.hypot(to.getX() - playerData.getLastLocation().getX(), to.getZ() - playerData.getLastLocation().getZ()); + double velFFTXZ = MathUtil.hypot(playerData.getLastLastLocation().getX() - playerData.getLastLocation().getX(), playerData.getLastLastLocation().getZ() - playerData.getLastLocation().getZ()); + double shit = Math.max(velFTXZ, velFFTXZ) / velFXZ; // With this you can calculate the AVG % of the velocity modification + if (playerData.getDeltaXZ() <= playerData.getVelocityHorizontal() * 0.5 && shit < 0.4D && velFXZ > 0.2) { + if ((buffer += 1.1 - shit) > 3.5) { + handleFlag(player, "Velocity B", "§b* §fHorizontal velocity§b\n§b* §fest ptc=§b" + (int) (shit * 100) + "§f%", getBanVL("VelocityB"), 100000L); + buffer = 0; + } + } else { + buffer -= Math.min(buffer, 1.1); + } + } + } + } + } + } + } + public boolean isTouchingAir(Location location) { + if (!location.getWorld().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)) { + return true; + } + Block block = location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + 2, location.getBlockZ()); + return !block.getType().isSolid(); + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/commands/ACCommand.java b/src/main/java/me/liwk/karhu/commands/ACCommand.java new file mode 100644 index 0000000..6879eca --- /dev/null +++ b/src/main/java/me/liwk/karhu/commands/ACCommand.java @@ -0,0 +1,12 @@ +package me.liwk.karhu.commands; + +import me.liwk.karhu.Karhu; + +public class ACCommand { + + public Karhu main = Karhu.getInstance(); + + public ACCommand() { + main.getFramework().registerCommands(this); + } +} diff --git a/src/main/java/me/liwk/karhu/commands/sub/KarhuCommand.java b/src/main/java/me/liwk/karhu/commands/sub/KarhuCommand.java new file mode 100644 index 0000000..dfcb97f --- /dev/null +++ b/src/main/java/me/liwk/karhu/commands/sub/KarhuCommand.java @@ -0,0 +1,208 @@ +package me.liwk.karhu.commands.sub; + +import me.liwk.karhu.Karhu; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.commands.ACCommand; +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.menu.MainMenu; +import me.liwk.karhu.menu.SettingsMenu; +import me.liwk.karhu.util.check.EnabledUtil; +import me.liwk.karhu.util.command.Command; +import me.liwk.karhu.util.command.CommandArgs; +import me.liwk.karhu.util.file.HastebinUtil; +import me.liwk.karhu.util.file.LogUtil; +import me.liwk.karhu.util.update.UpdateCheck; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import java.util.Objects; + +public class KarhuCommand extends ACCommand { + + String name2 = Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.main-command.name")); + + @Command(name = "karhu", permission = "karhu.staff") + public void onCommand(CommandArgs command) { + Player player = command.getPlayer(); + String[] args = command.getArgs(); + if (command.getLabel().equalsIgnoreCase("karhu") || command.getLabel().equalsIgnoreCase(name2)) { + if (args.length >= 1) { + if (args[0].toLowerCase().equalsIgnoreCase("gui")) { + if (player.hasPermission("karhu.gui")) { + MainMenu.openMenu(player); + } else { + player.sendMessage(ChatColor.RED + "You don't have enough permissions."); + } + } else if (args[0].toLowerCase().equalsIgnoreCase("logs")) { + if (args.length == 1) { + player.sendMessage(ChatColor.RED + "Use: /logs "); + return; + } + Player target = Bukkit.getPlayer(args[1]); + if(target != null) { + player.sendMessage("§7§m--------------------------------------"); + player.sendMessage("§7Violations of §b" + target.getName() + "§f:"); + player.sendMessage(" "); + for (Check check : Karhu.getInstance().getPlayerDataManager().getPlayerData(target).getCheckManager().getChecks()) { + if(Karhu.getInstance().getPlayerDataManager().getPlayerData(target).getCheckVl(check) > 0) { + player.sendMessage(" §7* §b" + check.getRawName() + " §7- " + "x§b" + check.getVl()); + } + } + player.sendMessage("§7§m--------------------------------------"); + } else { + player.sendMessage(ChatColor.RED + "Couldn't find that player."); + } + + } else if (args[0].toLowerCase().equalsIgnoreCase("pastelogs")) { + if (args.length > 1) { + player.sendMessage("§7Searching logs..."); + + if (Bukkit.getOfflinePlayer(args[1]) == null) { + player.sendMessage("§cSorry, i couldn't find log file for " + args[1] + "."); + return; + } + + String target = Bukkit.getOfflinePlayer(args[1]).getName(); + StringBuilder end = new StringBuilder("Anticheat logs for player " + target + " pasted with " + Karhu.getInstance().getFileManager().getCommand() + " " + Karhu.getInstance().getBuild()); + LogUtil.TextFile logFile = new LogUtil.TextFile("" + target.toLowerCase(), "/logs"); + Bukkit.getScheduler().runTaskAsynchronously(Karhu.getInstance(), () -> { + try { + logFile.readTextFile(); + if (logFile.getLines().size() <= 0) { + player.sendMessage("§cSorry, i couldn't find log file for " + args[1] + "."); + return; + } + for (String s : logFile.getLines()) { + end.append("\n").append(s); + } + String url = HastebinUtil.uploadPaste(end.toString()); + if (url == null) { + player.sendMessage("§cCouldn't paste logs, maybe the file is too big?"); + return; + } + player.sendMessage("§aPasted logs to: §7" + url); + } catch (Exception ex) { + player.sendMessage("§cCouldn't paste logs, maybe the file is too big?"); + } + }); + } else player.sendMessage("§7Usage: " + "/karhu pastelogs player"); + + } else if (args[0].toLowerCase().equalsIgnoreCase("autoban")) { + if (player.hasPermission("karhu.autoban")) { + if (SettingsMenu.getAutoban()) { + Karhu.getInstance().getFileManager().getSettings().set("autoban", false); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.autoban.disabled")))); + } else { + Karhu.getInstance().getFileManager().getSettings().set("autoban", true); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.autoban.enabled")))); + } + Karhu.getInstance().getFileManager().save(); + Karhu.getInstance().getFileManager().load(Karhu.getInstance()); + } else { + player.sendMessage(ChatColor.RED + "You don't have enough permissions."); + } + + } else if (args[0].toLowerCase().equalsIgnoreCase("alerts")) { + Karhu.getInstance().getAlertsManager().toggleAlerts(player); + player.sendMessage(Karhu.getInstance().getAlertsManager().hasAlertsToggled(player) ? (ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.alerts.enabled")))) : (ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.alerts.disabled"))))); + + } else if (args[0].toLowerCase().equalsIgnoreCase("verbose")) { + Karhu.getInstance().getAlertsManager().toggleDevAlerts(player); + player.sendMessage(Karhu.getInstance().getAlertsManager().hasDevAlertsToggled(player) ? (ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.verbose.enabled")))) : (ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.verbose.disabled"))))); + + } else if (args[0].toLowerCase().equalsIgnoreCase("cancelban")) { + if (args.length > 1) { + if (Bukkit.getPlayer(args[1]) != null) { + if (EnabledUtil.playersToBeBanned.contains(Bukkit.getPlayer(args[1]))) { + try { + EnabledUtil.playersToBeBanned.remove(Bukkit.getPlayer(args[1])); + } catch (Exception ignored) { + } + player.sendMessage(ChatColor.GREEN + "Ban cancelled."); + } else { + player.sendMessage(ChatColor.RED + "Player is not queued for autoban."); + } + } else { + player.sendMessage(ChatColor.RED + "Player is not online"); + } + } else { + player.sendMessage(ChatColor.RED + "Usage: /cancelban "); + } + + } else if (args[0].toLowerCase().equalsIgnoreCase("exempt")) { + if (args.length > 1) { + Player target = Bukkit.getPlayer(args[1]); + if (target != null) { + PlayerData playerData = Karhu.getInstance().getPlayerDataManager().getPlayerData(target); + if (playerData.isExempt()) { + playerData.setExempt(false); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.exempt.disabled").replaceAll("%player%", args[1]))); + } else { + playerData.setExempt(true); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.exempt.enabled").replaceAll("%player%", args[1]))); + } + } + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.exempt.mention"))); + } + + } else if (args[0].toLowerCase().equalsIgnoreCase("reload")) { + if (player.hasPermission("karhu.reload")) { + Karhu.getInstance().getFileManager().load(Karhu.getInstance()); + player.sendMessage("§b§lKARHU §7// §aConfiguration reloaded!"); + } + + } else if (args[0].toLowerCase().equalsIgnoreCase("version")) { + player.sendMessage("§bVersion§7:"); + player.sendMessage("§7┃ §fCurrent: §b" + Karhu.getInstance().getBuild()); + player.sendMessage("§7┃ §fLatest: §b" + UpdateCheck.getNewVersion()); + + } else if (args[0].toLowerCase().equalsIgnoreCase("info")) { + if (args.length > 1) { + + Player target = Bukkit.getPlayer(args[1]); + + if (target == null) { + player.sendMessage("§cSorry, i couldn't find that player"); + return; + } + + PlayerData targetData = Karhu.getInstance().getPlayerDataManager().getPlayerData(target); + + player.sendMessage("§7"); + player.sendMessage("§7Name: §b" + target.getName()); + player.sendMessage("§7Version: §b" + targetData.getBrand() + " §7| §b" + targetData.getClientVersion().toString().replaceAll("_", ".").replaceAll("v.", "")); + player.sendMessage("§7Ping: §b" + targetData.getTransPing() + " §7| §b" + targetData.getPing()); + player.sendMessage("§7Sensitivity: §b" + Math.round(targetData.getSensitivity() * 200) + "%"); + player.sendMessage("§7Session duration: §b" + (System.currentTimeMillis() - targetData.getLastJoinTime()) / 1000 / 60 / 60 + "h" + " " + (System.currentTimeMillis() - targetData.getLastJoinTime()) / 1000 / 60 + "m"); + player.sendMessage("§7"); + } + + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-1")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-2")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-3")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-4")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-5")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-6")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-7")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-8")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-9")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-10")))); + } + } else { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-1")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-2")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-3")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-4")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Objects.requireNonNull(Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-5")))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-6"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-7"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-8"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-9"))); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Karhu.getInstance().getFileManager().getSettings().getString("commands.help.line-10"))); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/data/PlayerData.java b/src/main/java/me/liwk/karhu/data/PlayerData.java new file mode 100644 index 0000000..0286e12 --- /dev/null +++ b/src/main/java/me/liwk/karhu/data/PlayerData.java @@ -0,0 +1,311 @@ +package me.liwk.karhu.data; + +import io.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.utils.player.ClientVersion; +import lombok.Getter; +import lombok.Setter; +import me.liwk.karhu.check.api.Check; +import me.liwk.karhu.check.api.manager.CheckManager; +import me.liwk.karhu.util.Observable; +import me.liwk.karhu.util.*; +import me.liwk.karhu.util.evictinglist.EvictingList; +import me.liwk.karhu.util.pair.Pair; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.Vector; + +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Setter +@Getter +public class PlayerData { + + public static Map, Constructor> CONSTRUCTORS; + private final CheckManager checkManager = new CheckManager(this); + public final LinkedList recentCounts = new LinkedList<>(); + public boolean attackedSinceVelocity; + /*public static Class[] CHECKS1_8; + public static Class[] CHECKS1_9; + public static Class[] CHECKS1_13;*/ + public Map transactionTime = new HashMap<>(); + public short timerTransactionSent; + public int timerTransactionReceived; + public boolean receivedTransaction; + public int transactionStreak; + public int entityId; + private Map> recentPlayerPackets; + private Map> checkViolationTimes; + private Map, Check> checkMap; + private Map keepAliveTimes; + private Map checkVlMap; + + private CustomLocation lastCustomLocation; + private CustomLocation lastMovePacket; + private long lastMovement, lastMovePkt; + private int lastFlyTick; + private Set teleportLocations; + + public Location location, lastLocation, lastLastLocation, lastLastLastLocation; + + private boolean verifyingSensitivity; + + private double sensitivity; + + public double reachX, reachY, reachZ; + + public float deltaXZ, deltaXZ2, lastDeltaXZ, lastDeltaY, deltaY; + public float deltaX, deltaZ, lastDeltaX, lastDeltaZ; + + private int lastOpeningInteract; + + private LocationData locationData, previousLocation; + private final EvictingList> pastLocs = new EvictingList<>(20); + private final EvictingList> pastLocations = new EvictingList<>(30); + private final EvictingList> pastLocsHitBox = new EvictingList<>(20); + private final List playerLocations = new ArrayList<>(); + + private Player lastTarget, lastLastTarget; + public final Observable target = new Observable<>(null); + private int lastAttackedId; + private long lastTeleport, lastServerTeleport; + private CustomLocation teleportLocation; + public int totalTicks = 0, positionTicks; + + private final Map transactionSentMap = new HashMap<>(); + private final Map relmoveSentMap = new HashMap<>(); + + private double relmoveX, relmoveY, relmoveZ; + + private HashMap velocityIds = new HashMap<>(); + + private boolean velocityBeingConfirmed, relMoveBeingConfirmed, transactionBeingConfirmed; + private long lastDelayed; + private boolean allowTeleport; + private boolean inventoryOpen; + private int invStamp; + public long lastJoinTime, lastFlying, lastDelayedPacket, lastVehicle; + public int lastFlyingTicks, lastVehicleTicks; + public boolean droppedPackets; + private int lastPacketDrop; + public boolean underBlock; + private boolean sprinting; + private boolean sneaking; + public boolean inLiquid; + private boolean instantBreakDigging; + private boolean fakeDigging; + private boolean onGround; + private boolean onGroundPacket, lastOnGroundPacket, lastLastOnGroundPacket; + private boolean onStairs; + private boolean onCarpet; + private boolean OnSlab; + public boolean onSlime; + private boolean placing, digging; + private int lastDig; + private boolean banned; + private boolean exempt; + private boolean inWeb; + private boolean onIce; + private boolean belowBlock; + private boolean onLadder; + private boolean wasUnderBlock, wasOnGround, wasInLiquid, wasInWeb, wasBelowBlock, wasOnLadder; + private double lastGroundY; + //private int velocityId, relMoveId, transactionId; + private short velocityId; + private double velocityX, lastVelocityX; + private double velocityY, lastVelocityY; + private double velocityZ, lastVelocityZ; + private double velocityHorizontal; + private double moveSpeed; + private double newMoveSpeed; + private double lastVelocityFlyY; + private long lastDelayedMovePacket; + private long lastAttackPacket; + private long lastDamage; + private long lastCancelledDamage; + private long lastVelocity, lastGlide, lastRiptide; + public long lastWindowClick; + //private Deque samplesWindowClick = Lists.newLinkedList(); + private boolean gliding = false, riptiding = false; + + private long transPing, lastTransPing, averageTransPing; + private long ping, lastPing; + private long lastDead; + private int lastBlockPlaceCancelled; + private long lastFullblockMoved; + private long lastHillJump; + private long lastAimTime; + private long interact; + private int velocityH, velocityV; + private int lastCps; + private int movementsSinceIce; + private int movementsSinceUnderBlock; + private int aboveBlockTicks; + public int airTicks, clientAirTicks; + public int climbableTicks; + public int iceTicks, halfTicks, skullTicks, potTicks, wallTicks; + public int groundTicks; + public int slimeTicks; + public double slimeHeight, fallDistance, fallY; + public long lastOnSlime; + public int blockTicks; + public int velocityTicks; + public int liquidTicks; + public int lastAttackTick; + public Vec3 eyeLocation; + private Vec3 look, lookMouseDelayFix; + public int ticksSinceHit; + public double lastGravity; + public boolean isNearWater; + public long lastLag, lastLag2; + public int lastDroppedPackets; + public boolean lagTick; + public long lastKeepAlive, lastServerKeepAlive; + public int lastServerPositionTick, lastTeleportReset; + public boolean teleportReset; + + public double TimerABalance, TimerBCount, TimerCBalance; + + public boolean lastOnGround, lastLastOnGround; + + public long TimerALastPacket, TimerBLastPacket; + + public double lastSpeed, lastDist; + + public long lastFlag, lastFlag2; + public int clickerCClicks, clickerCOutliers, clickerCFlyingCount; + public boolean clickerCRelease; + + public boolean cinematic; + + public long lastCinematic; + + public Long lastUseEntity; + + + public int lastPosition; + public double attackerX, attackerY, attackerZ; + public float attackerYaw, attackerPitch; + + public int lastPosition2; + public double attackerX2, attackerY2, attackerZ2; + public float attackerYaw2, attackerPitch2; + + public UUID playerUUID; + + private long lastFast; + + private ClientVersion clientVersion; + + private String brand = "vanilla"; + + private Player dataPlayer; + + private boolean objectLoaded = false; + + public PlayerData(Player player, Plugin karhu) { + this.recentPlayerPackets = new HashMap<>(); + this.checkViolationTimes = new HashMap<>(); + this.checkMap = new HashMap<>(); + this.keepAliveTimes = new HashMap<>(); + this.checkVlMap = new HashMap<>(); + this.teleportLocations = Collections.newSetFromMap(new ConcurrentHashMap<>()); + this.banned = false; + this.clientVersion = PacketEvents.getAPI().getPlayerUtils().getClientVersion(player); + this.dataPlayer = player; + this.location = player.getLocation(); + this.lastLocation = this.location; + this.lastLastLocation = this.lastLocation; + this.lastJoinTime = System.currentTimeMillis(); + this.entityId = player.getEntityId(); + this.objectLoaded = true; + } + + private String dogshit11(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } + + @SuppressWarnings("unchecked") + public T getCheck(Class clazz) { + return (T) this.checkMap.get(clazz); + } + + public CustomLocation getLastPlayerPacket(UUID playerUUID, int index) { + List customLocations = this.recentPlayerPackets.get(playerUUID); + if (customLocations != null && customLocations.size() > index) { + return customLocations.get(customLocations.size() - index); + } + return null; + } + private String dogshit40(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } + + public void addPlayerPacket(UUID playerUUID, CustomLocation customLocation) { + List customLocations = this.recentPlayerPackets.get(playerUUID); + if (customLocations == null) { + customLocations = new ArrayList<>(); + } + if (customLocations.size() == 20) { + customLocations.remove(0); + } + customLocations.add(customLocation); + this.recentPlayerPackets.put(playerUUID, customLocations); + } + + public boolean isTeleporting() { + return teleportLocation != null; + } + + public double getCheckVl(Check check) { + if (!this.checkVlMap.containsKey(check)) { + this.checkVlMap.put(check, 0.0); + } + return this.checkVlMap.get(check); + } + + public void setCheckVl(double vl, Check check) { + if (vl < 0.0) { + vl = 0.0; + } + this.checkVlMap.put(check, vl); + } + + + private boolean hasFast(long timestamp) { + return lastFlying != 0L && lastFast != 0L && timestamp - lastFast < 100L; + } + + + public boolean isLagging(long currentTime, long length) { + return currentTime - lastLag < length; + } + + + public boolean isLagging2(long currentTime, long length) { + return currentTime - lastLag2 < length; + } + + public boolean hasFast() { + return hasFast(lastFlying); + } + + public int getViolations(Check check, Long time) { + Set timestamps = this.checkViolationTimes.get(check); + if (timestamps != null) { + return (int) timestamps.stream().filter(timestamp -> System.currentTimeMillis() - timestamp <= time).count(); + } + return 0; + } + + public void addViolation(Check check) { + Set timestamps = this.checkViolationTimes.get(check); + if (timestamps == null) { + timestamps = new HashSet<>(); + } + timestamps.add(System.currentTimeMillis()); + this.checkViolationTimes.put(check, timestamps); + } +} \ No newline at end of file diff --git a/src/main/java/me/liwk/karhu/entity/EntityLocationHandler.java b/src/main/java/me/liwk/karhu/entity/EntityLocationHandler.java new file mode 100644 index 0000000..3d6ac0e --- /dev/null +++ b/src/main/java/me/liwk/karhu/entity/EntityLocationHandler.java @@ -0,0 +1,42 @@ +package me.liwk.karhu.entity; + +import me.liwk.karhu.data.PlayerData; +import me.liwk.karhu.event.FlyingEvent; + +public class EntityLocationHandler { + public static void updateFlyingLocations(PlayerData data, FlyingEvent flying) { + if(flying.hasMoved()) { + data.lastPosition = 0; + data.attackerX = flying.getX(); + data.attackerY = flying.getY(); + data.attackerZ = flying.getZ(); + } + + if(!flying.hasMoved()) { + data.lastPosition++; + } + + if(flying.hasLooked()) { + data.attackerYaw = flying.getYaw(); + data.attackerPitch = flying.getPitch(); + } + } + + public static void updateFlyingLocations2(PlayerData data, FlyingEvent flying) { + if(flying.hasMoved()) { + data.lastPosition2 = 0; + data.attackerX2 = flying.getX(); + data.attackerY2 = flying.getY(); + data.attackerZ2 = flying.getZ(); + } + + if(!flying.hasMoved()) { + data.lastPosition2++; + } + + if(flying.hasLooked()) { + data.attackerYaw2 = flying.getYaw(); + data.attackerPitch2 = flying.getPitch(); + } + } +} diff --git a/src/main/java/me/liwk/karhu/event/AbilityEvent.java b/src/main/java/me/liwk/karhu/event/AbilityEvent.java new file mode 100644 index 0000000..43d6266 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/AbilityEvent.java @@ -0,0 +1,15 @@ +package me.liwk.karhu.event; + +public class AbilityEvent extends Event { + + private final long timeStamp; + + public AbilityEvent() { + timeStamp = (System.nanoTime() / 1000000); + } + + public long getTimeStamp() { + return timeStamp; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/AttackEvent.java b/src/main/java/me/liwk/karhu/event/AttackEvent.java new file mode 100644 index 0000000..4086c54 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/AttackEvent.java @@ -0,0 +1,25 @@ +package me.liwk.karhu.event; + +import lombok.Getter; +import org.bukkit.entity.Entity; + +@Getter +public class AttackEvent extends Event { + + private final int entityId; + private final Entity entity; + + public AttackEvent(int entityId, Entity entity) { + this.entityId = entityId; + this.entity = entity; + } + + public int getEntityId() { + return entityId; + } + + public Entity getEntity() { + return entity; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/BlockPlaceEvent.java b/src/main/java/me/liwk/karhu/event/BlockPlaceEvent.java new file mode 100644 index 0000000..67dee20 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/BlockPlaceEvent.java @@ -0,0 +1,18 @@ +package me.liwk.karhu.event; + +import lombok.Getter; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +@Getter +public class BlockPlaceEvent extends Event { + + private final Vector blockPos; + private final ItemStack itemStack; + + public BlockPlaceEvent(Vector blockPos, ItemStack itemStack) { + this.blockPos = blockPos; + this.itemStack = itemStack; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/ClientCommandEvent.java b/src/main/java/me/liwk/karhu/event/ClientCommandEvent.java new file mode 100644 index 0000000..31010b9 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/ClientCommandEvent.java @@ -0,0 +1,15 @@ +package me.liwk.karhu.event; + +import io.github.retrooper.packetevents.packetwrappers.in.clientcommand.WrappedPacketInClientCommand; +import lombok.Getter; + +@Getter +public class ClientCommandEvent extends Event { + + private final WrappedPacketInClientCommand.ClientCommand clientCommand; + + public ClientCommandEvent(WrappedPacketInClientCommand.ClientCommand clientCommand) { + this.clientCommand = clientCommand; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/DigEvent.java b/src/main/java/me/liwk/karhu/event/DigEvent.java new file mode 100644 index 0000000..76862fa --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/DigEvent.java @@ -0,0 +1,22 @@ +package me.liwk.karhu.event; + +import io.github.retrooper.packetevents.packetwrappers.in.blockdig.WrappedPacketInBlockDig; +import lombok.Getter; +import org.bukkit.util.Vector; + + +@Getter +public class DigEvent extends Event { + + private final Vector blockPos; + //private final Direction direction; + private final WrappedPacketInBlockDig.PlayerDigType digType; + + public DigEvent(Vector blockPos, WrappedPacketInBlockDig.PlayerDigType digType) { + this.blockPos = blockPos; + //this.direction = direction; + this.digType = digType; + } + +} + diff --git a/src/main/java/me/liwk/karhu/event/Event.java b/src/main/java/me/liwk/karhu/event/Event.java new file mode 100644 index 0000000..e59ce95 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/Event.java @@ -0,0 +1,9 @@ +package me.liwk.karhu.event; + +import io.github.retrooper.packetevents.event.PacketEvent; + +public class Event extends PacketEvent { + public boolean isAsyncByDefault() { + return false; + } +} diff --git a/src/main/java/me/liwk/karhu/event/FlyingEvent.java b/src/main/java/me/liwk/karhu/event/FlyingEvent.java new file mode 100644 index 0000000..371da2b --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/FlyingEvent.java @@ -0,0 +1,69 @@ +package me.liwk.karhu.event; + +import org.bukkit.Location; +import org.bukkit.World; + +public class FlyingEvent extends Event { + + private final double x; + private final double y; + private final double z; + private final float yaw; + private final float pitch; + private final boolean hasMoved; + private final boolean hasLooked; + private final boolean isOnGround; + private final World world; + + public FlyingEvent(double x, double y, double z, float yaw, float pitch, boolean hasMoved, boolean hasLooked, boolean isOnGround, World world) { + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + this.hasMoved = hasMoved; + this.hasLooked = hasLooked; + this.isOnGround = isOnGround; + this.world = world; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public boolean hasMoved() { + return hasMoved; + } + + public boolean hasLooked() { + return hasLooked; + } + + public boolean isOnGround() { + return isOnGround; + } + + public Location toLocation() { + return new Location(world, x, y, z, yaw, pitch); + } + + private String dogshit1(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/event/HeldItemSlotEvent.java b/src/main/java/me/liwk/karhu/event/HeldItemSlotEvent.java new file mode 100644 index 0000000..f1f4572 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/HeldItemSlotEvent.java @@ -0,0 +1,13 @@ +package me.liwk.karhu.event; + +import lombok.Getter; + +@Getter +public class HeldItemSlotEvent extends Event { + + private final int slot; + + public HeldItemSlotEvent(int slot) { + this.slot = slot; + } +} diff --git a/src/main/java/me/liwk/karhu/event/InteractEvent.java b/src/main/java/me/liwk/karhu/event/InteractEvent.java new file mode 100644 index 0000000..13066ac --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/InteractEvent.java @@ -0,0 +1,23 @@ +package me.liwk.karhu.event; + +import org.bukkit.entity.Entity; + +public class InteractEvent extends Event { + + private final int entityId; + private final Entity entity; + + public InteractEvent(int entityId, Entity entity) { + this.entityId = entityId; + this.entity = entity; + } + + public int getEntityId() { + return entityId; + } + + public Entity getEntity() { + return entity; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/PositionEvent.java b/src/main/java/me/liwk/karhu/event/PositionEvent.java new file mode 100644 index 0000000..25b31ca --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/PositionEvent.java @@ -0,0 +1,24 @@ +package me.liwk.karhu.event; + +import lombok.Getter; + +@Getter +public class PositionEvent extends Event { + + private final double x; + private final double y; + private final double z; + private final float yaw; + private final float pitch; + + public PositionEvent(double x, double y, double z, float yaw, float pitch) { + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + } + private String dogshit(boolean value) { + return value ? "%%__TIMESTAMP__%%" : "%%__USER__%%"; + } +} diff --git a/src/main/java/me/liwk/karhu/event/SwingEvent.java b/src/main/java/me/liwk/karhu/event/SwingEvent.java new file mode 100644 index 0000000..6b1ae2f --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/SwingEvent.java @@ -0,0 +1,15 @@ +package me.liwk.karhu.event; + +public class SwingEvent extends Event { + + private final long timeStamp; + + public SwingEvent() { + timeStamp = (System.nanoTime() / 1000000); + } + + public long getTimeStamp() { + return timeStamp; + } + +} diff --git a/src/main/java/me/liwk/karhu/event/WindowEvent.java b/src/main/java/me/liwk/karhu/event/WindowEvent.java new file mode 100644 index 0000000..b479e91 --- /dev/null +++ b/src/main/java/me/liwk/karhu/event/WindowEvent.java @@ -0,0 +1,15 @@ +package me.liwk.karhu.event; + +public class WindowEvent extends Event { + + private final long timeStamp; + + public WindowEvent() { + timeStamp = (System.nanoTime() / 1000000); + } + + public long getTimeStamp() { + return timeStamp; + } + +} diff --git a/src/main/java/me/liwk/karhu/gui/Button.java b/src/main/java/me/liwk/karhu/gui/Button.java new file mode 100644 index 0000000..7e87df4 --- /dev/null +++ b/src/main/java/me/liwk/karhu/gui/Button.java @@ -0,0 +1,31 @@ +package me.liwk.karhu.gui; + +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public abstract class Button { + + public int pos; + @Deprecated public Inventory inv; + public ItemStack item; + public int page; + + @Deprecated + public Button(Inventory inv, int pos, ItemStack item){ + this.inv = inv; + this.pos = pos; + this.item = item; + this.page = 1; + } + + public Button(int page, int pos, ItemStack item) { + this.page = page; + this.pos = pos; + this.item = item; + } + + public abstract void onClick(Player clicker, ClickType clickType); + +} diff --git a/src/main/java/me/liwk/karhu/gui/Callback.java b/src/main/java/me/liwk/karhu/gui/Callback.java new file mode 100644 index 0000000..a327c05 --- /dev/null +++ b/src/main/java/me/liwk/karhu/gui/Callback.java @@ -0,0 +1,7 @@ +package me.liwk.karhu.gui; + +public interface Callback { + + void call(T type); + +} diff --git a/src/main/java/me/liwk/karhu/gui/Gui.java b/src/main/java/me/liwk/karhu/gui/Gui.java new file mode 100644 index 0000000..b9cb59f --- /dev/null +++ b/src/main/java/me/liwk/karhu/gui/Gui.java @@ -0,0 +1,287 @@ +package me.liwk.karhu.gui; + +import me.liwk.karhu.Karhu; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; + +public class Gui implements Listener { + + private HashMap pages; + private HashMap> items; + private static final List guis = new ArrayList<>(); + private HashMap playerPages = new HashMap<>(); + private final Set